O modelo Ng não atualiza o valor do controlador

270

Provavelmente pergunta boba, mas eu tenho meu formulário html com entrada e botão simples:

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

Em seguida, no controlador (modelo e controlador são chamados de routeProvider):

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

Por que vejo a visualização atualizada corretamente, mas indefinida no console ao clicar no botão?

Obrigado!

Update: Parece que eu realmente resolvi esse problema (antes tive que apresentar algumas soluções alternativas) com: Só tive que alterar o nome da minha propriedade de searchTextpara search.text, em seguida, definir o $scope.search = {};objeto vazio no controlador e pronto ... Não tenho idéia do por que está funcionando Apesar ;]

alquimização
fonte
você tem certeza de que está usando este controlador nesta parte do documento? você pode postar um exemplo mínimo de falha?
wroniasty
1
Sim, 100% de certeza de que o controlador está ok, esse problema parece familiar para mim ... Surpreendentemente, ele funciona quando altero o nome da propriedade de searchTextpara search.text, alguma idéia do porquê?
alchemication
4
@ Arthur: Não é óbvio, mas o ng-model cria apenas uma espécie de variável local speak na sua opinião; portanto, se você quiser mantê-la dessa maneira, precisará passar para a função check (), como: check ( searchText) e seu controlador o reconhecerá então. Espero que ajude
alquimização
3
Para o registro, soletrou voila, não vuala, wollaetc.
Dan Bechard
3
Eu acho que a resposta que você está procurando é em stackoverflow.com/a/14049482/1217913
Willa

Respostas:

71

Controlador como versão (recomendado)

Aqui o modelo

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

O JS

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

Um exemplo: http://codepen.io/Damax/pen/rjawoO

O melhor será usar o componente com Angular 2.x ou Angular 1.5 ou superior

########

Caminho antigo (NÃO recomendado)

Isso NÃO é recomendado porque uma sequência é primitiva, altamente recomendável usar um objeto

Experimente isso na sua marcação

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

e isso no seu controlador

$scope.check = function (searchText) {
    console.log(searchText);
}
Damax
fonte
17
Isso funciona apenas de mão única ... e se você quiser alterar o valor de searchText?
Cdmckay
199
Isso também não responde ao "por quê?" questão.
Cdmckay
4
e se eu não quiser usar nenhum botão? i precisa enviar em entrar por exemplo
danikoren
2
Saniko => Para enviar no enter, você deve usar a tag do formulário e o ng-submit nele ( docs.angularjs.org/api/ng.directive:ngSubmit )
Damax
1
@ HP's411 é porque uma string é primitiva. Quando Angular atribui o valor, ele muda o ponteiro para o valor, para que o controlador analise o valor antigo porque ele possui o ponteiro antigo para o valor.
Damax
620

"Se você usa o ng-model, tem que ter um ponto lá."
Faça o seu modelo apontar para uma propriedade object.property e você estará pronto.

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>

Isso acontece quando os escopos filho estão em jogo - como rotas filho ou repetições de ng. O escopo filho cria seu próprio valor e um conflito de nome nasce como ilustrado aqui :

Veja este videoclipe para mais: https://www.youtube.com/watch?v=SBwoFkRjZvE&t=3m15s

Will Stern
fonte
94
Esta é a verdadeira resposta.
precisa saber é o seguinte
16
@ Catfish Com herança prototípica, sempre que você escreve na propriedade filho, a referência / conexão com a propriedade pai deixa de existir. Da maneira como os Ângulos Angulares são modelados, se você não possui um ponto, é criada uma nova propriedade no escopo filho. Este vídeo explica com mais detalhes: youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m
Will Stern
1
Gracias Boss. Isso é verdade! No entanto, não parece ser uma peculiaridade consistente, pois só encontro o problema ao usá-lo com o framework Ionic
Don Barry
6
@ user3677331 Funciona bem sem um ponto até você ter um escopo filho que tenta falar com ele (como um item dentro de um ng-repeat, por exemplo). Diga que o nome do seu modelo era "telefone" Seu escopo filho cria "telefone" e, em seguida, você entra em conflito com o escopo porque o escopo filho tem uma variável "telefone" e, portanto, não pode acessar "telefone" no escopo pai. Enquanto que se o escopo filho cria user.phone, ele será adicionado ao objeto de usuário do pai para que ambos os escopos estão apontando para o mesmo objeto
Will Stern
3
Muito útil. Eu não tinha certeza se encontraria uma resposta para uma pergunta relativamente vaga, mas isso estava morto.
CodeManiak
57

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

Evite ligações diretas às propriedades do escopo. A ligação de dados bidirecional às propriedades do objeto (exposta em um escopo) é uma abordagem preferida. Como regra geral, você deve ter um ponto em uma expressão fornecida à diretiva ng-model (por exemplo, ng-model = "thing.name").

Os escopos são apenas objetos JavaScript e imitam a hierarquia de dom. De acordo com a herança do protótipo JavaScript , as propriedades dos escopos são separadas por escopos. Para evitar isso, a notação de ponto deve ser usada para vincular modelos ng.

efirat
fonte
2
obrigado por esta referência. Considero isso um ponto interessante relacionado ao ng-model.
Kings
44

Usando em thisvez 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>

