Le modèle Ng ne met pas à jour la valeur du contrôleur

270

Question probablement idiote, mais j'ai mon formulaire html avec une simple entrée et un bouton:

<input type="text" ng-model="searchText" />
<button ng-click="check()">Check!</button>
{{ searchText }}

Puis dans le contrôleur (le modèle et le contrôleur sont appelés depuis routeProvider):

$scope.check = function () {
    console.log($scope.searchText);
}

Pourquoi la vue est-elle mise à jour correctement mais non définie dans la console lorsque je clique sur le bouton?

Merci!

Mise à jour: On dirait que j'ai réellement résolu ce problème (avant j'ai dû trouver des solutions) avec: Je n'ai eu qu'à changer le nom de ma propriété de searchTextà search.text, puis définir un $scope.search = {};objet vide dans le contrôleur et le tour est joué ... Je ne sais pas pourquoi cela fonctionne bien que ;]

alchimisation
la source
êtes-vous sûr que vous utilisez ce contrôleur dans cette partie du document? pouvez-vous publier un exemple d'échec minimal?
wroniasty
1
Oui, 100% sûr que le contrôleur est ok, ce problème me semble familier ... Étonnamment, cela fonctionne lorsque je change le nom de la propriété searchTexten search.text, aucune idée pourquoi?
alchemication
4
@Arthur: Ce n'est pas évident, mais ng-model ne crée qu'une sorte de variable locale locale dans votre vue, donc si vous voulez le garder de cette façon, vous devrez le passer dans la fonction check (), comme: check ( searchText) et votre contrôleur le reconnaîtra alors. J'espère que ça aide
alchemication
3
Pour mémoire, il est écrit voila, non vuala, wollaetc.
Dan Bechard
3
Je pense que la réponse que vous cherchez est sur stackoverflow.com/a/14049482/1217913
Willa

Réponses:

71

Contrôleur comme version (recommandé)

Voici le modèle

<div ng-app="example" ng-controller="myController as $ctrl">
    <input type="text" ng-model="$ctrl.searchText" />
    <button ng-click="$ctrl.check()">Check!</button>
    {{ $ctrl.searchText }}
</div>

Le JS

angular.module('example', [])
  .controller('myController', function() {
    var vm = this;
    vm.check = function () {
      console.log(vm.searchText);
    };
  });

Un exemple: http://codepen.io/Damax/pen/rjawoO

Le mieux sera d'utiliser un composant avec Angular 2.x ou Angular 1.5 ou supérieur

########

À l'ancienne (NON recommandé)

Ceci n'est PAS recommandé car une chaîne est une primitive, il est fortement recommandé d'utiliser un objet à la place

Essayez ceci dans votre balisage

<input type="text" ng-model="searchText" />
<button ng-click="check(searchText)">Check!</button>
{{ searchText }}

et cela dans votre manette

$scope.check = function (searchText) {
    console.log(searchText);
}
Damax
la source
17
Cela ne fonctionne que dans un sens ... que faire si vous souhaitez modifier la valeur de searchText?
cdmckay
199
Cela ne répond pas non plus au "pourquoi?" question.
cdmckay
4
que faire si je ne veux utiliser aucun bouton? je dois soumettre sur entrer par exemple
danikoren
2
Saniko => Pour soumettre sur enter, vous devez utiliser le formulaire tag et ng-submit dessus ( docs.angularjs.org/api/ng.directive:ngSubmit )
Damax
1
@ HP's411 c'est parce qu'une chaîne est une primitive. Lorsque Angular affecte la valeur, il change le pointeur sur la valeur, donc le contrôleur regarde l'ancienne valeur car il a l'ancien pointeur sur la valeur.
Damax
620

"Si vous utilisez ng-model, vous devez avoir un point dedans."
Faites pointer votre modèle vers une propriété object.property et vous serez prêt à partir.

Manette

$scope.formData = {};
$scope.check = function () {
  console.log($scope.formData.searchText.$modelValue); //works
}

Modèle

<input ng-model="formData.searchText"/>
<button ng-click="check()">Check!</button>

Cela se produit lorsque des étendues enfants sont en jeu - comme des routes enfants ou des répétitions ng. La portée enfant crée sa propre valeur et un conflit de nom naît comme illustré ici :

Voir ce clip vidéo pour plus: https://www.youtube.com/watch?v=SBwoFkRjZvE&t=3m15s

