2017-07-07 8 views
0

Ich möchte eine Direktive, die mit einer Liste von Objekten funktioniert, aber ich muss auch entweder eine 2-Wege-Bindung oder eine Funktion binden.

app.directive('myDir', function() { 
    return { 
     scope: { 
      list: "=?", 
      list_func: "&?listFunc" 
     }, 

     controller: ['$scope', function($scope) { 
      $scope.get_list = function() { 
       if($scope.list !== undefined) 
        return $scope.list(); 

       if($scope.list_func !== undefined) 
        return $scope.list_func()(); 
      }; 
     }] 
    }; 
}); 

Allerdings, wenn ich die listFunc Attribut mit einer Funktion, die eine Liste zurückgibt, bekomme ich diesen Fehler:

VM607 angular.js:68 Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting! Watchers fired in the last 5 iterations: [[{"msg":"fn: regularInterceptedExpression","newVal":19,"oldVal":17},{"msg":"fn: regularInterceptedExpression","newVal":"Harry"},{"msg":"fn: regularInterceptedExpression","newVal":"65"},{"msg":"fn: regularInterceptedExpression","newVal":"Sally"},{"msg":"fn: regularInterceptedExpression","newVal":"66"},{"msg":"fn: regularInterceptedExpression","newVal":9,"oldVal":8},{"msg":"fn: regularInterceptedExpression","newVal":"val"},{"msg":"fn: regularInterceptedExpression","newVal":"1"}],[{"msg":"fn: regularInterceptedExpression","newVal":21,"oldVal":19},{"msg":"fn: regularInterceptedExpression","newVal":"Harry"},{"msg":"fn: regularInterceptedExpression","newVal":"65"},{"msg":"fn: regularInterceptedExpression","newVal":"Sally"},{"msg":"fn: regularInterceptedExpression","newVal":"66"},{"msg":"fn: regularInterceptedExpression","newVal":10,"oldVal":9},{"msg":"fn: regularInterceptedExpression","newVal":"val"},{"msg":"fn: regularInterceptedExpression","newVal":"1"}],[{"msg":"fn: regularInterceptedExpression","newVal":23,"oldVal":21},{"msg":"fn: regularInterceptedExpression","newVal":"Harry"},{"msg":"fn: regularInterceptedExpression","newVal":"65"},{"msg":"fn: regularInterceptedExpression","newVal":"Sally"},{"msg":"fn: regularInterceptedExpression","newVal":"66"},{"msg":"fn: regularInterceptedExpression","newVal":11,"oldVal":10},{"msg":"fn: regularInterceptedExpression","newVal":"val"},{"msg":"fn: regularInterceptedExpression","newVal":"1"}],[{"msg":"fn: regularInterceptedExpression","newVal":25,"oldVal":23},{"msg":"fn: regularInterceptedExpression","newVal":"Harry"},{"msg":"fn: regularInterceptedExpression","newVal":"65"},{"msg":"fn: regularInterceptedExpression","newVal":"Sally"},{"msg":"fn: regularInterceptedExpression","newVal":"66"},{"msg":"fn: regularInterceptedExpression","newVal":12,"oldVal":11},{"msg":"fn: regularInterceptedExpression","newVal":"val"},{"msg":"fn: regularInterceptedExpression","newVal":"1"}],[{"msg":"fn: regularInterceptedExpression","newVal":27,"oldVal":25},{"msg":"fn: regularInterceptedExpression","newVal":"Harry"},{"msg":"fn: regularInterceptedExpression","newVal":"65"},{"msg":"fn: regularInterceptedExpression","newVal":"Sally"},{"msg":"fn: regularInterceptedExpression","newVal":"66"},{"msg":"fn: regularInterceptedExpression","newVal":13,"oldVal":12},{"msg":"fn: regularInterceptedExpression","newVal":"val"},{"msg":"fn: regularInterceptedExpression","newVal":"1"}]] http://errors.angularjs.org/1.6.2/$rootScope/infdig?p0=10&p1=%5B%5B%7B%22ms…2fn%3A%20regularInterceptedExpression%22%2C%22newVal%22%3A%221%22%7D%5D%5D at VM607 angular.js:68 at Scope.$digest (VM607 angular.js:17893) at Scope.$apply (VM607 angular.js:18125) at done (VM607 angular.js:12233) at completeRequest (VM607 angular.js:12459) at XMLHttpRequest.requestLoaded (VM607 angular.js:12387)

Ich habe this plunker Beispiel (mit einem offenen einer Javascript-Konsole Sie diese Fehler sehen) zeigen, was ich brauche. In diesem Beispiel erhalte ich diese Fehler, aber die Ansicht wird immer noch aktualisiert. Auf der App, die ich entwickle (was viel größer ist), bekomme ich so viele $digest Fehler, dass die Seite langsamer wird und die Ansicht nicht aktualisiert wird.

Was ist der beste Weg, um eine Funktion zu binden, ohne in einer Endlosschleife zu enden?

+0

Expression '&' Bindung sollte nur verwendet werden Ereignisse an eine übergeordnete Steuerung zu kommunizieren. Verwenden Sie einen Weg '<', um Daten von einem übergeordneten Controller zu erhalten. Vermeiden Sie eine beidseitige '=' Bindung. – georgeawg

+0

