2013-07-15 8 views
43

Po godzinach frustrujących wyszukiwań czuję, że muszę przesłać moje pytanie tutaj. Z góry przepraszam, jeśli na to pytanie odpowiedziano wcześniej, ale żadne z moich wyszukiwań nie pomogło do tej pory. Oto moje pytanie:Jak zmienić dane AngularJS poza zakresem?

Mój kod JavaScript tworzy obiekt, który jest modyfikowany i monitorowany przez AngularJS. W przypadku niektórych zdarzeń (takich jak wczytanie poprzedniego ustawienia obiektu), chcę zmienić właściwości tego obiektu spoza zakresu. Problemem jest to, że wejścia nie zmieni ...

Oto przykład, jak pragnę przeprowadzić te zmiany:


HTML kod:

<div ng-app="myApp" ng-controller="FirstCtrl"> 
<input type="number" ng-model="data.age"> 
<h1>{{data.age}}</h1> 

<input type="button" value="Change to 20" ng-model="data" onclick="change()"> 

kodu JavaScript :

var person = { 
    age: 16 
}; 

// Create module 
var myApp = angular.module('myApp', []); 
myApp.factory('Data', function() { 
    return person; 
}); 

function FirstCtrl($scope, Data) { 
    $scope.data = Data; 
} 

function change() { 
    person.age = 20; 
} 

Po naciśnięciu przycisku "Zmień na 20" nic się nie dzieje. Jak mogę zmienić wiek osoby z funkcji change?

Odpowiedz

71

⚠️ Ostrzeżenie: Ta odpowiedź jest stary, nie odzwierciedla najlepsze praktyki i mogą nie być kompatybilne z nowszymi wersjami Angular.

Odpowiedź MaxPRafferty jest poprawna - używanie funkcji w zakresie jest często lepszym sposobem na zrobienie tego - ale jest inna opcja. Możesz użyć metody angular.element(...).scope(), aby uzyskać dostęp do zakresu kątowego z niespokrewnionego skryptu JavaScript. Wybierz zakres najwyższego poziomu dla aplikacji poprzez ukierunkowanie element, który posiada atrybut ng-app określony, z czymś w swoim click obsługi:

function change() { 
    var appElement = document.querySelector('[ng-app=myApp]'); 
    var $scope = angular.element(appElement).scope(); 
    $scope.$apply(function() { 
     $scope.data.age = 20; 
    }); 
} 

Wypróbuj in this Fiddle.

Shaunjust pointed out że Angular przetwarza tylko "zegarki" lub "wiązania" podczas połączenia $digest(). Jeśli po prostu zmienisz bezpośrednio właściwości $scope, zmiany mogą nie zostać natychmiast odzwierciedlone i mogą pojawić się błędy.

Aby wywołać tę funkcję, możesz zadzwonić pod numer $scope.$apply(), który sprawdzi, czy nie ma brudnych zakresów i zaktualizuje wszystko, co jest poprawnie powiązane. Przekazanie funkcji, która wykonuje pracę wewnątrz $scope.$apply, pozwoli również Angularowi wychwycić wszelkie wyjątki. To zachowanie wyjaśniono w the documentation for Scope.

+0

To działa idealnie! Dziękuję Panu bardzo. Ilekroć zmieniam właściwości mojego obiektu poza kontrolerem, muszę to zrobić. Czy to nie dziwne? – gromit190

+0

@BirgerSkogengPedersen Angular może być bardzo miły i zwięzły, gdy używasz kodu, który jest w całości napisany w stylu Angular (co w tym przypadku oznaczałoby użycie 'ng-click' z metodą zdefiniowaną w twoim zasięgu), ale masz rację : może być dość gadatliwy, gdy musisz komunikować się między innym kodem JavaScript i Angular. Obawiam się, że nie mam wystarczająco dużo doświadczenia z Angular, aby naprawdę komentować, jak sobie z tym poradzić. –

+0

