Synchronisieren von Angular-“Service”-Listendaten mit Controllern

Das Lernen von Angular war einer der größten Produktivitätssteigerungen für die schnelle Anwendungsentwicklung in meiner Karriere. Einige der gängigen Strategien sind meiner Meinung nach jedoch verbesserungswürdig.

In AngularJS wird großer Wert auf die Trennung von Anliegen gelegt. Eines der am häufigsten praktizierten Muster zum Halten des Anwendungsstatus ist das Verschieben dieser Daten in Winkeldienste oder Fabriken. Welche der ersteren zu verwenden ist, ist meiner Meinung nach völlig eine persönliche Präferenz (ich entscheide mich die meiste Zeit für Fabriken).

Der Zweck dieses Beitrags richtet sich an Dienste, die Sammlungen enthalten, die durch Remote-Datenaufrufe aktualisiert werden.

Gezielte AngularJS-Version zum Zeitpunkt des Schreibens: 1.2.19

Zu oft bin ich auf eckige Controller gestoßen, die das einbringen $scope Dienst nur zu $watch eine Service-Sammlung:

Das Schlechte:

app.controller('Ctrl1', function($scope, DataFactory) {
  
  this.items = DataFactory.items;
  
  
  $scope.$watch(watchSource, function(current, previous){
    this.items = current;
  });
  
  function watchSource(){
    return DataFactory.items;
  }  
});

app.factory('DataFactory', function($q, $timeout) {
  var svc = {};
  svc.items = [];

  svc.getDataStream = function() {
    
    var fakeData = [
      { id: 1, name: 'name 1' },
      { id: 2, name: 'name 2' },
      { id: 4, name: 'name 4' }
    ];
    
    
    return $q.when(fakeData)
      .then(function(data) {
        svc.items = data;
      });
  };

  return svc;
});

Ich weiß, was Sie denken: “Warum funktioniert das nicht einfach standardmäßig?” Wir aktualisieren eindeutig die DataFactory.items.

svc.getDataStream = function() {
  return $q.when(fakeData)
    .then(function(data) {
      
      
      
      svc.items = data;
    });
};

Der Grund dafür, dass angle diesen Wert nicht „beobachtet“, liegt darin, dass das Implizite festgelegt wird $watch aus einer Ansicht verweist es auf das ursprüngliche Array aus der DataFactory. Aber wenn die Daten wieder durchkommen $http, wird die Eigenschaft durch eine andere Array-Referenz ersetzt. Daher kann Angular die Sammlung nicht ansehen, ohne böse hinzuzufügen $watch Funktionen in Ihren Steuerungen.

Warum also ist die $watch eine schlechte Sache in der Steuerung?

Es erhöht die kognitive Belastung, die erforderlich ist, um zu verstehen, was in der Steuerung vor sich geht. Wenn wir uns Code ansehen, den andere (oder wir selbst in 6 Monaten) geschrieben haben, können wir den leicht verstehen was ohne viel Zeit mit dem Analysieren der wie ist sehr vorteilhaft. Alle Bindungen in Ihrer Ansicht führen dazu, dass implizite Überwachungen festgelegt werden. Auch alle $watch Funktionen werden für alle ausgeführt $digest die in einem „Verdauungszyklus“ viele Male auftreten können (angular startet diese bei den meisten Interaktionen). Das Hinzufügen einer weiteren Uhr ist ein Muster, das Sie in Zukunft in Leistungsprobleme bringen kann.

Sicherlich können wir die Abhängigkeit von beseitigen $scope nur um diese Sammlung zu sehen. Betrachten wir ein sehr einfaches Beispiel für diese angepasste Trennung Todd Motto.

Je besser:

app.controller('Ctrl1', function(DataFactory) {
  
  this.items = DataFactory.items;

  
  DataFactory
    .getDataStream()
    .then(function() {
      
      this.items = DataFactory.items;
    }.bind(this));
});


app.factory('DataFactory', function($q, $timeout) {
  var svc = {};
  svc.items = [];

  svc.getDataStream = function() {
    
    var fakeData = [
      { id: 1, name: 'name 1' },
      { id: 2, name: 'name 2' },
      { id: 4, name: 'name 4' }
    ];
    
    
    return $q.when(fakeData)
      .then(function(data) {
        svc.items = data;
      });
  };

  return svc;
});

Jetzt hasse ich dieses Format nicht. Hier werden nach jedem Aufruf zum Aktualisieren der Dienstdatenquelle sowohl die Quelle als auch die Controller-Referenz aktualisiert. Es funktioniert für das, wofür es bestimmt ist, und es ist ziemlich einfach zu verstehen, was vor sich geht. Es adressiert jedoch nicht wirklich andere Controller als denjenigen, der den aktualisierten Dienst aufruft, der benachrichtigt wird.

Wie wäre es damit:

app.controller('Ctrl1', function(DataFactory) {
  
  this.items = DataFactory.items;
 
  
  
  DataFactory.getDataStream();
});

Liest sich das nicht einfach so viel sauberer?

Eintreten angular.copy. Was angular.copy tut, wenn ein neues Array gegeben wird und ein Quell-Array leer ist, die Quelle (durch Setzen der Länge auf 0 Ich denke …) und dann das Array mit den neuen Array-Elementen neu füllen.

Unser Endergebnis:

var app = angular.module('app', []);

app.controller('Ctrl1', function(DataFactory) {
  this.items = DataFactory.items;

  DataFactory.getDataStream();
});

app.controller('Ctrl2', function($timeout, DataFactory) {
  
  
  
  $timeout(DataFactory.getDataStream, 2000);
});

app.factory('DataFactory', function($q) {
  var svc = {};
  svc.items = [];

  svc.getDataStream = function() {
    
    var fakeData = [
      { id: 1, name: 'name 1' },
      { id: 2, name: 'name 2' },
      { id: 4, name: 'name 4' }
    ];
    
    
    return $q.when(fakeData)
      .then(function(data) {
        
        angular.copy(data, svc.items);
      });
  };

  return svc;
});

Dieser Artikel wurde ursprünglich auf veröffentlicht mein Blog.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *