AngularJS - How to render a partial with variables?

ID : 274506

viewed : 45

Tags : angularjsangularjs





Top 4 Answer for AngularJS - How to render a partial with variables?

vote vote

98

You can achieve that easily with a directive.

Something like that:

angular.module('myModule') .directive('cars', function () {   return {     restrict: 'E',     scope: { 'cars': '=data' },     template: "<div ng-repeat='car in cars'>\n" +     "  {{car.year}} {{car.make}} {{car.model}}\n" +     "</div>"   }; }); 

Then you can use it like that:

<h1>All New Cars</h1> <cars data="allCars | onlyNew"></cars>  <h1>All Toyotas</h1> <cars data="allCars | make:toyota"></cars> 

You can find more info about directives here.

vote vote

87

This directive provides 2-way data-binding between the parent scope and renamed "local" variables in the child scope. It can be combined with other directives like ng-include for awesome template reusability. Requires AngularJS 1.2.x

jsFiddle: AngularJS - Include a partial with local variables


The Markup

<div with-locals locals-cars="allCars | onlyNew"></div> 

What's going on:

  • This is basically an extension of the ngInclude directive to allow you to pass renamed variables in from the parent scope. ngInclude is NOT required at all, but this directive has been designed to work well with it.
  • You can attach any number of locals-* attributes, which will all be parsed & watched for you as Angular expressions.
    • Those expressions become available to the included partial, attached as properties of a $scope.locals object.
    • In the example above, locals-cars="..." defines an expression that becomes available as $scope.locals.cars.
    • Similar to how a data-cars="..." attribute would be available via jQuery using .data().cars

The Directive

EDIT I've refactored to make use of (and be independent of) the native ngInclude directive, and move some of the calculations into the compile function for improved efficiency.

angular.module('withLocals', []) .directive('withLocals', function($parse) {     return {         scope: true,         compile: function(element, attributes, transclusion) {             // for each attribute that matches locals-* (camelcased to locals[A-Z0-9]),             // capture the "key" intended for the local variable so that we can later             // map it into $scope.locals (in the linking function below)             var mapLocalsToParentExp = {};             for (attr in attributes) {                 if (attributes.hasOwnProperty(attr) && /^locals[A-Z0-9]/.test(attr)) {                     var localKey = attr.slice(6);                     localKey = localKey[0].toLowerCase() + localKey.slice(1);                      mapLocalsToParentExp[localKey] = attributes[attr];                 }             }              var updateParentValueFunction = function($scope, localKey) {                 // Find the $parent scope that initialized this directive.                 // Important in cases where controllers have caused this $scope to be deeply nested inside the original parent                 var $parent = $scope.$parent;                 while (!$parent.hasOwnProperty(mapLocalsToParentExp[localKey])) {                     $parent = $parent.$parent;                 }                  return function(newValue) {                     $parse(mapLocalsToParentExp[localKey]).assign($parent, newValue);                 }             };              return {                 pre: function($scope, $element, $attributes) {                      // setup `$scope.locals` hash so that we can map expressions                     // from the parent scope into it.                     $scope.locals = {};                     for (localKey in mapLocalsToParentExp) {                          // For each local key, $watch the provided expression and update                         // the $scope.locals hash (i.e. attribute `locals-cars` has key                         // `cars` and the $watch()ed value maps to `$scope.locals.cars`)                         $scope.$watch(                             mapLocalsToParentExp[localKey],                             function(localKey) {                                 return function(newValue, oldValue) {                                     $scope.locals[localKey] = newValue;                                 };                             }(localKey),                             true                         );                          // Also watch the local value and propagate any changes                         // back up to the parent scope.                         var parsedGetter = $parse(mapLocalsToParentExp[localKey]);                         if (parsedGetter.assign) {                             $scope.$watch('locals.'+localKey, updateParentValueFunction($scope, localKey));                         }                      }                 }             };         }     }; }); 
vote vote

75

I'd like to offer my solution, which is in a different design.

The ideal usage for you is:

<div ng-include-template="car-list.html" ng-include-variables="{ cars: (allCars | onlyNew) }"></div> 

ng-include-variables's object is added to the local scope. Therefore, it doesn't litter your global (or parent) scope.

Here's your directive:

.directive(   'ngIncludeTemplate'   () ->     {       templateUrl: (elem, attrs) -> attrs.ngIncludeTemplate       restrict: 'A'       scope: {         'ngIncludeVariables': '&'       }       link: (scope, elem, attrs) ->         vars = scope.ngIncludeVariables()         for key, value of vars           scope[key] = value     } ) 

(It's in Coffeescript)

IMO, ng-include is a little bit strange. Having access to the global scope decreases its reusability.

vote vote

67

Top 3 video Explaining AngularJS - How to render a partial with variables?







Related QUESTION?