AngularJS Tutorial im heißen Handtuch – Teil 2

Im zweiten Teil des kürzlich veröffentlichten AngularJS-1-Tutorials werden die Grundlagen des ersten Teils wie Modules, Directives, Controllers, Templates etc. um weiterführende Konzepte von Angular erweitert. Zu diesen Konzepten zählen Services, die Kommunikation mit dem Server, Promises, Two-Way Data Binding und Event-Handling. Die Struktur dieses Artikels orientiert sich an der von Teil 1, neben grundsätzlichen Erklärungen einzelner Konzepte wird die Beispielapplikation um eine neue View erweitert. Zu dieser View gehören ein weiteres Modul, ein Controller und eine neue Route. Am Ende wird es möglich sein, Einträge innerhalb der Datenbank über eine grafische Oberfläche zu manipulieren.

Momentaner Stand

Voraussetzung für die Fortsetzung ist die Absolvierung des Tutorials aus dem ersten Teil. Alternativ lässt sich folgender Branch clonen und als Ausgangspunkt verwenden. Damit sieht die Basisstruktur für dieses Tutorial wie folgt aus:

src/
 |__ client/
        |__ app/
               |__ blocks/
               |__ core/
               |__ layout/
               |__ widgets/
               |__ welcome/
               |__ app.module.js
 |__ server/*

Erweiterung um neue View

Als nächstes wird eine Maske erstellt, in der Superhelden in einer Tabelle dargestellt werden können. Zudem gibt es Felder zum Hinzufügen neuer Helden und zum Löschen bestehender Helden aus der Tabelle. Daher wird der aktuelle Stand der Webapp um den neuen Ordner /src/client/app/watchmen/ ergänzgt. Innerhalb des neuen Ordners werden vier neue Dateien erstellt. Ein Template, ein Controller, ein Modul und eine Route. Route und Module unterscheiden sich kaum von den bereits definierten Dateien welcome.module.js und welcome.route.js. Sie haben folgenden Inhalt:

// /src/client/app/watchmen/watchmen.module.js
angular.module('app.watchmen', [
    'app.core',
    'app.widgets'
]);

Das Modul muss als Dependency in die Datei /src/client/app/app.module.js eingetragen werden.

// app.module.js
angular.module('app', [
    'app.core',
    'app.widgets',
    'app.layout',
    'app.watchmen
]);

Als nächstes die Route:

// /src/client/app/watchmen/watchmen.route.js  

  angular
    .module('app.watchmen')
    .run(appRun);

  appRun.$inject = ['routerHelper'];
  /* @ngInject */
  function appRun(routerHelper) {
    routerHelper.configureStates(getStates());
  }

  function getStates() {
    return [
      {
        state: 'watchmen',
        config: {
          url: '/watchmen',
          templateUrl: 'app/watchmen/watchmen.html',
          controller: 'WatchmenController',
          controllerAs: 'vm',
          title: 'Watchmen',
          settings: {
            nav: 2,
            content: '<i class="fa fa-lock"></i> Watchmen'
          }
        }
      }
    ];
  }

Damit das Routing auf das Template watchmen.html möglich ist, muss dieses selbstverständlich auch existieren. Die HTML-Datei hat folgende Form:

<!-- /src/client/app/watchmen/watchmen.html -->
<section id="dashboard-view" class="mainbar">
  <section class="matter">
    <div class="container">
      <div class="row">
        <div class="col-md-6">
          <div class="widget wviolet">
            <div ht-widget-header title="Watchmen" allow-collapse="true"></div>
            <div class="widget-content text-center text-info">
              <table class="table table-condensed table-striped">
                <thead>
                  <tr>
                    <th>First Name</th>
                    <th>Last Name</th>
                    <th>Alias</th>
                    <th>Id</th>
                  </tr>
                </thead>
                <tbody>
                  <tr ng-repeat="w in vm.watchmen">
                    <td>{{w.firstName}}</td>
                    <td>{{w.lastName}}</td>
                    <td>{{w.alias}}</td>
                    <td>{{w._id}}
                  </tr>
                </tbody>
              </table>
              </div>
            </div>
            <div class="widget-foot">
              <div class="clearfix"></div>
            </div>
          </div>
        </div>
      </div>
      <div class="row">
        <div class="col-md-3">
          <span><h3>Add new Hero</h3></span>
          <form ng-submit="vm.sendData()">
              <div class="form-group row">
                <label for="firstNameInput" class="col-xs-2 col-form-label">First Name</label>
                <input class="form-control" type="text" placeholder="firstname" id="firstNameInput" ng-model="vm.firstName" required>
              </div>
              <div class="form-group row">  
                <label for="lastNameInput" class="col-xs-2 col-form-label">Last Name</label>
                <input class="form-control" type="text" placeholder="lastname" id="lastNameInput" ng-model="vm.lastName" required>
              </div>  
              <div class="form-group row">  
                <label for="aliasInput" class="col-xs-2 col-form-label">Alias</label>
                <input class="form-control" type="text" placeholder="alias" id="aliasInput" ng-model="vm.alias" required>
              </div>
              <button type="submit" class="btn btn-primary">Save new user</button>
          </form>
        </div>
      <div class="col-md-3">
        <span><h3>User deletion by Id</h3></span>
        <form ng-submit="vm.deleteData()">
          <div class="form-group row">
            <label for="idDeleteInput" class="col-xs-2 col-form-label">Id to delete</label>
            <input class="form-control" type="text" placeholder="id" id="idDeleteInput" ng-model="vm.idToDel" required>
          </div>
          <button type="submit" class="btn btn-primary">Delete user by Id</button>
        </form>
      </div>
    </div>
    </div>
  </section>