Will Stern
la source
94
Telle est la vraie réponse.
keslert
16
@Catfish Avec l'héritage prototypique, chaque fois que vous écrivez dans la propriété enfant, la référence / connexion à la propriété parent cesse d'exister. La façon dont les portées angulaires modélisent, si vous n'avez pas de point, alors une nouvelle propriété dans votre portée enfant est créée. Cette vidéo l'explique plus en détail: youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m
Will Stern le
1
Gracias Boss. C'est vrai! Cependant, cela ne semble pas être une bizarrerie cohérente car je ne rencontre le problème que lors de l'utilisation avec le framework Ionic
Don Barry
6
@ user3677331 Cela fonctionne très bien sans point jusqu'à ce que vous ayez une portée enfant qui essaie de lui parler (comme un élément dans une répétition ng par exemple). Supposons que le nom de votre modèle était "téléphone". Votre portée enfant crée "téléphone", puis vous obtenez un conflit de portée car la portée enfant a une variable "téléphone" et ne peut donc pas accéder à "téléphone" sur la portée parent. Alors que si la portée enfant crée user.phone, elle sera ajoutée à l'objet utilisateur du parent afin que les deux portées pointent vers le même objet
Will Stern
3
Très utile. Je n'étais pas sûr de trouver une réponse à une question relativement vague, mais c'était mort.
CodeManiak
57

Dans Mastering Web Application Development with AngularJS book p.19, il est écrit que

Évitez les liaisons directes aux propriétés de la portée. La liaison de données bidirectionnelle aux propriétés de l'objet (exposées sur une étendue) est une approche préférée. En règle générale, vous devriez avoir un point dans une expression fournie à la directive ng-model (par exemple, ng-model = "thing.name").

Les étendues ne sont que des objets JavaScript et imitent la hiérarchie dom. Selon l' héritage du prototype JavaScript , les propriétés des étendues sont séparées par des étendues. Pour éviter cela, la notation par points doit utiliser pour lier les modèles ng.

efirat
la source
2
merci pour cette référence. Je considère que c'est un point intéressant lié au modèle ng.
Kings
44

Utilisation thisau lieu de $scopetravaux.

