Ng-model non aggiorna il valore del controller


270

Probabilmente domanda stupida, ma ho il mio modulo html con input e pulsante semplici:

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

Quindi nel controller (modello e controller vengono chiamati da routeProvider):

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

Perché vedo la vista aggiornata correttamente ma non definita nella console quando faccio clic sul pulsante?

Grazie!

Aggiornamento: sembra che io abbia effettivamente risolto quel problema (prima dovevo trovare alcune soluzioni alternative) con: Ho dovuto solo cambiare il nome della mia proprietà da searchTexta search.text, quindi definire un $scope.search = {};oggetto vuoto nel controller e voilà ... Non ho idea del perché funzioni anche se ;]


sei sicuro di utilizzare questo controller in questa parte del documento? puoi pubblicare un esempio minimo di fallimento?
wroniasty,

1
Sì, sicuro al 100% che il controller sia ok, quel problema mi sembra familiare ... Sorprendentemente funziona quando cambio il nome della proprietà da searchTexta search.text, qualche idea sul perché ??
alchemication,

4
@Arthur: Non è abbastanza ovvio, ma ng-model crea solo una sorta di speak variabile locale nella tua vista, quindi se vuoi mantenerlo in questo modo dovresti passarlo nella funzione check (), come: check ( searchText) e quindi il tuo controller lo riconoscerà. Spero che aiuti
alchemication l'

3
Per la cronaca, è scritto voila, non è vuala, wollae così via
Dan Bechard

3
Penso che la risposta che stai cercando sia su stackoverflow.com/a/14049482/1217913
Willa,

Risposte:


71

Controller come versione (consigliato)

Ecco il modello

<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>

Il JS

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

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

Il migliore sarà utilizzare il componente con Angular 2.xo Angular 1.5 o superiore

########

Vecchio modo (NON consigliato)

Questo NON è raccomandato perché una stringa è una primitiva, si consiglia vivamente di usare un oggetto

Prova questo nel tuo markup

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

e questo nel tuo controller

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

17
Funziona solo a senso unico ... cosa succede se si desidera modificare il valore di searchText?
cdmckay,

199
Anche questo non risponde al "perché?" domanda.
cdmckay,

4
cosa succede se non desidero utilizzare alcun pulsante? ho bisogno di presentare su enter per esempio
danikoren

2
Saniko => Per inviare su Invio, è necessario utilizzare il tag del modulo e ng-submit su di esso ( docs.angularjs.org/api/ng.directive:ngSubmit )
Damax

1
@ HP's411 è perché una stringa è una primitiva. Quando Angular assegna il valore, cambia il puntatore al valore, quindi il controller guarda il vecchio valore perché ha il vecchio puntatore sul valore.
Damax,

620

"Se usi ng-model, devi avere un punto lì dentro."
Fai in modo che il tuo modello punti a un oggetto.proprietà e sarai a posto.

controllore

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

Modello

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

Ciò accade quando sono in gioco ambiti secondari, ad esempio percorsi secondari o ripetizioni ng. L'ambito figlio crea il proprio valore e nasce un conflitto di nomi come illustrato qui : guarda

questo video per ulteriori informazioni: https://www.youtube.com/watch?v=SBwoFkRjZvE&t=3m15s


94
Questa è la vera risposta.
keslert,

16
@Catfish Con l'eredità prototipale, ogni volta che si scrive sulla proprietà figlio, il riferimento / connessione alla proprietà padre cessa di esistere. Il modo in cui vengono modellati gli ambiti angolari, se non si dispone di un punto, viene creata una nuova proprietà nell'ambito del proprio ambito figlio. Questo video lo spiega in modo più dettagliato: youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m
Will Stern

1
Gracias Boss. Questo è vero! Tuttavia, non sembra essere una stranezza coerente poiché riscontro il problema solo quando si utilizza con il framework ionico
Don Barry,