</section>

Auf den ersten Blick wirkt das Template recht komplex und unübersichtlich, was jedoch vor allem an den Tag-Attributen für Bootstrap und für die HotTowel-Widgets liegt. Auf das wesentliche runtergebrochen besteht das HTML-Template aus drei Komponenten, einer Tabelle aus Superhelden, einer Input-Form zum Eintragen neuer Helden in die Tabelle und einer Input-Form zum Löschen einzelner Einträge.

<!-- table excerpt of watchmen.html -->
<table class="...">
  <thead>
    <tr>
      <th>First Name</th>
      <th>Last Name</th>
      <th>Alias</th>
      <th>Id</th>
    </tr>
  </thead>
  <tbody>
    <tr ng-repeat="w in vm.watchmen">
      <td>{{w.firstName}}</td>
      <td>{{w.lastName}}</td>
      <td>{{w.alias}}</td>
      <td>{{w._id}}
    </tr>
  </tbody>
</table>

Die Tabelle wird durch die ngRepeat-Directive aufgefüllt, indem durch alle Einträge des Arrays vm.watchmen (wird etwas weiter unten im Controller definiert) iteriert wird. Voraussetzung dabei ist selbstverständlich, dass das Array entsprechende Key-Value-Einträge für firstName, lastName, alias und _id besitzt.

Hinweis am Rande: Watchmen ist ein auf einem Comic von Autor Alan Moore und Zeichner Dave Gibbons basierender Superhelden-Film.

<!-- form input to add new heroes -->
<span><h3>Add new Hero</h3></span>
<form ng-submit="vm.sendData()">
  <label for="firstNameInput">First Name</label>
  <input type="text" placeholder="firstname" id="firstNameInput" ng-model="vm.firstName" required>
  <label for="lastNameInput">Last Name</label>
  <input type="text" placeholder="lastname" id="lastNameInput" ng-model="vm.lastName" required>
  <label for="aliasInput">Alias</label>
  <input type="text" placeholder="alias" id="aliasInput" ng-model="vm.alias" required>
  <button type="submit" class="...">Save new user</button>
</form>

Die Input-Form zum Eintragen neuer Superhelden enthält drei Text-Eingabefelder und einen Submit-Button. Für Angular relevant sind vor allem die Directiven ngModel und ngSubmit. Die Directive ngSubmit befindet sich innerhalb des Form-Tags und enthält einen String-Wert „vm.sendData()“. vm.sendData() ist eine Funktion, die im Watchmen-Controller definiert sein muss und ausgelöst wird, sobald die Form übermittelt wird. Der Wert von ngSubmit muss dabei einer gültigen Expression entsprechen.
NgModel bindet die Eingabe des Input-Felds über den Scope direkt an ein Attribut im Controller. Um dies zu verdeutlichen lässt sich das Template unterhalb des Form-Blocks um die Zeile <div>{{vm.firstName}}</div> erweitern. Eingaben in das First-Name-Textfeld werden nun synchron im Template angezeigt.

<!-- input form for user deletion -->
<span><h3>User deletion by Id</h3></span>
<form ng-submit="vm.deleteData()">
  <label for="idDeleteInput">Id to delete</label>
  <input type="text" placeholder="id" id="idDeleteInput" ng-model="vm.idToDel" required>
  <button type="submit">Delete user by Id</button>
