Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I pass many values to an Angularjs directive?

I'm creating a reusable bit of html via a directive. The html would have a few variables that I want passed from the original scope. This is done easily by declaring attributes on the directive, then creating an isolate scope and capturing them. The question is, is there a better way to do this for a larger number of variables? I had thought of passing in an object like {firstAttr: $scope.one, secondAttr: $scope.two...} and picking this object apart to get each piece. This works the first time, but the two-way databinding doesn't work (even using the '=').

The problem is the thing that is bound is the object, not each of the individual parts of the object. Could I maybe use the compile function in the directive to add each of the attributes to the element or something? so:

<mydirective databinding="{one:'first one', two:'second one'}">

would be translated into:

<mydirective one="first one" two="second one">

That way my databinding would work as expected by capturing the attributes in the directive. How would I go about accomplishing that design, or is there just another way completely to do this?

like image 237
Spencer Avatar asked Mar 21 '13 21:03

Spencer


Video Answer


2 Answers

The databinding directive idea is an interesting one but it's not the way I would do it since I believe you'd run into directive priority issues, plus the fact that it's very non-standard and would make your code hard to follow for future programmers. There's several ways to do this so I'll discuss 3 different solutions I've used.

Solution 1

If you only need one way data binding, the simplest solution is to use angular's scope.$eval function on the string representation of the object inside your directive after interpolating any simple scope variables on it using {{}}. The string representation doesn't even have to be valid JSON, since you'll notice in the example below I don't include quotes around the object keys.

In the view:

<div databinding="{one:'first', two:{{scopeVar}}, complex:[1,2, "Hi"]}"></div>

And in the javascript:

app.directive('databinding', function () {
   return{
      link: function (scope, elm, attrs) {

        console.debug(scope.$eval(attrs['databinding']));

      }
   }
});

Solution 2

Another one-way data binding solution is to create an option object inside the controller and pass it to the directive using "@" (or even "="):

In the controller:

$scope.options = {one: "first, two: "second"};

In the view:

<div databinding="options"></div>

And in the javascript:

app.directive('databinding', function () {
   return{
      scope: {

        options: "@" //Can also use = here

      },
      link: function (scope, elm, attrs) {

        console.log(scope.options);

      }
   }
});

Solution 3

If you do need two way data binding, you're mostly out of luck, as there's no elegant way to do it. HOWEVER, if you're in the market for hackish solutions, you can accomplish two way data binding with a method very similar to solution 2 but with a change to the option object.

Instead of declaring an option object containing simple primitive data types like strings, create a dummy object inside the option object, which you then declare your variables inside of. Doing it this way, changes to scope variables in your controller will also be realized inside the directive, as demonstrated through the timeouts.

Controller:

$scope.someScopeVar = "Declared in controller"    

$scope.options = {
  dummy: {
     one: $scope.someScopeVar,
     two: "second"
  }
}

window.setTimeout(function(){

  $scope.someScopeVar = "Changed in controller";

}, 2000)

View:

<div databinding="options"></div>

Directive:

app.directive('databinding', function () {
   return{
      scope: {
        options: "=" //You need to use = with this solution
      },
      link: function (scope, elm, attrs) {

        console.log(scope.options.dummy.one); //Outputs "Declared in controller"

        window.setTimeout(function(){

           console.log(scope.options.dummy.one) //Outputs "Changed in controller"

        }, 5000)

      }
   }
});

This method works since javascript passes objects by reference whereas primitives are copied. By nesting an object in an object the data binding is preserved.

like image 59
Scottmas Avatar answered Oct 17 '22 10:10

Scottmas


You can change scope in directive as follows

.('mydirective ', function(){

   var linker = function(scope, element){
        console.log(scope.one, scope.two);    
   }    
   return {
        link: linker,
        scope: {one:"=", two:"="}
   }    
});
like image 40
Anoop Avatar answered Oct 17 '22 10:10

Anoop