1

What Im trying to accomplish: I have jQuery plugin that I want to wrap to be an angular directive. And to do this i need to pass params to it, and the plugin have it ownonchange` even where I'm trying to change passed values so it will be reflected in the original scope. And I get some really unexpected and strange results. Here is fiddle number one:

http://jsfiddle.net/q1915b38/2/

Here I tried to simulate minimal example of what i want to accomplish. But as you see it just does not work at all. Value in the original controller scope doesn't change. But in real world example it act a bit differently. And here goes fiddle number 2.

http://jsfiddle.net/ne5hbgxp/

The only thing i changed from first one is template from template:

template: "<input type='text' id='blah' />",

to

template: "<input type='text' id='blah' ng-model='abc' />",

Basically i added to template an ng-model attribute which I don't use anywhere at all. But it just goes from totally not working to working with glitches. Now when change trigger first time nothing happens. But when it triggers second time - value from previous change got passed into original scope. When I change 3 time value - the value from second time goes to controller. And so on. So basically it have a delay with one step back for unknown for me reason. And this is exact behavior that I face in my real world example, although there no ng-model at all and all content generate via jQuery plugin.

So basically my questions are following:

1) Why its not working in first example

2) Why its working in second example with this strange behavior with one step delay? What the logic on this behavior?

3) What is a correct way to solve this ?

2 Answers 2

2

Since you're using jQuery to update something in your directive, a call to $apply() is needed to trigger an angular digest cycle

link: function(scope, iElement, iAttrs, controller) {
    $('#blah').change(function() {
        scope.value = $(this).val();
        scope.$apply();
    });
}

JSFiddle Link

However looking at this a bit closer, is there a reason why you prefer jQuery .change() in this example? Angular offers ngChange, which may be just what you are looking for, since you will be alleviated from explicitly calling a digest cycle since we're in Angular world and not battling jQuery so to speak. An example may include...

<input type='text' id='blah' ng-model='abc' ng-change='update()'/>

scope.update = function() {
    scope.value = scope.abc;
}

JSFiddle Link with ng-change

Sign up to request clarification or add additional context in comments.

2 Comments

It was simplified example. In real world i dont have access to template since its generated via jquery plugin so i cant put ng-change. I can only hook into that plugin change event. apply solved issue, but it still doesnt answer a second question.
Could you add attributes and $compile the templates? Could be more trouble than it's worth, but an idea. For the specifics as to why the example with ng-model is behaving different, my best guess is a $watch is being registered somewhere with the inclusion of the ng-model directive that otherwise would not have
1

Issue is pretty simple... events that change scope that are outside of angular's core directives aren't visible to angular so you you need to notify angular to perform a digest so the view can be updated.

This is done with $apply() or can use $timeout() to prevent calling $apply() while another digest cycle is in progress

    link: function (scope, iElement, iAttrs, controller) {
        $('#blah').change(function () {
            var $el =$(this);
            scope.$apply(function () {
                scope.value = $el.val();
            })
        });
    }

I would suggest taking advantage of the iElement being exposed in the directive. This is a jQuery object when jQuery is included in page prior to angular

4 Comments

ok. That worked. What about second question? And from what i have read in docs now that apply just force $digest. But i have tried to force digest manually and in have zero effect.
best practice is never call digest yourself
ye, i looked into source and found that apply call digest on rootscope while i was using digest on the passed scope. That makes difference. But still i wonder why it behaves so strange in second example versus first
if using jQuery is important that it gets inserted in page before angular...so you can use it instead of jQlite

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.