Edit: No momento em que escrevi esta resposta, eu tinha uma situação muito mais complicada do que isso. Após os comentários, tentei reproduzi-lo para entender por que funciona, mas sem sorte. Eu acho que de alguma forma (realmente não sei por quê) um novo escopo filho é gerado e thisse refere a esse escopo. Mas se $scopefor usado, na verdade, ele se refere ao escopo pai $ por causa do javascript recurso de escopo lexical .

Seria ótimo se alguém com esse problema testar dessa maneira e nos informar.

Feyyaz
fonte
Impecável, você é um gênio
fjcero
1
Parece que as funções anexadas $scopecriam um novo escopo (ou seja, na função check(), thisrefere-se a um escopo que é um escopo filho $scope). Por que é tão? Você pode dar algumas explicações?
Xun Yang
1
por que devo usar 'this' em vez de '$ scope' nesse caso? porque funcionou usando $ scope no meu projeto anterior, mas desta vez a mesma coisa não funciona usando $ scope. Mas 'isso' funcionou. Por que é que?
precisa saber é o seguinte
isso funciona, mas se você disser this.searchText = "something else"ou mesmo se $scope.searchText = "something else", não atualizará a exibição. você pode explicar esse problema?
Shri
deveria. Eu tentei usando o código acima, atualizou a exibição.
Feyyaz 01/04
13

Eu tive o mesmo problema e foi devido a mim não declarar o objeto em branco primeiro na parte superior do meu controlador:

$scope.model = {}

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

Espero que isso funcione para você!

Milionário
fonte
10

Me deparei com o mesmo problema ao lidar com uma visão não trivial (há escopos aninhados). E finalmente descobri que isso é uma coisa complicada conhecida ao desenvolver o aplicativo AngularJS devido à natureza da herança baseada em protótipo do java-script. Os escopos aninhados do AngularJS são criados por esse mecanismo. E o valor criado a partir do ng-model é colocado no escopo filho, sem dizer que o escopo pai (talvez aquele injetado no controlador) não verá o valor; o valor também sombreará qualquer propriedade com o mesmo nome definido no escopo pai, se não usar ponto para impor um acesso de referência de protótipo. Para obter mais detalhes, consulte o vídeo on-line específico para ilustrar esse problema, http://egghead.io/video/angularjs-the-dot/ e os comentários a seguir.

Roger Jin
fonte
6

Dê uma olhada neste violino http://jsfiddle.net/ganarajpr/MSjqL/

Eu (presumo!) Fiz exatamente o que você estava fazendo e parece estar funcionando. Você pode verificar o que não está funcionando aqui para você?

ganaraj
fonte
Hmmmm .. obrigado por analisar isso, eu presumo que deveria funcionar ... por que não está funcionando para mim? E o que é mais importante: por que funciona com objetos e não com variáveis ​​de string simples? Talvez porque eu indique meus controladores de maneira inadequada no routeProvider? Estava tentando evitar globais e coloquei meus controladores como modulename.ctrlName no arquivo controllers.js. Isso poderia causar dor de cabeça?
alchemication
Não sei ao certo por que isso não funciona para você. Se você pudesse isolar esta questão em um violino, eu acho que alguém seria capaz de lhe dar uma resposta melhor :)
ganaraj
Ok, vou fazer, acabando de trabalhar agora, mas com certeza amanhã ou mais tarde à noite, vivas! Pode haver um problema com a maneira que eu namespaced do meu ctrl, vamos ver ...
alchemication
6

Como ninguém mencionou isso, o problema pode ser resolvido adicionando $parentà propriedade bound

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

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

    $scope.BankLogin = function () {
        console.log($scope.ssn); // works!
    };
}]);
Eric Herlitz
fonte
obrigado pela resposta, no entanto, y está funcionando? idealmente, ele deveria estar trabalhando no mesmo elemento de escopo certo e não no pai?
V31
5

Para mim, o problema foi resolvido armazenando meus dados em um objeto (aqui "dados").

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 ajude

Plici Stéphane
fonte
3
Pouco tópico aqui, mas 'data' é um termo plural para 'datum'
thethakuri 19/05/16
@thethakuri e "datum" em húngaro são "date", então eu também não usaria isso: P
EpicPandaForce
Prefiro waffle house ao IHOP
Nico Westerdale
2

Eu apenas tive esse problema usando um root_controller vinculado ao elemento do corpo. Então eu estava usando o ng-view com o roteador angular. O problema é que o angular SEMPRE cria um novo escopo quando ele insere o html no elemento ng-view. Como conseqüência, minha função "check" foi definida no escopo pai do escopo modificado pelo elemento ng-model.

Para resolver o problema, basta usar um controlador dedicado no conteúdo html carregado pela rota.

Moritz
fonte
2

Você pode fazer isso para ativar a pesquisa em ng-keypressenter para inserir texto e ng-clickpara o ícone:

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

Eu tive o mesmo problema.
A maneira correta seria definir o 'searchText' como uma propriedade dentro de um objeto.

Mas e se eu quiser deixar como está, uma corda? bem, eu tentei todos os métodos mencionados aqui, nada funcionou.
Mas então notei que o problema está apenas na iniciação, então acabei de definir o atributo value e funcionou.

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

Dessa forma, o valor é definido apenas como '$ scope.searchText' e está sendo atualizado quando o valor de entrada é alterado.

Eu sei que é uma solução alternativa, mas funcionou para mim ..

Hike Nalbandyan
fonte
2

Eu estava enfrentando o mesmo problema ... A resolução que funcionou para mim é usar esta palavra-chave ..........

alerta (this.ModelName);

SnktJavaMaster
fonte