</form>

Die zweite Form arbeitet nach demselben Prinzip. Durch Klicken des Submit-Buttons wird hier die Funktion vm.deleteData() ausgeführt. Diese ist noch nicht deklariert, da der entsprechende Watchmen-Controller noch nicht definiert wurde. Doch vor der Erstellung des Controllers ein kurzer Blick auf Services und Provider.

Services von Constant bis Provider

Jede Web-Applikation besteht aus einer Vielzahl von Objekten mit verschiedenen Aufgaben. Die meisten dieser Objekte werden bei Angular automatisch durch den Injector Service instantiiert und mit anderen Komponenten verbunden. Dabei kann allgemein zwischen zwei Typen von Objekten unterschieden werden, die der Injector erzeugt. Es gibt Services und spezialisierte Objekte. Services sind Objekte, deren API vom Entwickler bestimmt wird, der den Service schreibt, wohingegen spezialisierte Objekte Controller, Directives, Filter oder Animationen sind. Services enthalten beispielsweise Logik und Funktionen, die über die komplette Applikation hinweg geteilt werden sollen. Statt komplexe Berechnungen oder sich wiederholende Aufgaben durch dieselbe Funktion in jedem Controller separat zu definieren, reicht es aus, diese in einem Service einmalig zu definieren und anschließend in beliebige Controller zu injizieren.
Um dem Injector mitzuteilen, wie Service-Objekte erschaffen werden, müssen sogenannte Recipes am Injector registriert werden. Es gibt fünf verschiedene Recipe-Typen: Constant, Value, Service, Factory und Provider. Hinweis: Recipe-Typ Service ist nicht gleichbedeutend mit Service-Objekten, sondern nur ein spezieller Subtyp davon.

Eine Constant kann überall injiziert werden und ihr Wert kann niemals geändert werden.

var app = angular.module('app');
  
app.constant('MAX_INT', 2147483647);
  
app.controller('MyCtrl', function (MAX_INT) {
  // useage of constant here
});

Ein Value ist nichts weiter als ein Wert, der injiziert wird. Ein Value kann ein String, eine Number oder eine Funktion sein.

var app = angular.module('app');
  
app.value('movieTitle', 'Watchmen');
  
app.controller('MyCtrl', function (movieTitle) {
  // useage of value here
});

Bei der Konfiguration eines Services registriert man eine Constructor-Funktion, die eine Service-Instanz erzeugt. Diese Funktion wird von Angular aufgerufen, wenn der zugehörige Service angefordert wird. Die erzeugte Instanz wird dann injiziert. Services enthalten Objekte oder Funktionen, die über die gesamte Applikation geteilt werden sollen. Services sind Singletons und Abhängigkeiten in Form von Services oder Values können injiziert werden.

var app = angular.module('app');
   
app.service('movie', function () {
  this.title = 'Watchmen';
});
  
app.controller('MyCtrl', function(movie) {
  // useage of service here
});

Eine Factory ist eine Funktion, die starke Ähnlichkeiten mit einem Service aufweist. Nutzt beispielsweise ein Controller die Factory-Funktion, wird das Ergebnis der Factory in den Controller injiziert. Eine Factory ist ebenfalls ein Singleton und die gleichen Dependencies wie bei einem Service können in eine Factory injiziert werden. Im Gegensatz zur Factory wird bei einem Service die übergebene Funktion mit new konstruiert.

var app = angular.module('app');
   
app.factory('movie', function () {
  return {
    title : 'Watchmen';
  }
});
  
app.controller('MyCtrl', function(movie) {
  // usage of factory here
});

Ein Provider kann als eine konfigurierbare Factory verstanden werden. Provider akzeptieren Objekte oder einen Constructor. Provider erlauben die Konfiguration eines Service-Objekts beim Start der Applikation.

var app = angular.module('app');
  
app.provider('movie', function () {
  var version;
  return {
    setVersion: function (value) {
      version = value;
    },
    $get: function () {
      return {
          title: 'Watchmen' + ' ' + version
      }
    }
  }
});

app.config(function (movieProvider) {
  movieProvider.setVersion('Reloaded');
});
  
app.controller('MyCtrl', function (movie) {
  titleWithVersion = movie.title; 
});

Durch app.config wird der Provider zum Start der Applikation injiziert und dessen Methode setVersion wird aufgerufen. Diese setzt die Variable version auf den Wert ‚Reloaded‘. Anschließend wird der Provider-Service in einen Controller injiziert und kann dort über movie.title aufgerufen werden. Die Variable in dem Controller titleWithVersion hat somit den Wert „Watchmen Reloaded“.

