📜 ⬆️ ⬇️

AngularJS: select subtleties within the directive template

This article will describe the solution of one specific problem, and also show by example how $ transclude works.

The task is as follows: make a directive, a wrapper for select. Suppose that we want to add both a select and a label to it with a single tag (then you can add filling errors there, but for simplicity we will not do this). In general, then, at first glance, everything looks simple.

Make a directive and call it field. We will use this:
')
<field title="" type="select" ng-model="selectedColor" options="color.id as color.name for color in colors"></field> 

We came up with several attributes:


Notice that the selectedColor and colors variables from the example are the scope variables in which we used the directive. We specifically indicated them through attributes so that the directive could access them.

Directive code:

 angular.module('directives').directive('field', function () { return { // -    restrict: 'E', // ,      scope: { ngModel: "=", title: "@", type: "@", options: "@" }, //   templateUrl: '/tpl/fields/select.html', }); 


Template code:
 <div class="field"> <label>{{title}}</label> <select ng-model="ngModel" ng-options="{{options}}"></select> </div> 


Live: codepen.io/Dzorogh/pen/umCKG?editors=101


It seems that everything looks simple and should work. Run, check in the debugger -
 <field title="" type="select" ng-model="selectedColor" options="color.id as color.name for color in colors"> <label></label> <select ng:options="color.id as color.name for color in colors" ng:model="ngModel"></select> </field> 

It seems not bad ... just where are all the options? Why are colors empty (more precisely, undefined)?

The fact is that when we specify the scope parameter in a directive, the whole directive body turns into a stone wall - the isolate scope. This isolated skoup does not allow those inside to see the external scope. But, accordingly, all those variables ( ngModel, type, title, options ) that we specified to the directive parameter will be added to our isolated scope and tied to external variables.

And here we can see the problem - we did not add colors to those variables. And rightly so, the directive, of course, should not know what kind of array we want to use there. We have already indicated that we are using this array by writing it in the options attribute.

To access the array, we must at least know the name of the variable. Therefore, the best option is to parse options. The source code of angular.js helps us here, there is a regular for ngOptions.

But wait, even getting the variable name from the outer scope, how can we add it inside?

Actually, the most interesting


To bind external variables to internal ones after initializing directives, you need to use such a thing as "$ transclude". Calling this function gives access to the external scopes and you can simply get any variable from there and “pass” directives into the scop.

New directive, with regular season and transquid:
 .directive('field', function() { //    angularjs var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/; }; return { restrict: 'E', scope: { ngModel: "=", type: "@", title: "@", options: "@" }, // ""    transclude: true, templateUrl: '/fields/select.html', // $transclude   5 . link: function (scope, element, attrs, controller, $transclude) { if (scope.type == 'select') { //     var parsedOptions = attrs.options.match(NG_OPTIONS_REGEXP); //   (   | ) var optionsArray = /^(.*) \|/.exec(parsedOptions[7]) || parsedOptions[7]; // optionsArray -  ( ) ,     . //     ,      Scope // (,   )    . $transclude(function (clone, $outerScope) { scope[optionsArray] = $outerScope[optionsArray]; }); } } } }); 


The final version in CodePen: codepen.io/Dzorogh/pen/gBbyI

Source: https://habr.com/ru/post/228863/


All Articles