0

What I am trying to achieve: trigger an angular js directive on a jquery ui event which in turn calls a method in the controller, which adds a value in an array which should appear in the view because of the two way data binding.

What does not work: the value is properly added in the array but the view is not updated

HTML

<li id="collection_{{$index+1}}" class="collection" ng-repeat="collection in column | filter:avoidUndefined" ng-controller="collectionController" ng-model="collection">
    <h1 class="collection-title"> {{ collection.title }}</h1>
    <ul class="bookmarks sortable droppable" droppable=".draggable" sortable=".bookmarks.sortable">
        <li class="bookmark" ng-repeat="bookmark in collection.bookmarks">
            <a class="bookmark-link bookmark-{{bookmark.id}}" href="{{bookmark.url}}">{{bookmark.title}}</a>
        </li>
    </ul>
</li>

DIRECTIVE

directives.directive('droppable',function(){
  return {
    link:function(scope,el,attrs){
        el.droppable({
            accept: attrs.droppable,
            drop: function( event, ui ) {            
                var url = $(ui.helper).text();
                scope.addBookmark(null, url, url);
                $(this).removeClass('ui-state-highlight');
            },
...

CONTROLLER

$scope.addBookmark = function (id, title, url){
    if (id == null) {
        ...
    }
    var bookmark = {
        'id': id,
        'title': title,
        'url': url,
        'type': 'bookmark'
    };
    $scope.collection.bookmarks.push(bookmark);
};

$scope.collection.bookmarks is updated properly but the view stays the same. If I call the same addBookmark function directly with a normal button it works.

6
  • have you tried putting a scope.$apply(); at the end of your link function? Commented May 11, 2014 at 14:49
  • that produces the error: Error: [$rootScope:inprog] Commented May 11, 2014 at 14:53
  • Your directive is doing something it should not... Directives Should not call controller methods Commented May 11, 2014 at 15:04
  • @MirkoGuarnier I would say that it solves your problem. However it is not right way to do it. Commented May 11, 2014 at 15:48
  • So what would be a proper way ? should I use $emit instead of calling directly a function ? Commented May 11, 2014 at 15:49

2 Answers 2

1

You forgot to wrap your drop-callback in $apply:

directives.directive('droppable',function(){
  return {
    link:function(scope,el,attrs){
        el.droppable({
            accept: attrs.droppable,
            drop: function( event, ui ) {            
                scope.$apply(function(scope){
                    var url = $(ui.helper).text();
                    scope.addBookmark(null, url, url);
                    $(this).removeClass('ui-state-highlight');
                });
            },
...
Sign up to request clarification or add additional context in comments.

1 Comment

It works, and I put $(this).removeClass('ui-state-highlight') outside scope.$apply
0

My recommendation is to use $emit instead of calling a method of the controller directly in your directive.

Directives should be always independent components, if inside the directive there is a call to a method from a controller(outside the directive) this will create a dependency between my directive and the controller and of course this will force one not being able to exist without the other.

If I would have to apply a design principle to a directive it will be the S in SOLID, Single responsibility principle. Directives should be able to encapsulate and work independently.

I would probably try this on my directive: scope.$emit("UrlChanged", url);

Something like this:

directives.directive('droppable',function(){
  return {
    link:function(scope,el,attrs){
        el.droppable({
            accept: attrs.droppable,
            drop: function( event, ui ) {            
                    var url = $(ui.helper).text();
                    scope.$emit("UrlChanged", url);
                    $(this).removeClass('ui-state-highlight');

            },

On my controller:

$scope.$on("UrlChanged", function(event, url){
   ... //your has changed.    
});

2 Comments

Despite I like this recommendation, in this way I am back to the initial problem. Should I wrap your $emit inside a scope.$apply ?
Without $apply the view is not updated, only the model

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.