Как смотреть директиву директивы ng-model

У меня есть директива, которая использует родительскую область в этом представлении. У этой директивы есть дочерняя директива, использующая изолированную область. Я пытаюсь заставить родительскую директиву следить за любыми изменениями, внесенными в ngModel дочерней директивы, и обновлять свой собственный модальный режим, если были внесены изменения. Вот jsfiddle, который, вероятно, объясняет лучше: http://jsfiddle.net/Alien_time/CnDKN/

Вот код:

   <div ng-app="app">
        <div ng-controller="MyController">

            <form name="someForm">
                <div this-directive ng-model="theModel"></div>
            </form>

         </div>
    </div>

Javascript:

    var app = angular.module('app', []);
app.controller('MyController', function() {

});

app.directive('thisDirective', function($compile, $timeout) {

    return {
        scope: false,

        link: function(scope, element, attrs) {
            var ngModel = attrs.ngModel;
            var htmlText = '<input type="text" ng-model="'+ ngModel + '" />' +
                           '<div child-directive ng-model="'+ ngModel + '"></div>';

            $compile(htmlText)(scope, function(_element, _scope) {
                element.replaceWith(_element);                
            });

            // Not sure how to watch changes in childDirective's ngModel ???????

        }, // end link
    } // end return

});

app.directive('childDirective', function($compile, $timeout) {
    return {
            scope: {
                ngModel: '='            
        },  

        link: function(scope, element, attrs, ngModel) {
            var htmlText = '<input type="text" ng-model="ngModel" />';

            $compile(htmlText)(scope, function(_element, _scope) {
                element.replaceWith(_element);                
            });   

            // Here the directive text field updates after some server side process
            scope.ngModel = scope.dbInsertId;

            scope.$watch('dbInsertId', function(newValue, oldValue) {
                if (newValue)
                    console.log("I see a data change!");  // Delete this later
                    scope.ngModel = scope.imageId;
            }, true);

        },

    } // end return
});

В примере вы можете видеть, что внутри родительской директивы есть ввод текста, а также ее дочерняя директива. Если вы введете внутри каждого из них, другая модель будет обновлена, так как они связаны ngmodel. Однако текстовый ввод дочерней директивы обновляется после подключения к серверу. Когда это происходит, текстовый ввод в родительской директиве не обновляется. Поэтому я думаю, что мне нужно следить за ngModel внутри дочерней директивы на предмет любых изменений. Как я могу это сделать? Есть ли смысл?


person Neel    schedule 02.04.2014    source источник
comment
Думаю, вам нужно найти ngModelController и как работать с ng-моделью в ваших собственных директивах, есть несколько приличных документов по теме docs.angularjs.org/api/ng/type/ngModel.NgModelController   -  person shaunhusain    schedule 02.04.2014
comment
Что-то вроде этого, что вы хотите? jsfiddle.net/CnDKN/2   -  person JoseM    schedule 02.04.2014
comment
@JoseM Я думаю, это именно то, что я хотел! :) Я попробую это в своем коде и обновлю. Большое спасибо!   -  person Neel    schedule 02.04.2014
comment
Привет @JoseM Это хорошо работает в примере, но я делаю что-то не так в своих кодах. Мой вопрос: нужна ли мне функция timeout в реальной связи с сервером? Я хочу, чтобы область действия обновлялась до scope.ngModel = scope.dbInsertId; при изменении на dbInsertId. Но когда я добавляю это внутри scope.$watch перед return ngModel.$modelValue;, это не обновляет родительскую директиву. Это работает, когда я установил его в $timeout. Что я делаю неправильно?   -  person Neel    schedule 03.04.2014
comment
Вам нужно сделать scope.$ apply(), так как этот код вызывается вне цикла дайджеста angular   -  person JoseM    schedule 03.04.2014
comment
Я прочитал scope.$apply(), но не смог найти подходящего примера того, как его использовать. Первый раз сталкиваюсь с scope.$apply. Если я не прошу слишком многого, не могли бы вы показать мне быстрый пример, пожалуйста? Мне очень жаль, что я беспокою вас. :(   -  person Neel    schedule 03.04.2014
comment
Я попробовал несколько методов scope.apply() и выдал много ошибок. Значит, я явно что-то делаю не так.   -  person Neel    schedule 03.04.2014
comment
Привет @JoseM, наконец-то я смог заставить scope.apply() работать. Спасибо за вашу вчерашнюю поддержку. Это помогло мне понять, как я могу смотреть директиву chlld из родительской директивы. Если вы можете опубликовать свой комментарий выше в качестве ответа, я приму его, так как ваш jsfiddle помог мне с моим вопросом. :)   -  person Neel    schedule 03.04.2014
comment
Разве ng-change=fn() тоже не решит?   -  person Thiago C. S Ventura    schedule 21.01.2016


Ответы (1)


Как упомянул @shaunhusain, вы должны использовать ngModelController для взаимодействия с ngModel. В ngModelController вы можете настроить наблюдение за $modelValue и изменить значение в модели, вызвав $setViewValue. Помните, что для использования ngModelController вам нужно добавить require: "ngModel" к вашему объекту определения директивы.