Mögliches Duplikat von [Funktion in eckig wird immer ausgeführt] (https://stackoverflow.com/questions/33380828/function-in-angular-keeps-getting-executed/33380887#33380887). – georgeawg

+0

@georgeawg danke für den Kommentar. Ich vermeide die Zweiwege-Bindung so viel wie möglich, in meinem Fall ändert sich die Eingabeliste leicht, abhängig von der Interaktion des Benutzers mit der Direktive. – Pablo

Antwort

1

Angular registriert eine implizite $watchCollection auf der get_list() Ausdruck. Dieser Ausdruck wird mindestens zweimal aufgerufen, weil angular überprüfen möchte, ob sich das Modell stabilisiert hat.

<ul> 
    <li ng-repeat="obj in get_list()"> 
    name: {{ obj.name }}<br /> 
    age: {{ obj.age }} 
    </li> 
</ul> 

Ihr Code gibt eine andere Array von Objekten, jedes Mal get_list() so Winkel genannt verdaut wird fortgesetzt, bis es die maximale Digest Zyklen Grenze erreicht.

$scope.get_list = function(val) { 
    return function() { 
     return [ { name: "val", age: val } ]; 
    } 
    } 

Obwohl der Inhalt identisch ist, erstellt die Funktion jedes Mal, wenn sie aufgerufen wird, neue Objekte.

Vermeiden Sie Funktionsaufrufe in Vorlagen. Setzen Sie stattdessen Werte auf den Bereich und lassen Sie die Richtlinie auf Änderungen dieser Werte reagieren.

Aus dem Text & Tabellen:

Error: $rootScope:infdig

This error occurs when the application's model becomes unstable and each $digest cycle triggers a state change and subsequent $digest cycle. AngularJS detects this situation and prevents an infinite loop from causing the browser to become unresponsive.

One common mistake is binding to a function which generates a new array every time it is called. For example:

<div ng-repeat="user in getUsers()">{{ user.name }}</div> 
$scope.getUsers = function() { 
    return [ { name: 'Hank' }, { name: 'Francisco' } ]; 
}; 

Since getUsers() returns a new array, AngularJS determines that the model is different on each $digest cycle, resulting in the error. The solution is to return the same array object if the elements have not changed:

var users = [ { name: 'Hank' }, { name: 'Francisco' } ]; 

$scope.getUsers = function() { 
    return users; 
}; 

— AngularJS Error Reference - Error: $rootScope:infdig


Should I give up with this kind of functions and use a filter instead?

TL; DR Ja.

Komponenten sollten nur als "Skins" fungieren. Sie sollten nur die ihnen übergebenen Daten anzeigen und Benutzerereignisse an den übergeordneten Controller übermitteln. Das Modell sollte die Single Source of Truth sein. Die Richtlinie sollte nicht Daten von einem übergeordneten Controller abrufen müssen.

Der Ausdruck & Bindung sollte nur verwendet werden, um Benutzerereignisse an den übergeordneten Controller zu kommunizieren. Der übergeordnete Controller sollte dann das Modell entsprechend ändern. Diese Separation of Concerns sorgt für eine App, die leichter zu verstehen, zu debuggen, zu testen und zu pflegen ist.


One more question: this also applies for functions returning functions, right?

Das sollte "Funktionen Rückkehr Funktionen" Muster vermieden werden. Es sorgt für eine verwirrende Vorlage. Stattdessen rufen Funktionen mit Einheimischen:

<my-component on-event="gotEvent($event)"> 
</my-compnent> 
scope: { onEvent: "&" }, 
template: `<input ng-model=myModel ng-change="myChange()" />`, 
link: function(scope, elem, attrs) { 
    scope.myChange = function() { 
     scope.onEvent({$event: myModel}); 
    }; 
} 

Ich empfehle, mit $ lokale Variable prefixing sie von variablen geordneten Bereich zu unterscheiden.Einige Leute empfehlen, $event zu verwenden, um den Konventionen von Angular 2+ zu entsprechen.

Aus den Text & Tabellen:

  • & or &attr - provides a way to execute an expression in the context of the parent scope. If no attr name is specified then the attribute name is assumed to be the same as the local name. Given <my-component my-attr="count = count + $value"> and the isolate scope definition scope: { localFn:'&myAttr' } , the isolate scope property localFn will point to a function wrapper for the count = count + $value expression. Often it's desirable to pass data from the isolated scope via an expression to the parent scope. This can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression is increment($amount) then we can specify the amount value by calling the localFn as localFn({$amount: 22}) .

— AngularJS Comprehensive Directive API Reference ($scope)

+0

danke für die Erklärung, ich hätte das wissen müssen, ich denke, ich habe nicht aufmerksam genug aufgepasst. – Pablo

+0

Noch eine Frage: das gilt auch für Funktionen, die Funktionen zurückgeben, oder? Ich habe meinen Plünderer (https://plnkr.co/edit/OxuCZZgf5i06SuOU9LYl?p=preview) so modifiziert, dass 'get_peeps' das gleiche Objekt zurückgibt. Aber "get_peeps" gibt tatsächlich eine Funktion zurück, die dasselbe Objekt zurückgibt. Jetzt habe ich keinen Fehler bekommen, aber wenn ich meinen HTML-Code in der Vorlage mit einem anderen Parameter vervielfältige '' dann bekomme ich wieder '$ rootScope: infdig'. Beide Direktiven haben unterschiedliche Bereiche, warum erhalte ich diesen Fehler jetzt? – Pablo

+0

Soll ich mit dieser Art von Funktionen aufgeben und stattdessen einen Filter verwenden? – Pablo

Verwandte Themen