Atualizar modelo angular após definir o valor de entrada com jQuery

160

Eu tenho esse cenário simples:

Elemento de entrada cujo valor é alterado pelo método val () do jQuery.

Estou tentando atualizar o modelo angular com o valor que o jQuery define. Eu tentei escrever uma diretiva simples, mas não está fazendo o que eu quero.

Aqui está a diretiva:

var myApp = angular.module('myApp', []);
myApp.directive('testChange', function() {
    return function(scope, element, attrs) {        
        element.bind('change', function() {
            console.log('value changed');
        })
    }
})

esta é a parte do jQuery:

$(function(){
    $('button').click(function(){
        $('input').val('xxx');
    })
})

e html:

<div ng-app="myApp">
    <div ng-controller="MyCtrl">
        <input test-change ng-model="foo" />
        <span>{{foo}}</span>
    </div>
</div>

<button>clickme</button>

Aqui está o violino com a minha tentativa:
http://jsfiddle.net/U3pVM/743/

Alguém pode, por favor, apontar a direção certa?

Michal J.
fonte
2
Misturar AngularJS e jQuery, fora das diretivas, geralmente é uma má idéia. O jQuery é obrigatório para o que você deseja fazer?
Blackhole
Por que você não define o evento click com angular para uma função no controlador para alterar o valor do modelo?
Jimmy Kane
@Blackhole, estou usando o plugin de upload jquery que faz seu trabalho e coloca o caminho para o arquivo carregado na entrada oculta. Tudo o que preciso é ter acesso a esse caminho no modelo angular. Por isso, pensei em defini-lo como um valor de entrada oculta e, depois de alterada, atualize o modelo angular.
Michal J.
Não vejo o Jquery chamar no seu violino ... Você pode verificar e apontar onde altera o valor com o JQ?
Valentyn Shybanov
Seu violino está vinculado a um aplicativo de amostra Todo, que não está relacionado a esta pergunta.
Stewie

Respostas:

322

O ngModel escuta o evento "input"; portanto, para "consertar" seu código, você precisará acionar esse evento após definir o valor:

$('button').click(function(){
    var input = $('input');
    input.val('xxx');
    input.trigger('input'); // Use for Chrome/Firefox/Edge
    input.trigger('change'); // Use for Chrome/Firefox/Edge + IE11
});

Para a explicação desse comportamento específico, verifique esta resposta que eu dei há um tempo: " Como o AngularJS captura internamente eventos como 'onclick', 'onchange'? "


Mas, infelizmente, este não é o único problema que você tem. Como apontado em outros comentários, sua abordagem centrada no jQuery está totalmente errada. Para obter mais informações, dê uma olhada neste post: Como eu “penso no AngularJS” se tenho um background em jQuery? )

Stewie
fonte
5
Legal! Com uma exceção: por que não funciona em entradas ocultas? Quando troco meu tipo de entrada para texto, ele funciona conforme o esperado.
30513 Karol
8
Isso resolveu um grande problema ao usar plugins de calendário jquery. Ao hackear o plug-in para acionar ('input') após input.val (), o evento ng-change é acionado após o usuário selecionar a data do calendário. Obrigado!
Drone Brain
3
Funciona perfeitamente em entradas ocultas: element.triggerHandler ("change");
Falko
2
isso não funciona para mim. $ ("# Distância"). Val (dados); $ ("# Distance"). Trigger ('entrada');
Stas Svishov
10
Com o Angular 1.4.3, o inputevento não funciona no IE, incluindo o IE11. inputO evento funciona apenas quando $sniff.hasEvent('input')é verdadeiro, mas $sniff.hasEvent('input')é falso mesmo no IE11! Em vez disso, podemos usar o changeevento .
Shuhei Kagawa
61

Espero que isso seja útil para alguém.

Não foi possível obter o jQuery('#myInputElement').trigger('input')evento para meu aplicativo angular.

Eu era capaz angular.element(jQuery('#myInputElement')).triggerHandler('input')de ser apanhada.

ginman
fonte
2
por favor, explique a solução, não basta escrever código. eu recebo angular não é $ não é uma função
Thakhani Tharage
1
$ representa jQuery aqui. Está marcado na pergunta.
ginman 22/06/2015
Isso funcionou para mim também, enquanto $ ('myInputElement'). Trigger ('input') era esquisito. Parecia funcionar aleatoriamente algumas vezes, mas não outras, e eu nunca consegui estabelecer um padrão. Esta solução funciona sempre.
BryanP
2
Mina é trabalhado com prefixo #:angular.element($('#myInputElement')).triggerHandler('input')
Ebru Yener
Esse triggerHandler funcionou para mim também, eu estava lidando com a área de texto e não com a entrada. Graças
Mounir
25

A resposta aceita que estava acionando o inputevento com o jQuery não funcionou para mim. Criar um evento e despachar com JavaScript nativo fez o truque.

$("input")[0].dispatchEvent(new Event("input", { bubbles: true }));
Martins Balodis
fonte
Esta foi a solução para mim também. Usando Angular 1.1.5, jQuery 2.2.4 e Jasmine 2.5.3. Muito, muito estranho que nenhum outro mecanismo de gatilho funcione.
Lars-Erik
Eu estava procurando o método JS nativo para acionar a validação de formulário ao definir uma entrada em um script e isso funcionou para mim, obrigado.
Dispersa
Funciona mesmo com o Angular 5, ótimo!
Igor
Obrigado. Especialmente útil para webview.evaluatejavascript
Berry Wing
22

