AngularJS: привязка ng-модели не обновляется при изменении с помощью jQuery

100

Это мой HTML:

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

Когда я набираю текст в поле, модель обновляется через механизм двусторонней привязки. Сладкий.

Однако, когда я делаю это через JQuery ...

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

Это не обновляет модель. Зачем?

Грег
источник
1
Зачем вам с самого начала делать вторую? Вы используете фреймворк, а затем решаете обойти его и установить значение с помощью манипуляций с DOM. Лучший совет, который можно дать с angular новичкам: забудьте о существовании jquery. в 90% случаев будет достаточно angular, а оставшиеся 10% могут быть достигнуты с помощью jqlite внутри ссылки директивы (элемент на самом деле представляет собой обернутый элемент jqlite, готовый к манипулированию).
аннулировано
1
очень важный вопрос
Syed Md. Kamruzzaman
есть очень веские причины, по которым вы можете захотеть изменить угловые модели с помощью манипуляции с dom, возможно, вы разработчик, работающий с инструментом A / B-тестирования и хотите заполнять формы за кулисами, или вы работаете над скриптом greasemonkey для автозаполнения форм.
Shadaez

Ответы:

135

Angular не знает об этом изменении. Для этого вам следует позвонить $scope.$digest()или внести изменения внутри $scope.$apply():

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

См. Это, чтобы лучше понять грязную проверку

ОБНОВЛЕНИЕ : вот пример

Ренан Томаль Фернандес
источник
Я сделал это, как вы говорите: fiddle.jshell.net/AladdinMhaimeed/agvTz/8, но это не работает
Аладдин Мхемед
1
См. Этот fiddle.jshell.net/agvTz/38. Вы должны вызвать function$ scope. $ Apply, передав функцию в качестве аргумента. И $ apply должен вызываться, когда вы вносите изменения в $ scope, то есть внутри onSelect. Ах, я ожидаю, что вы поместите манипуляции с DOM внутри контроллера только для примера, в реальном приложении это неправильно;)
Ренан Томаль Фернандес
Тогда что мне делать, чтобы практиковаться в углах? разделить манипуляции с DOM в директиву?
Аладдин Мхемед
2
Да, вот пример fiddle.jshell.net/agvTz/39: директива может использоваться сколько угодно раз с простым datepicker="scopeKeyThatYouWant"вводом
Renan Tomal Fernandes
Просто вызовите .. $ scope. $ Digest () для урегулирования. это замедлится?
Thant Zin
29

Просто используйте;

$('#selectedDueDate').val(dateText).trigger('input');
Ян Кури
источник
Я использовал trigger('input')для этого датупикер onSelect. Он правильно обновляет значение.
iman
Это помогло мне. Я обновлял значение привязанного ввода из теста директивы, и перенос .val('something'вызова в $apply(или $digestпоследующий вызов ) не работал.
Том Селдон
2
После нескольких часов поисков это сработало! Спасибо!
Дэн
Альхамдулилла, отлично, это работает на меня. спасибо, чтобы сэкономить мои часы
Syed Md. Kamruzzaman
Правда. Я использовал то, $('#selectedDueDate').val(dateText).trigger('change');что мне не помогло. .trigger('input');работает как по волшебству
jafarbtech
17

Я обнаружил, что если вы не помещаете переменную непосредственно в область видимости, она обновляется более надежно.

Попробуйте использовать "dateObj.selectedDate" и в контроллере добавьте selectedDate в объект dateObj следующим образом:

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

Это сработало для меня.

Бен Талиадорос
источник
4
Это сработало и для меня. Я потратил более 2 часов, пытаясь понять, почему не работает двусторонняя привязка. На странице он работал нормально, но область в контроллере не обновлялась. Затем я попробовал ваш подход в отчаянии (потому что для меня не было никакого смысла, что это должно быть правдой) и, черт возьми, это сработало! Спасибо!
TMc
3
То же самое - может ли гуру angularJs объяснить, почему это работает? Это похоже на то, что у представления есть собственная вложенная область видимости, и иногда вы застреваете, обновляя только область обзора, а не область контроллера
Том Карвер
1
У меня хорошо работает! Интересно, почему, черт возьми, это не работает так же с голым прицелом.
Стивен
1
Это мало связано с angular и во многом связано с тем, как работает javascript. Когда вы назначаете переменную области видимости объекту, вы назначаете ее по ссылке, а не по значению, как это делается, когда переменная устанавливается равной примитивному значению. Я говорил об этом в своем посте здесь. stackoverflow.com/questions/14527377/… . Я сослался на этот кусок, который сделал в этом посте, чтобы проиллюстрировать plnkr.co/edit/WkCT6aYao3qCmzJ8t5xg?p=preview .
Ник Брэди,
4

Попробуй это

var selectedDueDateField = document.getElementById("selectedDueDate");
var element = angular.element(selectedDueDateField);
element.val('new value here');
element.triggerHandler('input');
Луч
источник
Его можно использовать, когда у вас нет доступа к $ scope контроллера angular. Например, когда вы пишете сценарий с помощью Tampermonkey.
wassertim 08
3

Просто запустите следующую строку в конце вашей функции:

$ scope. $ apply ()

Рохан М. Набар
источник
2

Что бы ни происходило за пределами Angular, Angular этого никогда не узнает.

Цикл дайджеста помещает изменения из модели -> контроллер, а затем из контроллера -> модель.

Если вам нужно увидеть последнюю модель, вам нужно запустить цикл дайджеста

Но есть вероятность, что цикл дайджеста продолжается, поэтому нам нужно проверить и запустить цикл.

Желательно всегда выполнять безопасное нанесение.

       $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 передает строку, числа и логические значения по значению, в то время как массивы и объекты передаются по ссылке. Таким образом, вы можете создать пустой объект и сделать дату свойством этого объекта. Таким образом angular обнаружит изменения модели.

В контроллере

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

В html

<div ng-controller="yourController">
<input id="selectedDueDate" type="text" ng-model="vm.selectedDate" />
</div>
Химаншу Миттал
источник
1

Вы должны инициировать событие изменения входного элемента, потому что ng-model прослушивает входные события, и область видимости будет обновлена. Однако обычный триггер jQuery у меня не работал . Но вот что работает как шарм

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

Следующее не сработало

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

Вы можете узнать больше о создании и отправке синтетических событий .

Хари Дас
источник
0

Я написал этот небольшой плагин для jQuery, который будет выполнять все вызовы для .val(value)обновления элемента angular, если он есть:

(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);

Просто вставьте этот скрипт после jQuery и angular.js, и val(value)теперь обновления должны работать нормально .


Минифицированная версия:

!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);

Пример:


Этот ответ был дословно скопирован с моего ответа на другой аналогичный вопрос .

dav_i
источник
0

Просто используйте:

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

вместо того:

$('#selectedDueDate').val(dateText);
Векерле Тибор
источник