AngularJS: ligação do modelo ng não é atualizada quando alterada com jQuery


98

Este é o meu HTML:

<input id="selectedDueDate" type="text" ng-model="selectedDate" />

Quando digito na caixa, o modelo é atualizado por meio do mecanismo de ligação bidirecional. Doce.

No entanto, quando faço isso via JQuery ...

$('#selectedDueDate').val(dateText);

Não atualiza o modelo. Por quê?


1
Por que você faria o segundo para começar? Você está usando uma estrutura e decide contorná-la e definir o valor por meio da manipulação do DOM. O melhor conselho que se pode dar com o Angular para iniciantes: esqueça que o jquery existe. em 90% dos casos, angular será suficiente e os 10% restantes podem ser obtidos por meio do jqlite dentro de um link de diretiva (o elemento é na verdade um elemento empacotado do jqlite pronto para ser manipulado).
— Anulado em

1
pergunta muito importante
— Syed Md. Kamruzzaman

há boas razões pelas quais você pode querer mudar os modelos angulares com manipulação de dom, talvez você seja um desenvolvedor trabalhando com uma ferramenta de teste A / B e queira preencher formulários nos bastidores, ou você esteja trabalhando em um script greasemonkey para preencher automaticamente os formulários.
— Shadaez

Respostas:


134

Angular não sabe dessa mudança. Para isso, você deve ligar $scope.$digest()ou fazer a alteração dentro de $scope.$apply():

$scope.$apply(function() { 
   // every changes goes here
   $('#selectedDueDate').val(dateText); 
});

Veja isto para entender melhor a verificação suja

ATUALIZAÇÃO : Aqui está um exemplo


Fiz isso como você diz: fiddle.jshell.net/AladdinMhaimeed/agvTz/8, mas não funciona
— Aladdin Mhemed

1
Veja este fiddle.jshell.net/agvTz/38 Você deve chamar o function$ scope. $ Apply passando uma função como argumento. E $ apply deve ser chamado quando você fizer as alterações em $ scope, portanto, dentro de onSelect. Ah, espero que você coloque a manipulação do DOM dentro do controlador apenas para o exemplo, em um aplicativo real isso está errado;)
— Renan Tomal Fernandes 16/10/12

então o que devo fazer para ter uma boa prática angular? separar a manipulação de DOM em uma diretiva?
— Aladdin Mhemed,

2
Sim, aqui um exemplo fiddle.jshell.net/agvTz/39 a diretiva pode ser usada quantas vezes você quiser com um simples datepicker="scopeKeyThatYouWant"na entrada
— Renan Tomal Fernandes

Basta chamar .. $ scope. $ Digest () para estabelecer. vai desacelerar?
— Thant Zin

29

Apenas use;

$('#selectedDueDate').val(dateText).trigger('input');

Eu usei trigger('input')com um selecionador de data em seu onSelectevento. Ele atualiza o valor corretamente.
— iman

