2014-10-24 9 views
5

W mojej dyrektywie niestandardowej dodaję elementy do DOM na podstawie liczby obiektów w mojej tablicy źródeł danych. Muszę obejrzeć konkretną właściwość w każdym obiekcie. Kiedy dodaję te elementy do DOM, chcę ustawić $ watch na własności checked każdego obiektu w tablicy toppings, ale to nie działa i nie wiem dlaczego. Ustawiam breakpoint wewnątrz funkcji, która powinna zostać wywołana, gdy właściwość zmieni się z true na false lub false na true, ale ta funkcja nigdy nie zostanie wywołana. Czy ten powód jest oczywisty? Uczę się właśnie Angulara, więc mógłbym łatwo popełnić głupi błąd.AngularJS: obserwowanie konkretnej właściwości w tablicy obiektów w celu zmiany wartości właściwości

$scope.bits = 66; (i.e. onions and olives) 


$scope.toppings = [ 
    { topping: 1, bits: 2, name: 'onions' }, 
    { topping: 2, bits: 4, name: 'mushrooms' }, 
    { topping: 3, bits: 8, name: 'peppers' }, 
    { topping: 4, bits: 16, name: 'anchovies' }, 
    { topping: 5, bits: 32, name: 'artichokes' }, 
    { topping: 6, bits: 64, name: 'olives' }, 
    { topping: 7, bits: 128, name: 'sausage' }, 
    { topping: 8, bits: 256, name: 'pepperoni' } 
    ] 

Każdy obiekt w modelu otrzymuje nową właściwość checked, która będzie miała wartość true lub false.

UWAGA: tablica obiektów będzie zawierać maksymalnie kilkanaście pozycji. Wydajność nie jest problemem.