Data-Services und Promises

Die Factory dataservice in der Datei /src/client/app/core/dataservice.js enthält eine Vielzahl von Funktionen, die XHR-Calls auf den definierten REST-Server machen. Die Funktionen geben Promises zurück, die wiederum an einen Controller übertragen und dort aufgelöst werden können (dazu gleich mehr). Zu den Funktionen zählen getWatchmen(), eine Funktion, die alle in einer Datenbank eingetragenen Watchmen zurückgibt, die Funktion postWatchmen(), um neue Helden zur Datenbank hinzuzufügen, und deleteWatchmenById(), damit einzelne Helden aus der Datenbank gelöscht werden können. Die Datei besteht aus folgendem Inhalt:

// dataservice.js
angular
    .module('app.core')
    .factory('dataservice', dataservice);

  dataservice.$inject = ['$http', 'exception', 'logger'];
  /* @ngInject */
  function dataservice($http, exception, logger) {
    var service = {
      getWatchmen: getWatchmen,
      postWatchmen: postWatchmen,
      deleteWatchmenById: deleteWatchmenById
    };

    return service;

    function getWatchmen() {
      return $http.get('/api/watchmen')
        .then(success)
        .catch(fail);

      function success(response) {
        return response.data;
      }

      function fail(e) {
        return exception.catcher('XHR Failed for getWatchmen')(e);
      }
    }

    function postWatchmen(data) {
      return $http.post('/api/watchmen', data)
        .then(success)
        .catch(fail);

      function success(response) {
        return response.data;
      }

      function fail(e) {
        return exception.catcher('XHR Failed for postWatchmen')(e);
      }
    }

    function deleteWatchmenById(data) {
      return $http.delete('/api/watchmen/' + data, data)
        .then(success)
        .catch(fail);

      function success(response) {
        return response.data;
      }

      function fail(e) {
        return exception.catcher('XHR Failed for deleteWatchmenById')(e);
      }
    }
  }

Zuerst ein kleiner Exkurs zu Promises: Promises werden durch asynchrone Funktionen zurückgegeben und geben ein Versprechen ab, dass der Programmteil dieser Funktion ausgeführt wird. Dies geschieht entweder erfolgreich (resolve) oder schlägt fehl (reject). Dadurch kann von vornherein auf erfolgreiche bzw. fehlerhafte Ausführung reagiert werden.

Als Beispiel für Promises dient der Angular-Service $http innerhalb der Factory dataservice.js. Über $http sind XHR-Calls auf entfernte HTTP-Server möglich, die wiederum als Promise-Objekt zurückgegeben werden.

// excerpt dataservice.js
function getWatchmen() {
      return $http.get('/api/watchmen')
        .then(success)
        .catch(fail);

      function success(response) {
        return response.data;
      }

      function fail(e) {
        return exception.catcher('XHR Failed for getWatchmen')(e);
      }
    } 

Der Service $http innerhalb der Funktion getWatchmen greift mittels Get-Methode auf die REST-API des Servers zu und gibt dabei einen Promise zurück. Ist der Zugriff erfolgreich wird die Funktion success ausgeführt und die Daten der Response zurückgegeben. In diesem Fall ein Promise-Objekt, das ein Array mit Superhelden aus der Datenbank enthält. Schlägt der Zugriff fehl, z.B. weil die Verbindung zur Datenbank nicht gelingt, wird der Fehler abgefangen und die Funktion fail wird ausgeführt.
Die genaue Definition der Funktion exception.catcher befindet sich in der Datei /src/client/app/blocks/exception/exception.js.

Die insgesamt drei in der Factory dataservice beinhalteten Funktionen werden in Form eines Service-Objekts zurückgegeben, das anschließend in andere Komponenten der Beispielapplikation injiziert werden kann, z.B. in den WatchmenController, über das die Funktionen an dieser Stelle aufgerufen werden können.

var service = {
      getWatchmen: getWatchmen,
      postWatchmen: postWatchmen,
      deleteWatchmenById: deleteWatchmenById
};

return service;

Der Watchmen-Controller

Als nächstes wird der Controller erstellt, dessen Zuständigkeit in /src/client/app/watchmen/watchmen.route.js festgelegt worden ist. Der WatchmenController wird am Modul app.watchmen registriert und implementiert die Logik der Funktionen aus dem Template watchmen.html (vm.sendData() und vm.deleteData()).

