AngularJS: detecta automaticamente a mudança no modelo

103

Suponha que eu queira fazer algo como executar algum código automaticamente (como salvar dados em um servidor) sempre que os valores de um modelo mudam. A única maneira de fazer isso é definir algo como ng-changeem cada controle que poderia alterar o modelo?

Ou seja, com as visualizações, as coisas mudam conforme o modelo é alterado, sem a necessidade de conectar nada explicitamente. Existe uma analogia com a capacidade de executar um código que salva em um servidor? Algo como

myModel.on('change', function() {
  $.post("/my-url", ...);
});

como você pode ver com algo como backbone.

Alec
fonte

Respostas:

151

Em visualizações com {{}}e / ou modelo ng, o Angular está configurando $watch()para você nos bastidores.

Por padrão, $watchcompara por referência. Se você definir o terceiro parâmetro como $watchpara true, o Angular, em vez disso, observará "superficialmente" as alterações do objeto. Para arrays, isso significa comparar os itens do array, para mapas de objetos, isso significa observar as propriedades. Então, isso deve fazer o que você quiser:

$scope.$watch('myModel', function() { ... }, true);

Atualização : Angular v1.2 adicionou um novo método para isso, `$ watchCollection () :

$scope.$watchCollection('myModel', function() { ... });

Observe que a palavra "superficial" é usada para descrever a comparação em vez de "profundo" porque as referências não são seguidas - por exemplo, se o objeto observado contém um valor de propriedade que é uma referência a outro objeto, essa referência não é seguida para comparar o outro objeto.

Mark Rajcok
fonte
1
Ah, ótimo! Existe alguma razão para que isso não pareça ser tão documentado (ou seja, eu não acho que nenhum dos tutoriais no site angular mencionou configurar $ relógios diretamente)? Há algo de ruim nisso que tornaria a configuração de ng-changeganchos (potencialmente múltiplos) em controles de entrada uma ideia melhor?
Alec
12
Sim, seria bom se o tutorial principal mencionasse $ watch em algum lugar. O que é "ruim" nessa abordagem é que pode ser demorado se o seu modelo for grande (cada ciclo de resumo - cada pressionamento de tecla em um campo de entrada - resultará em uma verificação suja profunda deste modelo, possivelmente várias vezes) . Nesse caso, $ watch () es seletivos ou mudança ng seletiva seriam melhores.
Mark Rajcok
8

E se você precisar estilizar seus elementos de formulário de acordo com seu estado (modificado / não modificado) dinamicamente ou para testar se alguns valores realmente mudaram, você pode usar o seguinte módulo, desenvolvido por mim: https://github.com/betsol / angular-input-modificado

Ele adiciona propriedades e métodos adicionais ao formulário e aos seus elementos filhos. Com ele, você pode testar se algum elemento contém novos dados ou até mesmo testar se todo o formulário possui novos dados não salvos.

Você pode configurar o seguinte relógio: $scope.$watch('myForm.modified', handler)e seu manipulador será chamado se alguns elementos do formulário realmente contiverem novos dados ou se forem revertidos para o estado inicial.

Além disso, você pode usar a modifiedpropriedade de elementos de formulário individuais para realmente reduzir a quantidade de dados enviados a um servidor por meio de chamada AJAX. Não há necessidade de enviar dados inalterados.

Como um bônus, você pode reverter seu formulário ao estado inicial por meio da chamada ao reset()método do formulário .

Você pode encontrar a demonstração do módulo aqui: http://plnkr.co/edit/g2MDXv81OOBuGo6ORvdt?p=preview

Felicidades!

Slava Fomin II
fonte
Existe uma maneira de verificar isso no controlador. por exemplo, se clicar no botão x, posso fazer um pop-up de confirmação if (myform.modified)?
Flash de
Claro, basta passar FormController para a função do seu controlador: <form name="myForm">, <button ng-click="vm.doSomething(myForm)">.
Slava Fomin II
obrigado isso vai fazer algo apenas se o formulário foi modificado certo?
Flash
Isso passará FormControllerpara a doSomething()função do seu controlador. Você pode fazer o que quiser com ele dentro dessa função, por exemplo, verificar se o formulário foi realmente modificado verificando a FormController.modifiedpropriedade booleana.
Slava Fomin II
Obrigado! Excelente recurso
Flash