web-dev-qa-db-fra.com

Comment modifier les données AngularJS en dehors de la portée?

Après des heures de recherches frustrantes, j’ai le sentiment que je dois poser ma question ici. Je m'excuse par avance si cette question a déjà reçu une réponse, mais aucune de mes recherches n'a été utile jusqu'à présent. Alors voici ma question:

Mon code JavaScript crée un objet qui est modifié et surveillé par AngularJS. Sur certains événements (comme charger un paramètre précédent de l'objet), je souhaite modifier les propriétés de cet objet en dehors de la portée. Le problème est que les entrées ne changent pas ...

Voici un exemple de la façon dont je souhaite effectuer ces modifications:


Code HTML:

<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()">

Code 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;
}

Quand j'appuie maintenant sur le bouton "Changer en 20", rien ne se passe. Comment modifier l'âge de la personne à partir de la fonction change?

44
gromit190

⚠️ Avertissement: Cette réponse est ancienne, ne reflète pas les meilleures pratiques et peut ne pas être compatible avec les versions les plus récentes de Angular. 

La réponse de MaxPRafferty est correcte - utiliser une fonction dans la portée est souvent la meilleure façon de procéder - mais il existe une autre option. Vous pouvez utiliser la méthode angular.element(...).scope() pour accéder à une étendue angulaire à partir de JavaScript non associé. Sélectionnez l'étendue de niveau supérieur de l'application en ciblant l'élément ayant l'attribut ng-app spécifié, avec quelque chose comme dans votre gestionnaire de clics:

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

Essayez-le dans ce Fiddle }.

Shaun _ { je viens de souligner qu'Annular ne traitera que les "surveillances" ou les "liaisons" lors d'un appel $digest(). Si vous modifiez simplement les propriétés du $scope directement, les modifications risquent de ne pas être immédiatement reflétées et des bogues risquent de se produire.

Pour déclencher cela, vous pouvez appeler $scope.$apply() qui va vérifier les étendues sales et mettre à jour tout ce qui est lié correctement. Le fait de passer une fonction qui effectue le travail dans $scope.$apply permettra également à Angular d’attraper toutes les exceptions. Ce comportement est expliqué dans la documentation de Scope .

71
Jeremy Banks

La réponse de Jeremy est vraiment bonne, mais maintenant Angular a changé, et ne fonctionnera plus, à moins que vous n'ayez ajouté cette ligne de code:

$scope = $scope.$$childHead;

Donc, la fonction modifiée devrait ressembler à ceci

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;
    });
}
14
Aleksandrus

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

Vous souhaitez définir votre attribut ng-click sur une fonction de votre portée, comme suit:

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;
    }
}
5
MaxPRafferty

Utilisez simplement un court $ timeout

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

et vous avez terminé.

J'ai déjà essayé toutes ces astuces $ apply du site www auparavant et elles ne fonctionnaient pas toutes. Mais ce $ timeout fonctionne toujours comme un charme et dans tous les cas . Vous avez uniquement besoin d'une référence de votre $ scope et de votre $ timeout.

Wy et comment ça marche:

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

Cela fonctionne fondamentalement comme une file d'attente. Mais sachez que c'est asynchrone.

3
Steffomio

En utilisant la réponse parfaite de Jeremy Banks, en une seule ligne, même si je fais référence au contrôleur par rapport à l'application:

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

Avec $ render, la méthode est appelée par la directive ng-model lorsque la valeur a été modifiée en dehors de la directive. Obtenez une nouvelle valeur en lisant la propriété $ viewValue. Regardez: https://jsfiddle.net/cesar_ade/g5ybs6ne/

ctrl.$render=function(){
    setSelected(ctrl.$viewValue || 'Not Sure');
}
0
César Alcívar