// watchmen.controller.js
  angular
    .module('app.watchmen')
    .controller('WatchmenController', WatchmenController);

  WatchmenController.$inject = ['$q', 'dataservice', 'logger'];
  /* @ngInject */
  function WatchmenController($q, dataservice, logger) {
    var vm = this;

    vm.watchmen = [];
    vm.title = 'Watchmen';

    
    vm.sendData = function () {
      vm.data = {
        firstName: vm.firstName,
        lastName: vm.lastName,
        alias: vm.alias
      };
      return postWatchmen(vm.data);
    };

    
    vm.deleteData = function () {
      return deleteWatchmenById(vm.idToDel);
    };

    activate();

    // controller initialization function
    function activate() {
      var promises = [getWatchmen()];
      return $q.all(promises).then(function () {
        logger.info('Activated Watchmen View');
      });
    }

    function getWatchmen() {
      // returns and manipulates promise
      return dataservice.getWatchmen().then(function (data) {
        vm.watchmen = data;
        return vm.watchmen;
      });
    }

    function postWatchmen(data) {
      // send data via service and refresh table
      return dataservice.postWatchmen(data).then(getWatchmen);
    }

    function deleteWatchmenById(data) {
      // send data via service and refresh table
      return dataservice.deleteWatchmenById(data).then(getWatchmen);
    }
  }

Folgende Services werden in den Controller injiziert: $q, dataservice und logger. Der Service dataservice wurde soeben definiert, $q dient als Promise-Constructor, bietet aber auch generelle Funktionen, um Promises zu behandeln, und logger ist, wie der Name schon sagt, ein selbstgeschriebener Logger (siehe /src/client/app/blocks/logger/logger.js).

// excerpt of watchmen.controller.js

function getWatchmen() {
  // returns and manipulates promise
  // Step 2
  return dataservice.getWatchmen().then(function (data) {
    // Step 3
    vm.watchmen = data;
    return vm.watchmen;
  });
}
activate();

// controller initialization function
function activate() {
  // Step 1 
  var promises = [getWatchmen()];
    // Step 4
    return $q.all(promises).then(function () {
      logger.info('Activated Watchmen View');
    });
} 

Während der Controller initialisiert wird, ruft er die Funktion activate() auf. Anschließend wird var promises als Array aller aufzulösender Promises (in diesem Fall ist das nur eins) definiert. Die im Controller definierte Funktion getWatchmen bedient sich der Funktion dataservice.getWatchmen() aus dataservice.js. Diese Funktion liefert ein Promise-Objekt zurück, das bei Erfolg Daten aus der Datenbank enthält. Der Funktion then wird ein Callback übergeben, deren Parameter data der Rückgabewert des vorherigen Promise-Objekts ist, das anschließend an das Array vm.watchmen (siehe watchmen.html) im Controller gebunden wird. Somit enthält das Array promises also wiederum ein Promise. Zurück zur activate()-Funktion. Die Funktion $q.all(promises) ruft die Funktion then auf, wenn alle Promises in dem Array aufgelöst sind, in diesem Fall ist das nur einer (var promises = [getWatchmen()];).

Das klingt alles furchtbar kompliziert, bedeutet aber vereinfacht im Wesentlichen folgendes:

1. Frage getWatchmen-Funktion nach Daten für die Superhelden und warte auf Promise.
2. Führe dataservice.getWatchmen-Funktion mit Zugriff auf Datenbank aus.
3. Bei Erfolg setze die Rückgabe-Daten aus der DB auf vm.watchmen und löse Promise auf.
4. Führe logger.info aus, weil alle Promises für $q.all aufgelöst sind.

Neue Helden hinzufügen oder entfernen

Durch Erstellen des Controllers stehen nun alle nötigen Komponenten für die zweite View zur Verfügung. Es ist nun möglich, über die Watchmen-View Superhelden über die erste Input-Form anzulegen (vm.sendData()). Alle angelegten Superhelden werden in einer Tabelle angezeigt, die verantwortliche Funktion dafür ist getWatchmen(). Diese können mittels der zweiten Form über ihre generierte ID aus der Datenbank gelöscht werden und werden nun nicht mehr in der Tabelle angezeigt. Die Aktualisierung der Tabelle befindet sich innerhalb der Funktionsdeklaration von postWatchmen(data) und deleteWatchmenById(data).
Die entscheidende Code-Zeile dafür ist return dataservice.postWatchmen(data).then(getWatchmen);.