link: function link(scope, iElement, iAttrs, controller, transcludeFn) { 


    <snip> 

// At this point scope.model refers to $scope.toppings. Confirmed. 

    angular.forEach(scope.model, function (value, key) { 

    // bitwise: set checked to true|false based on scope.bits and topping.bits 

    scope.model[key].checked = ((value.bits & scope.bits) > 0); 


    scope.$watch(scope.model[key].checked, function() { 
     var totlBits = 0; 
     for (var i = 0; i < scope.model.length; i++) { 
      if (scope.model[i].checked) totlBits += scope.model[i].bits; 
     } 
      scope.bits = totlBits; 
     }); 

    }); 

    <snip> 

Odpowiedz

1

Parametr watchExpression na $ scope. $ Watch powinien być ciągiem lub funkcją. Nie eksperymentowałem z tym w znacznym stopniu (staram się unikać wyraźnych zegarków tam, gdzie to możliwe), ale myślę, że działa również wtedy, gdy ogląda się "proste" właściwości zakresu jako odniesienia do obiektów, ale nie tak dobrze w przypadku bardziej złożonych odniesień.

Myślę, że jeśli podasz referencję jako ciąg znaków, np. 'model [' + key + '] .checked', możesz odnieść sukces (mówię tylko dlatego, że wcześniej zrobiłem coś podobnego za pomocą $ watchCollection).

Alternatywnie powinieneś być w stanie dostarczyć funkcję, np.

$scope.$watch(function() { return scope.model[key].checked; }, function() { ... });

Nadzieja to pomaga!

+0

Podejście funkcyjne zadziałało. Dzięki! – Tim

+0

Wadą tego podejścia jest to, że tworzysz jednego obserwatora na klucz na obiekcie. $ WatchCollection jest bardziej performatywny. –

+1

@William Lepinski: Czy możesz wyjaśnić, dlaczego WatchCollection w tablicy, która musi oglądać każdą nieruchomość na każdym przedmiocie, będzie działał lepiej niż zegarek tylko na obiekcie, który muszę oglądać? Nie kłócę się, chciałbym tylko wiedzieć, jak to się robi. W jaki sposób realizowany jest głęboki zegarek? – Tim

0

Zamiast tego należy użyć $watchCollection.

Od docs:

$ watchCollection (obj, słuchacz); Płytkie zegarki właściwości obiektu i pożary po każdej zmianie właściwości (w przypadku tablic oznacza to obserwowanie elementów tablicy, w przypadku map obiektów oznacza to oglądanie właściwości). Po wykryciu zmiany wywoływane jest wywołanie zwrotne wywołania.

Kolekcja obj jest obserwowana za pomocą standardowej operacji $ watch i jest sprawdzana przy każdym wywołaniu funkcji $ digest(), aby sprawdzić, czy jakiekolwiek elementy zostały dodane, usunięte lub przeniesione. Detektor jest wywoływany za każdym razem, gdy zmieni się coś w obiekcie. Przykłady obejmują dodawanie, usuwanie i przenoszenie elementów należących do obiektu lub tablicy.

+1

ale ogląda wszystkie właściwości kolekcji. nie tylko jedna nieruchomość. W tym przypadku OP chce oglądać tylko "sprawdzoną" właściwość. – harishr

+0

Cóż, możesz zerwać swoją tablicę chwytając tylko sprawdzoną właściwość. Przypisz nowo utworzoną tablicę do zakresu i użyj polecenia $ watchCollection do obejrzenia tablicy sprawdzonych właściwości. –

2

tablicę obiektów:

$scope.toppings = [ 
    { topping: 1, bits: 2, name: 'onions' }, 
    { topping: 2, bits: 4, name: 'mushrooms' }, 
    { topping: 3, bits: 8, name: 'peppers', checked:undefined /*may be*/ } 
]; 

Watch użyciu angularjs $ WatchCollection:

Zamiast monitorowania objects tablicę powyżej, które mogą zmienić dla każdej nieruchomości w obiekcie, będziemy tworzyć tablica właściwości elementów, dla których oglądamy kolekcję (.checked).

Mamy filter elementy tablicy, aby monitorować tylko te elementy, które zostały zdefiniowane i map.checked że do tablicy dla kątowego watchCollection.

Po zmianie ognia, porównę starą i nową macierz (.checked), aby uzyskać dokładny zmieniony element przy użyciu metody lodash difference.

$scope.$watchCollection(
       //Watch Function 
       ()=>($scope.toppings.filter(tp=>tp.checked!=undefined).map(tp=>tp.checked)), 

       //Listner 
       (nv,ov)=>{ 
       if(nv==ov||nv=="undefined")return; 

       //Use lodash library to get the changed obj 
       let changedTop= _.difference(nv,ov)[0]; 

       //Here you go.. 
       console.log("changed Topping",changedTop); 
      }) 
+1

Jest to jedna z lepszych odpowiedzi w moim opinia, i działa znacznie lepiej dla tablic, jednak ze względu na autora pytania i każdego, kto szuka pomocy w tym zakresie, należy dodać więcej szczegółów do swojej odpowiedzi, aby wyjaśnić, że używasz skryptu typu i wyjaśnić swoje rozumowanie do filtrowania i mapowania tablicy. W przeciwnym razie świetne rozwiązanie. – Squall

+0

To nie jest TypeScript, ale JavaScript ES6 :-). Edytowane w celu zastosowania filtra. – ahmadalibaloch

+0

Myślę, że działa to tylko w bardzo specyficznym przypadku (to znaczy, że są to PO, jak wspomniano w uwadze), że obserwowana nieruchomość ma nie więcej niż dwa stany. –

1

użyć MAPA zebrać wszystkie wartości nieruchomości + trzeba konwertować je do małej ciąg znaków (w tym przypadku 1 i 0), a następnie łączą je ze sobą w łańcuch, który może być obserwowany.

maszynopis przykład:

 $scope.$watch(
      () => this.someArray.map(x => x.selected ? "1" : "0").join(""), 
      (newValue, oldValue, scope) => this.onSelectionChanged(this.getSelectedItems())); 
+1

Podejrzewam, że jest to trochę bardziej wydajne niż wykonanie "filtru", a następnie "mapy", jak w drugiej odpowiedzi @ahmadalibaloch –

Powiązane problemy