function AppCtrl($scope){
  $scope.searchText = "";
  $scope.check = function () {
    console.log("You typed '" + this.searchText + "'"); // used 'this' instead of $scope
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app>
  <div ng-controller="AppCtrl">
    <input ng-model="searchText"/>
    <button ng-click="check()">Write console log</button>
  </div>
</div>

Edit: Au moment d'écrire cette réponse, j'avais une situation beaucoup plus compliquée que cela. Après les commentaires, j'ai essayé de le reproduire pour comprendre pourquoi ça marche, mais pas de chance. Je pense thisqu'en quelque sorte (je ne sais pas vraiment pourquoi) une nouvelle portée enfant est générée et fait référence à cette portée. Mais s'il $scopeest utilisé, il fait référence à la portée $ parent en raison de la fonctionnalité de portée lexicale de javascript .

Ce serait génial si quelqu'un ayant ce problème testait de cette façon et nous en informer.

Feyyaz
la source
Parfait, vous êtes un génie
fjcero
1
On dirait que les fonctions attachées à $scopecréent une nouvelle portée (c'est-à-dire que dans la fonction check(), se thisréfère à une portée qui est une portée enfant $scope). Pourquoi en est-il ainsi? Pouvez-vous donner quelques explications?
Xun Yang
1
pourquoi devrais-je utiliser «ceci» au lieu de «$ scope» dans ce cas? parce que cela fonctionnait en utilisant $ scope dans mon projet précédent mais cette fois la même chose ne fonctionne pas en utilisant $ scope. Mais «cela» a fonctionné. Pourquoi donc?
Rashedul.Rubel
cela fonctionne, mais si vous dites this.searchText = "something else"ou même si $scope.searchText = "something else"cela ne met pas à jour la vue. pouvez-vous expliquer ce problème?
Shri
cela devrait. Je l'ai essayé en utilisant le code ci-dessus, il a mis à jour la vue.
Feyyaz
13

J'ai eu le même problème et c'est parce que je n'ai pas déclaré l'objet vide en premier en haut de mon contrôleur:

$scope.model = {}

<input ng-model="model.firstProperty">

J'espère que cela fonctionnera pour vous!

Millsionaire
la source
10

Je suis tombé sur le même problème lorsqu'il s'agissait d'une vue non triviale (il existe des étendues imbriquées). Et finalement découvert que c'est une chose connue et délicate lors du développement d'une application AngularJS en raison de la nature de l'héritage basé sur prototype de java-script. Les portées imbriquées AngularJS sont créées via ce mécanisme. Et la valeur créée à partir de ng-model est placée dans la portée des enfants, sans dire que la portée parent (peut-être celle injectée dans le contrôleur) ne verra pas la valeur, la valeur masquera également toute propriété avec le même nom défini dans la portée parent si elle n'utilise pas dot pour appliquer un accès de référence au prototype. Pour plus de détails, consultez la vidéo en ligne spécifique pour illustrer ce problème, http://egghead.io/video/angularjs-the-dot/ et les commentaires qui en découlent.

Roger Jin
la source
6

Jetez un oeil à ce violon http://jsfiddle.net/ganarajpr/MSjqL/

J'ai (je suppose!) Fait exactement ce que vous faisiez et cela semble fonctionner. Pouvez-vous vérifier ce qui ne fonctionne pas ici pour vous?

ganaraj
la source
Hmmmm .. merci de regarder ça, je suppose que ça devrait marcher ... pourquoi ça ne marche pas pour moi? Et ce qui est plus important: pourquoi cela fonctionne-t-il avec des objets et pas avec des variables de chaîne simples ?? Peut-être parce que je réfère mes contrôleurs de manière inappropriée dans routeProvider ?? J'essayais d'éviter les globaux et j'ai mis mes contrôleurs en tant que modulename.ctrlName dans le fichier controllers.js. Cela pourrait-il causer des maux de tête?
alchemication
Je ne sais pas vraiment pourquoi cela ne fonctionne pas pour vous. Si vous pouviez isoler ce problème dans un violon, je suppose que quelqu'un pourrait vous donner une meilleure réponse :)
ganaraj
D'accord, va faire, juste terminer le travail maintenant mais le fera à coup sûr demain ou plus tard dans la soirée, bravo! Il y a peut-être un problème avec la façon dont j'ai nommé mes ctrl, nous verrons ...
alchemication
6

Comme personne ne l'a mentionné, le problème peut être résolu en ajoutant $parentà la propriété liée

<div ng-controller="LoginController">
    <input type="text" name="login" class="form-control" ng-model="$parent.ssn" ng-pattern="/\d{6,8}-\d{4}|\d{10,12}/" ng-required="true" />
    <button class="button-big" type="submit" ng-click="BankLogin()" ng-disabled="!bankidForm.login.$valid">Logga in</button>
</div>

Et le contrôleur

app.controller("LoginController", ['$scope', function ($scope) {
    $scope.ssn = '';

    $scope.BankLogin = function () {
        console.log($scope.ssn); // works!
    };
}]);
Eric Herlitz
la source
merci pour la réponse, mais ça marche? il devrait idéalement travailler sur le même élément de portée et non sur le parent?
V31
5

Pour moi, le problème a été résolu en stockant mes données dans un objet (ici "données").

NgApp.controller('MyController', function($scope) {

   $scope.my_title = ""; // This don't work in ng-click function called

   $scope.datas = {
      'my_title' : "",
   };

   $scope.doAction = function() {
         console.log($scope.my_title); // bad value
         console.log($scope.datas.my_title); // Good Value binded by'ng-model'
   }
   

});

J'espère que ça va aider

Plici Stéphane
la source
3
Peu hors sujet ici, mais «données» est un terme pluriel pour «datum»
thethakuri
@thethakuri et "datum" en hongrois est "date", donc je ne l'utiliserais certainement pas non plus: P
EpicPandaForce
Je préfère la gaufre à l'IHOP
Nico Westerdale
2

Je viens d'avoir ce problème avec un root_controller lié à l'élément body. Ensuite, j'utilisais ng-view avec le routeur angulaire. Le problème est qu'angular crée TOUJOURS une nouvelle portée quand il insère le html dans l'élément ng-view. En conséquence, ma fonction "check" a été définie sur la portée parent de la portée qui a été modifiée par mon élément ng-model.

Pour résoudre le problème, utilisez simplement un contrôleur dédié dans le contenu html chargé par route.

moritz
la source
2

Vous pouvez le faire pour activer la recherche en ng-keypressentrée pour le texte d'entrée et en ng-clickpour l'icône:

<input type="text" ng-model="searchText" ng-keypress="keyEnter(this,$event)" />
<button ng-click="check(searchText)">Check!</button>

in the controller
$scope.search = function (searchText) {
        console.log(searchText);
    }
    $scope.keyEnter = function (serachText,$event) {
        var keyCode = $event.which || $event.keyCode;
        if (keyCode === 13) {//KeyCode for Enter key
           console.log(searchText);
        }
    }
Naoufal Gaffa
la source
2

J'ai eu le même problème.
La bonne façon serait de définir le «searchText» pour être une propriété à l'intérieur d'un objet.

Mais si je veux le laisser tel quel, une chaîne? eh bien, j'ai essayé toutes les méthodes mentionnées ici, rien n'a fonctionné.
Mais ensuite, j'ai remarqué que le problème ne concerne que l'initiation, donc je viens de définir l'attribut value et cela a fonctionné.

<input type="text" ng-model="searchText" value={{searchText}} />

De cette façon, la valeur est simplement définie sur la valeur '$ scope.searchText' et elle est mise à jour lorsque la valeur d'entrée change.

Je sais que c'est une solution de contournement, mais cela a fonctionné pour moi ..

Randonnée Nalbandyan
la source
2

J'étais confronté au même problème ... La résolution qui a fonctionné pour moi est d'utiliser ce mot-clé ..........

alerte (this.ModelName);

SnktJavaMaster
la source