0

I'm not sure if this has been asked before, but I went through a lot of angular 2-way binding posts and frankly couldn't understand what they were talking about. So I'll just state my problem again.

Lets say I want to use a tags input plugin such as this. I define an angular directive called tags and within it, initialize tagsinput on a text input. The text element is bound to a model which is populated in the controller from a service.

I have this in Plunker here.

The problem is when the directive loads, the model is not populated yet, so the tagsinput plugin is initialized with an empty value. But I want it to be initialized with a server side value. I've come across this problem often and I've been resorting to initializing my plugins in service callbacks within controllers which I know is cardinal sin.

I'm unable to get out of thinking that eventually, at some level, the plugin initialization must be delayed until the service returns so the value is available. And am I correct if I say that even in the angular way of doing it, this is somehow achieved? But in any case, I'm not able to figure out how to get this working.

2 Answers 2

2

I think the best way to solve this problem is using a watcher inside your directive.

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

app.controller('Main', function ($scope, $timeout) {

   //this simulates data being loaded asynchronously after 3 second 
   $timeout(function () {
       $scope.list = [1, 2, 3, 4];
   }, 3000);

});

app.directive("mydirective", function()
{   
    return {
        restrict: "A",
        link: function(scope, element, attrs)
        {
            scope.$watch('list', function (newVal, oldVal) {
                if (newVal) {

                    //initialize the plugns etc
                    scope.result = 'Model has been populated';
                }    
            });
        }
    };
});

In my example I am using $timeout inside the controller to simulate an async load of data, once the 'list' model gets populated the watcher inside the directive will realize it (newVal is not empty or undefined) and then the plugin can be initialized. This is a better approach instead of callbacks inside the controller because your code is much more concise.

Here is a jsBin

EDIT:

Just remember that the digest cycle has to be fired when the model is populated. If you are using $resource or $http to fetch the data from the server you don't have to worry, but if you are doing it outside Angular you will have to fire the digest cycle with $scope.$apply() so the watcher can be aware that the model was populated.

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

Comments

1

As Bertrand said it's achievable by using watchers in directives link function. You need to wait for data to appear and this is what watchers are for. Take a look at my update to your plunker here http://plnkr.co/edit/hb3UYl.

I faked async response with $timeout instead of $http but it doesn't really matter here.

What it does is it sets up watcher in directive's link function which looks for changes of $scope.list. If change is detected and newValue is something useful it uses it to populate input field and turn it into tag list.

Notice I changed your $('.taginput') into element as this is what you are operating on. Also I think this is good practice to search for DOM elements you want to manipulate only down the hierarchy of current element. This makes you sure that if you place directive on given element - no other elements except this one and its children will be impacted.

Comments

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.