Isso funcionou para mim. Eu estava atualizando o valor de uma entrada associada de um teste de diretiva e envolver a .val('something'chamada em um $apply(ou chamar $digestdepois) não funcionou.
— Tom Seldon

2
Depois de horas de procura, esta foi a que funcionou! Obrigado!
— Dan,

Alhamdulillah, perfeito, está funcionando para mim. obrigado por economizar minhas horas
— Syed Md. Kamruzzaman

Verdade. Eu estava usando o $('#selectedDueDate').val(dateText).trigger('change');que não estava funcionando para mim. .trigger('input');funciona como mágica
— jafarbtech

17

Descobri que, se você não colocar a variável diretamente no escopo, ela será atualizada de forma mais confiável.

Tente usar algum "dateObj.selectedDate" e no controlador adicione a selectedDate a um objeto dateObj da seguinte forma:

$scope.dateObj = {selectedDate: new Date()}

Isso funcionou para mim.


4
Isso funcionou para mim também. Eu perdi mais de 2 horas tentando descobrir por que a ligação bidirecional não estava funcionando. Funcionou bem na página, mas o escopo no controlador não estava sendo atualizado. Então tentei sua abordagem por desespero (porque não fazia sentido para mim que isso fosse verdade) e, caramba, funcionou! Obrigado!
— TMc

3
O mesmo aqui - algum guru do angularJs pode explicar por que isso funciona? É como se a visão tivesse seu próprio escopo aninhado e às vezes você fica preso atualizando apenas o escopo da visão, não o escopo do controlador
— Tom Carver

1
Funciona bem para mim! Eu me pergunto por que diabos não funciona da mesma forma no escopo simples.
— Stephen de

1
Tem pouco a ver com angular e muito a ver com o funcionamento do javascript. Quando você atribui a variável de escopo a um objeto, você a atribui por referência, em oposição a por valor, como feito quando uma var é definida como igual a um valor primitivo. Falei sobre isso no meu post aqui. stackoverflow.com/questions/14527377/… . Eu referenciei este golpe que fiz naquele post para ilustrar plnkr.co/edit/WkCT6aYao3qCmzJ8t5xg?p=preview .
— Nick Brady,

4

Tente isto

var selectedDueDateField = document.getElementById("selectedDueDate");
var element = angular.element(selectedDueDateField);
element.val('new value here');
element.triggerHandler('input');

É utilizável quando você não tem acesso ao $ escopo do controlador do angular. Quando você escreve um script com uma Tampermonkey, por exemplo.
— wassertim

3

Basta executar a seguinte linha no final de sua função:

$ scope. $ apply ()


2

Aconteça o que acontecer fora do escopo do Angular, o Angular nunca saberá disso.

O ciclo Digest coloca as alterações do modelo -> controlador e, em seguida, do controlador -> modelo.

Se você precisa ver o modelo mais recente, você precisa acionar o ciclo de resumo

Mas existe uma chance de um ciclo de resumo em andamento, então precisamos verificar e inicializar o ciclo.

De preferência, sempre execute uma aplicação segura.

       $scope.safeApply = function(fn) {
            if (this.$root) {
                var phase = this.$root.$$phase;
                if (phase == '$apply' || phase == '$digest') {
                    if (fn && (typeof (fn) === 'function')) {
                        fn();
                    }
                } else {
                    this.$apply(fn);
                }
            }
        };


      $scope.safeApply(function(){
          // your function here.
      });

1

AngularJS passa string, números e booleanos por valor enquanto passa arrays e objetos por referência. Assim, você pode criar um objeto vazio e tornar sua data uma propriedade desse objeto. Dessa forma, o angular detectará mudanças no modelo.

No controlador

app.module('yourModule').controller('yourController',function($scope){
$scope.vm={selectedDate:''}
});

Em html

<div ng-controller="yourController">
<input id="selectedDueDate" type="text" ng-model="vm.selectedDate" />
</div>

1

Você tem que acionar o evento de mudança do elemento de entrada porque o modelo ng escuta os eventos de entrada e o escopo será atualizado. No entanto, o gatilho jQuery regular não funcionou para mim. Mas aqui está o que funciona como um encanto

$("#myInput")[0].dispatchEvent(new Event("input", { bubbles: true })); //Works

Seguir não funcionou

$("#myInput").trigger("change"); // Did't work for me

Você pode ler mais sobre como criar e despachar eventos sintéticos .


0

Eu escrevi este pequeno plugin para jQuery que fará todas as chamadas para .val(value)atualizar o elemento angular, se presente:

(function($, ng) {
  'use strict';

  var $val = $.fn.val; // save original jQuery function

  // override jQuery function
  $.fn.val = function (value) {
    // if getter, just return original
    if (!arguments.length) {
      return $val.call(this);
    }

    // get result of original function
    var result = $val.call(this, value);

    // trigger angular input (this[0] is the DOM object)
    ng.element(this[0]).triggerHandler('input');

    // return the original result
    return result; 
  }
})(window.jQuery, window.angular);

Basta inserir este script após jQuery e angular.js e as val(value)atualizações devem funcionar bem.


Versão reduzida:

!function(n,t){"use strict";var r=n.fn.val;n.fn.val=function(n){if(!arguments.length)return r.call(this);var e=r.call(this,n);return t.element(this[0]).triggerHandler("input"),e}}(window.jQuery,window.angular);

Exemplo:


Esta resposta foi copiada literalmente de minha resposta a outra pergunta semelhante .


0

Apenas use:

$('#selectedDueDate').val(dateText).trigger('input');

ao invés de:

$('#selectedDueDate').val(dateText);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.