Eu não acho que o jQuery seja necessário aqui.

Você pode usar $ relógio e ng clique com o botão em vez

<div ng-app="myApp">
  <div ng-controller="MyCtrl">
    <input test-change ng-model="foo" />
    <span>{{foo}}</span>

    <button ng-click=" foo= 'xxx' ">click me</button>
    <!-- this changes foo value, you can also call a function from your controller -->
  </div>
</div>

No seu controlador:

$scope.$watch('foo', function(newValue, oldValue) {
  console.log(newValue);
  console.log(oldValue);
});
Utopik
fonte
1
Obrigado Utopik, você está certo, eu não devo misturar jquery com angular quando houver uma maneira de fazer o trabalho usando angular.
Michal J.
17

É necessário usar o código a seguir para atualizar o escopo do modelo de entrada específico da seguinte maneira

$('button').on('click', function(){
    var newVal = $(this).data('val');
    $('select').val(newVal).change();

    var scope = angular.element($("select")).scope();
    scope.$apply(function(){
        scope.selectValue = newVal;
    });
});
jayM
fonte
1
var scope = angular.element($("select")).scope(); scope.$apply(function(){ scope.selectValue = newVal; }); essa parte me ajudou muito. Meu problema foi atualizar um modelo angular após uma chamada de jquery ajax na função success. Foi lento com $ http porque havia um ouvinte de evento de alteração duplicado para o elemento que estava fazendo outra coisa, então eu o mesclei no jQuery.
Emmanuel Mahuni
1
O uso de element.scope () é uma péssima idéia, porque funciona apenas com o log ativado. Se você estiver em um site de produção, desabilite o logon na sua configuração com $compileProvider.debugInfoEnabled(false);ou $logProvider.debugEnabled(false);Consulte code.angularjs.org/1.4.0/docs/guide/production para obter mais informações.
RWAM 01/04
Eu tentei este método para variável oculta e funcionou. var li_cuboid_id = $ ('# li_cuboid_id'); li_cuboid_id.val (json.cuboidId) .change (); var scope = angular.element ($ ("# li_cuboid_id")). scope (); scope. $ apply (function () {scope.cuboidId = json.cuboidId;}); a variável oculta é definida como: <input id = "li_cuboid_id" type = hidden ng-model = "cuboidId">
Rahul Varadkar 15/18
7

Fiz modificações apenas na inicialização do controlador adicionando listener no botão de ação:

$(document).on('click', '#action-button', function () {
        $timeout(function () {
             angular.element($('#input')).triggerHandler('input');
        });
});

Outras soluções não funcionaram no meu caso.

Ebru Yener
fonte
4

Eu sei que é um pouco tarde para responder aqui, mas talvez eu possa economizar algumas vezes por dia.

Eu tenho lidado com o mesmo problema. Um modelo não será preenchido depois que você atualizar o valor da entrada do jQuery. Eu tentei usar eventos de gatilho, mas nenhum resultado.

Aqui está o que eu fiz que pode salvar seu dia.

Declare uma variável na sua tag de script em HTML.

Gostar:

<script>
 var inputValue="";

// update that variable using your jQuery function with appropriate value, you want...

</script>

Uma vez você fez isso usando o serviço abaixo de angular.

$ window

Agora, abaixo da função getData chamada do mesmo escopo do controlador, você terá o valor que deseja.

var myApp = angular.module('myApp', []);

app.controller('imageManagerCtrl',['$scope','$window',function($scope,$window) {

$scope.getData = function () {
    console.log("Window value " + $window.inputValue);
}}]);
Khawaja Asim
fonte
3

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 esse script depois do jQuery e do angular.js e as val(value)atualizações agora devem ser boas.


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:

dav_i
fonte
Desculpe desenterrar isso, mas alguma idéia de como isso poderia / poderia funcionar com a caixa de seleção ou seleciona uma vez que aqueles usam .prop em vez de .val? Isso está funcionando perfeitamente para alterações .val de entrada, pelo menos, então obrigado!
Austin
@ Austin Acho que não sei. Esta é uma pergunta de cinco anos sobre duas tecnologias que são amplamente consideradas desatualizadas; portanto, não guardei o conhecimento na cabeça. Boa sorte para obter a solução que você precisa.
dav_i 19/02
2

Se você estiver usando o IE, precisará usar: input.trigger ("change");

William
fonte
2

adicione .change()depois de definir o valor.

exemplo:('id').val.('value').change();

também não se esqueça de adicionar onchangeou ng-changemarcar em html

Azeez
fonte
0

Eu fiz isso para poder atualizar o valor de ngModel de fora com Vanilla / jQuery:

function getScope(fieldElement) {
    var $scope = angular.element(fieldElement).scope();
    var nameScope;
    var name = fieldElement.getAttribute('name');
    if($scope) {
        if($scope.form) {
            nameScope = $scope.form[name];
        } else if($scope[name]) {
            nameScope = $scope[name];
        }
    }
    return nameScope;
}

function setScopeValue(fieldElement, newValue) {
    var $scope = getScope(fieldElement);
    if($scope) {
        $scope.$setViewValue(newValue);
        $scope.$validate();
        $scope.$render();
    }
}

setScopeValue(document.getElementById("fieldId"), "new value");
anlijudavid
fonte