Ah, pokonaj mnie .scope() Jeremy! Jeśli chodzi o twoje pytanie, @BirgerSkogengPedersen, tak, to jest jedna z głównych skarg na kątowe - ogólna odpowiedź kątowa polega na tym, że jeśli modyfikujesz zakresy używając zewnętrznego kodu, to nie jesteś przyzwyczajony do MVC i sprawiasz, że twój kod jest mniej łatwy w utrzymaniu. Wydaje mi się, że pojawia się to najczęściej podczas pracy z innymi bibliotekami stron trzecich, które są wymagane przez projekt, a często najlepszym sposobem na ich obsługę jest dołączenie funkcji stron trzecich w zasięgu globalnym lub w funkcjach kompilacji i linkowania niestandardowej dyrektywy. dla tego. – MaxPRafferty

5

http://jsfiddle.net/MaxPRafferty/GS6Qk/

Chcesz ustawić atrybut ng-kliknij, aby funkcja w swoim zakresie, jak następuje:

var person = { 
    age: 16 
}; 

// Create module 
var myApp = angular.module('myApp', []); 
myApp.factory('Data', function() { 
    return person; 
}); 

function FirstCtrl($scope, Data) { 
    $scope.data = Data; 
    $scope.update = function(){ 
     $scope.data.age = 20; 
    } 
} 
+0

Dzięki za odpowiedź, ale nadal nie rozumiem, jak przycisk zmieniłby wiek osoby. Nawet jeśli tak, to jak będę w stanie zmienić wiek osób poza kontrolerem? Przepraszam, jeśli moje pierwotne pytanie było trochę niejasne. – gromit190

+0

Dlaczego potrzebujesz fabryki, jeśli możesz przypisać wartość bezpośrednio? – Alex78191

14

Jeremy's answer jest naprawdę dobre, choć teraz kątowa nie zmieniło, i nie będzie już pracować, chyba że dodasz ten wiersz kodu:

$scope = $scope.$$childHead; 

Więc zmieniona funkcja powinna wyglądać tak

function change() { 
    var appElement = document.querySelector('[ng-app=myApp]'); 
    var $scope = angular.element(appElement).scope(); 
    $scope = $scope.$$childHead; // add this and it will work 
    $scope.$apply(function() { 
     $scope.data.age = 20; 
    }); 
} 
+0

Hm, '$$ childHead' ma wartość' null' dla mnie ... –

+0

czy zaakceptowana odpowiedź działa dla ciebie? – Aleksandrus

+0

idealne! Proponuję zmienić drugą odpowiedź: – eddi

0

Metoda $ render jest wywoływana przez dyrektywę ng-model, gdy wartość została zmodyfikowana poza dyrektywą. Uzyskaj nową wartość, czytając właściwość $ viewValue. Spójrz: https://jsfiddle.net/cesar_ade/g5ybs6ne/

ctrl.$render=function(){ 
    setSelected(ctrl.$viewValue || 'Not Sure'); 
} 
2

Korzystanie doskonałą odpowiedź Jeremy Banks, sporządzonej w jednej linii, choć ja przedstawieniu kontroler vs aplikacji:

angular.element('[ng-controller=myController]').scope().$apply(function(x){ x.foo = "bar"; }); 
2

prostu użyć krótkiego $ limit czasu

var whatever = 'xyz'; 
$timeout(function(){ 
    $scope.yourModel.yourValue = whatever; 
}, 0); 

i gotowe.

Próbowałem wszystkich tych hacków z aplikacjami $ z całej strony przedtem i wszystkie nie działały. Ale ten limit czasu działa zawsze jak czar i w każdym przypadku. Wszystko czego potrzebujesz, to referencja twojego $ scope i $ timeout.

Wy i jak to działa:

// Imagine an angular buildin-function 
angular.$queue = function(fn){ 
    $timeout(fn, 0); 
} 

To działa w zasadzie jak kolejce. Ale pamiętaj, że jest to asynchroniczne.

+0

pracował jak urok. CZEMU?! jak to działa ?! :) –

+0

Ponieważ $ timeout jest przetwarzany w ramach procedur skrótu angulars. A $ timeout z zerowym (0) czasem działa dokładnie tak samo jak kolejka. Lub innymi słowy: z $ timeout (fn, 0) mówisz "Hej kanciaste, zrób to później, proszę, jak tylko skończysz swoją obecną pracę." – Steffomio