Ng-model no actualiza el valor del controlador

270

Probablemente una pregunta tonta, pero tengo mi formulario html con entrada y botón simples:

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

Luego, en el controlador (la plantilla y el controlador se llaman desde routeProvider):

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

¿Por qué veo la vista actualizada correctamente pero no definida en la consola al hacer clic en el botón?

¡Gracias!

Actualización: Parece que realmente he resuelto ese problema (antes tenía que encontrar algunas soluciones) con: Solo tuve que cambiar el nombre de mi propiedad de searchTexta search.text, luego definir un $scope.search = {};objeto vacío en el controlador y listo ... No tengo idea de por qué está funcionando aunque ;]

alquimia
fuente
¿estás seguro de que estás usando este controlador en esta parte del documento? ¿Puedes publicar un ejemplo de falla mínima?
wroniasty
1
Sí, 100% seguro de que el controlador está bien, ese problema me parece familiar ... Sorprendentemente, funciona cuando cambio el nombre de la propiedad searchTexta search.text, ¿alguna idea de por qué?
alquimia
44
@Arthur: No es obvio, pero ng-model solo crea una especie de variable local de voz en su vista, por lo tanto, si desea mantenerla de esta manera, deberá pasarla a la función check (), como: check ( searchText) y su controlador lo reconocerá entonces. Espero que ayude
alquimia
3
Para el registro, se escribe así voila, no vuala, wolla, etc.
Dan Bechard
3
Creo que la respuesta que está buscando está en stackoverflow.com/a/14049482/1217913
Willa

Respuestas:

71

Controlador como versión (recomendado)

Aquí la plantilla

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

El JS

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

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

Lo mejor será usar un componente con Angular 2.xo Angular 1.5 o superior

########

Viejo camino (NO recomendado)

Esto NO se recomienda porque una cadena es primitiva, se recomienda usar un objeto

Prueba esto en tu marcado

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

y esto en tu controlador

$scope.check = function (searchText) {
    console.log(searchText);
}
Damax
fuente
17
Esto solo funciona en un sentido ... ¿y si desea cambiar el valor de searchText?
cdmckay
199
Esto tampoco responde al "¿por qué?" pregunta.
cdmckay
44
¿Qué pasa si no quiero usar ningún botón? Necesito enviar al entrar por ejemplo
danikoren
2
Saniko => Para enviar al ingresar, debe usar la etiqueta del formulario y ng-submit en él ( docs.angularjs.org/api/ng.directive:ngSubmit )
Damax
1
@ HP's411 es porque una cadena es primitiva. Cuando Angular asigna el valor, cambia el puntero al valor, por lo que el controlador mira el valor anterior porque tiene el puntero anterior al valor.
Damax
620

"Si usa ng-model, debe tener un punto allí".
Haga que su modelo apunte a un objeto.propiedad y estará listo para comenzar.

Controlador

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

Modelo

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

Esto sucede cuando los ámbitos secundarios están en juego, como rutas secundarias o ng-repeats. El ámbito secundario crea su propio valor y un conflicto de nombres nace como se ilustra aquí :

Vea este video clip para más información: https://www.youtube.com/watch?v=SBwoFkRjZvE&t=3m15s

Will Stern
fuente
94
Esta es la verdadera respuesta.
keslert
16
@Catfish Con la herencia prototípica, cada vez que escribe en la propiedad secundaria, la referencia / conexión a la propiedad primaria deja de existir. La forma en que Angular Scopes modela, si no tiene un punto, se crea una nueva propiedad dentro de su ámbito secundario. Este video lo explica con más detalle: youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m
Will Stern
1
Gracias jefe. ¡Esto es verdad! Sin embargo, no parece ser un capricho constante ya que encuentro el problema solo cuando lo uso con el marco iónico
Don Barry
66
@ user3677331 Funciona bien sin un punto hasta que tenga un ámbito secundario que intente hablar con él (como un elemento dentro de una repetición ng, por ejemplo). Supongamos que el nombre de su modelo era "teléfono". El ámbito secundario crea "teléfono", y luego obtiene un conflicto de ámbito porque el ámbito secundario tiene una variable "teléfono" y, por lo tanto, no puede acceder a "teléfono" en el ámbito primario. Mientras que si el ámbito secundario crea user.phone, se agregará al objeto de usuario principal para que ambos ámbitos apunten al mismo objeto
Se Stern
3
Muy útil. No estaba seguro de encontrar una respuesta a una pregunta relativamente vaga, pero esto estaba en lo cierto.
CodeManiak
57

En Mastering Web Application Development with AngularJS book p.19, está escrito que

Evite los enlaces directos a las propiedades del alcance. El enlace de datos bidireccional a las propiedades del objeto (expuesto en un ámbito) es un enfoque preferido. Como regla general, debe tener un punto en una expresión proporcionada a la directiva ng-model (por ejemplo, ng-model = "thing.name").

Los ámbitos son solo objetos JavaScript e imitan la jerarquía dom. Según la herencia de prototipos de JavaScript , las propiedades de los ámbitos se separan a través de los ámbitos. Para evitar esto, se debe usar la notación de puntos para vincular ng-models.

efirat
fuente
2
Gracias por esta referencia. Considero que este es un punto interesante relacionado con ng-model.
Reyes
44

Utilizando en thislugar de $scopeobras.

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>