Beispielsweise lässt sich Edward Blake alias The Comedian zur Datenbank hinzufügen. Anschließend kann über die REST-API geprüft werden, dass er als neuer Eintrag in der Datenbank besteht (localhost:3000/api/watchmen). Durch Kopieren seiner generierten ID lässt sich der Eintrag wieder aus der Datenbank und somit auch aus der Tabelle entfernen.

Event-Handling

AngularJS ermöglicht das Behandeln eintretender Events durch die Funtkion $on. Die $on-Funktion hat die Form $on(name, listener), wobei name der Name eines Events und listener eine Funktion ist, die bei Eintreten des Events ausgeführt wird. $on kann an einen bestimmten $scope oder aber an $rootScope, zum applikationsübergreifenden Event-Handling, gebunden werden. In der Datei /src/client/app/blocks/router/router-helper.provider.js wird die $on-Funktion genutzt, um auf Routingfehler zu reagieren.

function handleRoutingErrors() {
  // Route cancellation:
  // On routing error, go to the welcome view.
  // Provide an exit clause if it tries to do it twice.
  $rootScope.$on('$stateChangeError',
    function(event, toState, toParams, fromState, fromParams, error) {
      if (handlingStateChangeError) {
        return;
      }
      stateCounts.errors++;
      handlingStateChangeError = true;
      var destination = (toState &&
        (toState.title || toState.name || toState.loadedTemplateUrl)) ||
        'unknown target';
      var msg = 'Error routing to ' + destination + '. ' +
        (error.data || '') + '. <br/>' + (error.statusText || '') +
        ': ' + (error.status || '');
      logger.warning(msg, [toState]);
      $location.path('/');
      }
  );
}

Der Code-Ausschnitt illustriert das Event-Handling, wenn ein Fehler beim Wechseln des Zustands auftritt ('$stateChangeError'). Anschließend wird die Listener-Funktion mit dem Event-Objekt event und einigen vordefinierten Argumenten ausgeführt. In diesem Fall wird bei Eintreten eines Routing-Fehlers auf die Welcome-View gewechselt und eine Fehlermeldung durch den Logger ausgegeben.

Rekapitulation des Tutorials

Damit ist das AngularJS-Einstiegs-Tutorial abgeschlossen. Der abschließende Stand der Applikation befindet sich wie schon die vorherigen beiden auf einem eigenen Branch des Repositories. Zu den im zweiten Teil des Tutorials gehörenden neuen Elementen gehören:

  • Template, Controller, Route, und Module für Watchmen-View
  • Services von Constant bis Provider
  • Data-Services und Promises
  • Event-Handling

Selbstverständlich gibt es noch eine Vielzahl weiterer von Angular bereitgestellter Komponenten zur Erstellung eigener Webapplikationen, aber für den Einstieg soll es das gewesen sein. Das AngularJS-Tutorial soll einen generellen Überblick über das Framework AngularJS verschaffen und als Einstieg für größere eigens erstellte Webapplikationen dienen.

Ich hoffe, dass ich durch dieses Einstiegs-Tutorial Euer Interesse für Angular wecken konnte und Ihr bald schon selbst damit anfangt, eigene Applikationen zu schreiben.

Diesen Beitrag teilen

Kommentare

  1. • разметка основания, на котором будут размещена та или иная конструкция и ее планировка;
    • также купить леса строительные цена
    • ознакомление рабочих с конструкцией, проведение инструктажа по ее сборке, креплению и по технике безопасности;
    • раскладка элементов конструкции по периметру установки;
    • размещение в необходимых местах подъемных механизмов, если они будут использоваться при сборке конструкции;
    • проверка каждого элемента, как и щитов настила на предмет выявления повреждений;
    • установка первого яруса;

  2. Что такое размеры рамных лесов строительных – временное вспомогательное сооружение для размещения рабочих и материалов при выполнении строительных, монтажных и других работ. Применяются как снаружи, так и внутри здания. Чаще всего строительные леса собираются из унифицированных металлических и деревянных элементов.

  3. Что такое сборка рамных строительных лесов – временное вспомогательное сооружение для размещения рабочих и материалов при выполнении строительных, монтажных и других работ. Применяются как снаружи, так и внутри здания. Чаще всего строительные леса собираются из унифицированных металлических и деревянных элементов.

Malte Kreutzfeldt
Software Development
Stets mit offenen Augen für neue Open Source Tools, Frameworks und Best Practice Entwicklungsmethoden lässt er der Erweiterung seines IT-Horizonts schier keine Grenzen.