Когда вы получаете значение из-за пределов angular (например, из вашей базы данных) и, в свою очередь, хотите использовать это значение для изменения значения модели, вам нужно обернуть этот код в scope.$apply()

app.directive('thisDirective', function($compile, $timeout, $log) {

    return {
        scope: false,
        require: 'ngModel',

        link: function(scope, element, attrs, ngModel) {
            ...

            scope.$watch(
                function(){
                    return ngModel.$modelValue;
                }, function(newValue, oldValue){
                    $log.info('in *thisDirective* model value changed...', newValue, oldValue);
                }, true);

        }, // end link
    } // end return

});

app.directive('childDirective', function($compile, $timeout, $log) {
    return {
        scope: {
            ngModel: '='
        },
        require: 'ngModel',

        link: function(scope, element, attrs, ngModel) {
            ...

            scope.$watch(
                function(){
                    return ngModel.$modelValue;
                }, function(newValue, oldValue){
                    $log.info('in *childDirective* model value changed...', newValue, oldValue);
                }, true);

            // make believe change by server
            setTimeout(function() {
                scope.$apply(function() {
                    ngModel.$setViewValue('set from the server...');
                };
            },5000);


        },

    } // end return
});

соответствующий jsfiddle http://jsfiddle.net/CnDKN/2/

НО Я не думаю, что это неправильное использование $setViewValue. Согласно документам, это должно использоваться для обновления значения, отображаемого в пользовательском интерфейсе, а не обязательно значения модели.

На самом деле есть еще один способ сделать эту работу, который, как мне кажется, более прост и удобен в использовании. Просто используйте =attr для настройки двунаправленной привязки свойства, которое вы хотите использовать как в thisDirective, так и в childDirective. И затем вы можете просто настроить атрибут ng-model в своей директиве, и когда вы впервые используете его, вам даже не нужно знать, что он использует ng-model внизу.

Вот код, который показывает, что я имею в виду:

app.directive('thisDirective', function($compile, $timeout, $log) {

    return {
        scope: {
            thisval: '='
        },

        link: function(scope, element, attrs) {

            var htmlText = '<input type="text" ng-model="thisval" />' +
                           '<div child-directive childval="thisval"></div>';

            $compile(htmlText)(scope, function(_element, _scope) {
                element.replaceWith(_element);                
            });

            scope.$watch('thisval',function(newVal,oldVal){
                $log.info('in *thisDirective* thisval changed...',
                          newVal, oldVal);
            });


        }, // end link
    } // end return

});

app.directive('childDirective', function($compile, $timeout, $log) {
    return {
        scope: {
            childval: '='
        },

        link: function(scope, element, attrs) {
            var htmlText = '<input type="text" ng-model="childval" />';

            $compile(htmlText)(scope, function(_element, _scope) {
                element.replaceWith(_element);                
            });

            scope.$watch('childval',function(newVal,oldVal){
                $log.info('in *childDirective* childval changed...',
                          newVal, oldVal);
            });

            // make believe change that gets called outside of angular
            setTimeout(function() {
                // need to wrap the setting of values in the scope 
                // inside of an $apply so that a digest cycle will be 
                // started and have all of the watches on the value called
                scope.$apply(function(){
                    scope.childval = "set outside of angular...";
                });
            },5000);

        },

    } // end return
});

Обновленный пример jsfiddle: http://jsfiddle.net/CnDKN/3/

person JoseM    schedule 03.04.2014
comment
Отличный ответ! Столько всего из него узнал! Спасибо @JoseM :) - person Neel; 03.04.2014
comment
Привет, JoseM. После твоего замечательного ответа и поддержки я просто собрал воедино то, что узнал на данный момент. Теперь я понимаю, как и когда использовать метод scope.$appy(). Я боролся с этим. Я также считаю, что гораздо проще иметь изолированную область, передавать ng-model и использовать ее для просмотра и обновления модели. Ваш второй пример очень помог с этим. Одной из самых больших проблем было $apply. Происходило лишь то, что установка $watch обновляла ng-модель в дочерней директиве, но родительская директива не знала об изменении. .... - person Neel; 03.04.2014
comment
... Вот почему область в родительском элементе не обновлялась, хотя они были связаны ngModelController. Поскольку это было обновление на стороне сервера ajax, добавление метода $apply обновило область действия, и родительская директива узнала об этом изменении. Все заработало в итоге. фу... какая это была кривая обучения. Будучи новичком в $apply, мне было трудно понять, как и где его использовать, и примеров по этому поводу было не так много. Теперь все это имеет смысл. Большое спасибо за вашу помощь... Это так приятно, когда вы изучаете магию Angular после того, как заблудились в пустыне! :) - person Neel; 03.04.2014
comment
@blackops_programmer просмотрите видео с egghead.io по адресу egghead.io/technologies/angularjs, чтобы лучше понять AngularJS. - person JoseM; 03.04.2014