6
@ user3677331 Funziona bene senza un punto fino a quando non si ha un ambito figlio che tenta di parlarci (come un elemento all'interno di una ripetizione ng, ad esempio). Supponiamo che il nome del modello sia "telefono" L'ambito figlio crea "telefono", quindi si verifica un conflitto di ambito poiché l'ambito figlio ha una variabile "telefono" e pertanto non può accedere a "telefono" nell'ambito padre. Considerando che se l'ambito figlio crea user.phone, verrà aggiunto all'oggetto utente del genitore in modo che entrambi gli ambiti puntino allo stesso oggetto
Will Stern

3
Molto utile. Non ero sicuro di trovare una risposta a una domanda relativamente vaga, ma questa era morta.
CodeManiak,

57

In Mastering Sviluppo di applicazioni Web con il libro AngularJS p.19, è scritto che

Evita i collegamenti diretti alle proprietà dell'ambito. L'approccio preferito è l'associazione di dati bidirezionali alle proprietà dell'oggetto (esposte su un ambito). Come regola empirica, dovresti avere un punto in un'espressione fornita alla direttiva ng-model (ad esempio, ng-model = "thing.name").

Gli ambiti sono solo oggetti JavaScript e imitano la gerarchia dom. Secondo l' ereditarietà del prototipo JavaScript , le proprietà degli ambiti sono separate attraverso gli ambiti. Per evitare ciò, la notazione punto dovrebbe usare per legare i modelli ng.


2
grazie per questo riferimento. Considero questo un punto interessante legato al modello ng.
Re

44

Utilizzando al thisposto di $scopeopere.

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>

Modificare: Al momento in cui scrivevo questa risposta, avevo una situazione molto più complicata di questa. Dopo i commenti, ho provato a riprodurlo per capire perché funziona, ma senza fortuna. Penso che in qualche modo (non so davvero perché) venga generato un nuovo ambito figlio e si thisriferisca a tale ambito. Ma se $scopeviene utilizzato, in realtà si riferisce all'ambito $ parent a causa dell'ambito lessicale di javascript funzionalità .

Sarebbe bello se qualcuno avesse questo problema test in questo modo e informarci.


Impeccabile, sei un genio
fjcero

1
Sembra che le funzioni associate $scopecreino un nuovo ambito (cioè in funzione check(), si thisriferisce a un ambito che è un ambito figlio di $scope). Perché è così? Puoi darci qualche spiegazione?
Xun Yang,

1
perché dovrei usare 'this' invece di '$ scope' in questo caso? perché ha funzionato usando $ scope nel mio progetto precedente ma questa volta la stessa cosa non funziona usando $ scope. Ma "questo" ha funzionato. Perché?
Rashedul

funziona, ma se lo dici this.searchText = "something else"o anche se $scope.searchText = "something else", non aggiorna la vista. puoi spiegare questo problema?
Shri

dovrebbe. L'ho provato usando il codice sopra, ha aggiornato la vista.
Feyyaz,

13

Ho avuto lo stesso problema ed è stato dovuto al fatto che non ho dichiarato l'oggetto vuoto prima nella parte superiore del controller:

$scope.model = {}

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

Spero che questo funzioni per te!


10

Mi sono imbattuto nello stesso problema quando ho a che fare con una vista non banale (ci sono ambiti nidificati). E finalmente ho scoperto che questa è una cosa nota quando si sviluppa un'applicazione AngularJS a causa della natura dell'eredità basata su prototipo di java-script. Gli ambiti nidificati AngularJS vengono creati tramite questo meccanismo. E il valore creato da ng-model viene inserito nell'ambito figlio, senza dire che l'ambito padre (forse quello iniettato nel controller) non vedrà il valore, il valore ombreggerà anche qualsiasi proprietà con lo stesso nome definito nell'ambito padre se non usi il punto per imporre un accesso di riferimento al prototipo. Per maggiori dettagli, controlla il video online specifico per illustrare questo problema, http://egghead.io/video/angularjs-the-dot/ e i commenti che seguono.


6

Dai un'occhiata a questo violino http://jsfiddle.net/ganarajpr/MSjqL/

Ho (suppongo!) Fatto esattamente quello che stavi facendo e sembra funzionare. Puoi controllare cosa non funziona qui per te?


Hmmmm .. grazie per aver esaminato questo, presumo che dovrebbe funzionare ... perché non funziona per me? E cosa è più importante: perché funziona con gli oggetti e non con semplici variabili stringa ?? Forse perché rimando i miei controller in modo inappropriato in routeProvider ?? Stavo cercando di evitare i globali e ho messo i miei controller come modulename.ctrlName nel file controllers.js. Ciò potrebbe causare mal di testa?
alchemication,

Non sono davvero sicuro del perché non funzioni per te. Se potessi isolare questo problema in un violino, immagino che qualcuno sarebbe in grado di darti una risposta migliore :)
Ganaraj,

Okay, finirò il lavoro adesso, ma lo farò sicuramente domani o più tardi la sera, evviva! Potrebbe esserci un problema con il modo in cui ho assegnato il nome al mio Ctrl, vedremo ...
alchemication

6

Poiché nessuno lo ha menzionato, il problema può essere risolto aggiungendo $parentalla proprietà associata

<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>

E il controller

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

    $scope.BankLogin = function () {
        console.log($scope.ssn); // works!
    };
}]);

grazie per la risposta, comunque y funziona? dovrebbe funzionare idealmente sullo stesso elemento scope giusto e non sul genitore?
V31,

5

Per me il problema è stato risolto immagazzinando i miei dati in un oggetto (qui "dati").

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'
   }
   

});

Spero che possa aiutare


3
Poco fuori tema qui, ma "dati" è un termine plurale per "datum"
thethakuri,

@thethakuri e "datum" in ungherese è "data", quindi sicuramente non lo
userei

Preferisco waffle house a IHOP
Nico Westerdale

2

Ho appena avuto questo problema usando un root_controller associato all'elemento body. Quindi stavo usando ng-view con il router angolare. Il problema è che l'angolazione crea SEMPRE un nuovo ambito quando inserisce l'html nell'elemento ng-view. Di conseguenza, la mia funzione "check" è stata definita sull'ambito padre dell'ambito che è stato modificato dal mio elemento ng-model.

Per risolvere il problema, basta usare un controller dedicato all'interno del contenuto html caricato in route.


2

Puoi farlo per abilitare la ricerca in ng-keypressimmettere per il testo di input e in ng-clickper l'icona:

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

2

Ho avuto lo stesso problema.
Il modo corretto sarebbe impostare "searchText" come proprietà all'interno di un oggetto.

E se volessi lasciarlo così com'è, una stringa? bene, ho provato ogni singolo metodo menzionato qui, niente ha funzionato.
Ma poi ho notato che il problema è solo nell'iniziazione, quindi ho appena impostato l'attributo value e ha funzionato.

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

In questo modo il valore è appena impostato su "$ scope.searchText" e viene aggiornato quando cambia il valore di input.

So che è una soluzione alternativa, ma ha funzionato per me ..


2

Stavo affrontando lo stesso problema ... La risoluzione che ha funzionato per me è usare questa parola chiave ..........

alert (this.ModelName);

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.