Editar: en el momento de escribir esta respuesta, tenía una situación mucho más complicada que esta. Después de los comentarios, intenté reproducirlo para entender por qué funciona, pero no tuve suerte. Creo que de alguna manera (no sé por qué) se genera un nuevo ámbito secundario y se thisrefiere a ese ámbito. Pero si $scopese usa, en realidad se refiere al $ alcance primario debido a la función de alcance léxico de JavaScript .

Sería genial si alguien que tiene este problema lo prueba de esta manera y nos informa.

Feyyaz
fuente
Perfecto, eres un genio
fjcero
1
Parece que las funciones adjuntas $scopecrean un nuevo ámbito (es decir, en función check(), se thisrefiere a un ámbito que es un ámbito secundario $scope). ¿Por que es esto entonces? ¿Puedes dar algunas explicaciones?
Xun Yang
1
¿Por qué debería usar 'this' en lugar de '$ scope' en este caso? porque funcionó usando $ scope en mi proyecto anterior, pero esta vez no funciona usando $ scope. Pero 'esto' ha funcionado. ¿Porqué es eso?
Rashedul
esto funciona, pero si dices this.searchText = "something else"o incluso si $scope.searchText = "something else"no actualiza la vista. ¿Puedes explicar este problema?
Shri
debería. Lo probé usando el código anterior, actualizó la vista.
Feyyaz
13

Tuve el mismo problema y fue debido a que no declaró el objeto en blanco primero en la parte superior de mi controlador:

$scope.model = {}

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

¡Espero que esto funcione para ti!

Millonario
fuente
10

Me encontré con el mismo problema al tratar con una vista no trivial (hay ámbitos anidados). Y finalmente descubrí que esto es algo complicado conocido al desarrollar la aplicación AngularJS debido a la naturaleza de la herencia basada en prototipos de java-script. Los ámbitos anidados de AngularJS se crean a través de este mecanismo. Y el valor creado a partir de ng-model se coloca en el ámbito secundario, sin decir que el ámbito primario (tal vez el inyectado en el controlador) no verá el valor, el valor también sombreará cualquier propiedad con el mismo nombre definido en el ámbito primario si no usa punto para imponer un prototipo de acceso de referencia. Para obtener más detalles, consulte el video en línea específico para ilustrar este problema, http://egghead.io/video/angularjs-the-dot/ y los comentarios que lo siguen.

Roger Jin
fuente
6

Echa un vistazo a este violín http://jsfiddle.net/ganarajpr/MSjqL/

He (¡supongo!) Hice exactamente lo que estabas haciendo y parece estar funcionando. ¿Puedes comprobar lo que no funciona aquí para ti?

ganaraj
fuente
Hmmmm ... gracias por investigar esto, supongo que debería funcionar ... ¿por qué no funciona para mí? Y lo que es más importante: ¿por qué funciona con objetos y no con variables de cadena simple? ¿Quizás porque remito mis controladores de manera inadecuada en routeProvider? Estaba tratando de evitar los globales y puse mis controladores como modulename.ctrlName en el archivo controllers.js. ¿Podría eso causar dolor de cabeza?
alquimia
No estoy realmente seguro de por qué no funciona para ti. Si pudieras aislar este problema en un violín, creo que alguien podría darte una mejor respuesta :)
ganaraj
Bien, lo haré, solo terminaré el trabajo ahora, pero lo haré seguro mañana o más tarde en la noche, ¡salud! Puede haber un problema con la forma en que he espaciado los nombres de mis ctrl, veremos ...
alquimia
6

Como nadie mencionó esto, el problema se puede resolver agregando $parenta la propiedad vinculada

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

Y el controlador

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

    $scope.BankLogin = function () {
        console.log($scope.ssn); // works!
    };
}]);
Eric Herlitz
fuente
gracias por la respuesta, sin embargo, ¿funciona esto? idealmente debería estar trabajando en el mismo elemento de alcance correcto y no el padre?
V31
5

Para mí, el problema se resolvió almacenando mis datos en un objeto (aquí "datos").

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

});

Espero que ayude

Plici Stéphane
fuente
3
Poco fuera de tema aquí, pero 'datos' es un término plural para 'dato'
thethakuri
@thethakuri y "datum" en húngaro es "fecha", así que seguro que tampoco lo usaría: P
EpicPandaForce
Prefiero Waffle House a IHOP
Nico Westerdale
2

Acabo de tener este problema al usar un root_controller vinculado al elemento body. Luego estaba usando ng-view con el enrutador angular. El problema es que angular SIEMPRE crea un nuevo alcance cuando inserta el html en el elemento ng-view. Como consecuencia, mi función "verificar" se definió en el ámbito principal del ámbito modificado por mi elemento ng-model.

Para resolver el problema, solo use un controlador dedicado dentro del contenido html cargado de ruta.

Moritz
fuente
2

Puede hacer eso para habilitar la búsqueda en ng-keypressenter para ingresar texto y en ng-clickel icono:

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

Yo tuve el mismo problema.
La forma correcta sería configurar el 'searchText' para que sea una propiedad dentro de un objeto.

Pero, ¿y si quiero dejarlo como está, una cuerda? bueno, probé todos los métodos mencionados aquí, nada funcionó.
Pero luego noté que el problema está solo en la iniciación, así que acabo de establecer el atributo de valor y funcionó.

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

De esta manera, el valor se establece en el valor '$ scope.searchText' y se actualiza cuando cambia el valor de entrada.

Sé que es una solución, pero funcionó para mí ...

Caminata Nalbandyan
fuente
2

Estaba enfrentando el mismo problema ... La resolución que funcionó para mí es usar esta palabra clave ..........

alerta (this.ModelName);

SnktJavaMaster
fuente