Angularjs passes ngModel from wrapper directive to wrapped directive

I am new to Angular and am still painfully enveloping myself in special directives.

I would like to reuse this HTML bit

<ui-select ng-model="model.selectedLanguages" multiple search-enabled="true" theme="select2" style="width: 300px;"> <ui-select-match placeholder="Pick one...">{{$item.name}}</ui-select-match> <ui-select-choices repeat="lang.id as lang in langs |filter: { name : $select.search }"> <div ng-bind-html="lang.name | highlight: $select.search" ></div> </ui-select-choices> </ui-select> 

wrapping it in my custom directive:

 <language-picker ng-model="model.selectedLanguages"/> 

something like that:

 app.directive('languagePicker', function() { return { template : '<ui-select ng-model="**PARENT NGMODEL**" multiple search-enabled="true" theme="select2" style="width: 300px;"><ui-select-match >{{$item.name}}</ui-select-match><ui-select-choices repeat="lang.id as lang in langs | filter: { name : $select.search }"><div ng-bind-html="lang.name | highlight: $select.search"></div></ui-select-choices></ui-select>', restrict : 'E', require : 'ngModel', replace : true .... }; }); 

But how to pass ngModel from my language-picker directive to ui-select ?

UPDATE

Using the suggestions below, I got it with ui-select, but the external model is not updated at all, see plnkr.co/edit/Y43dmMGIc5GxM9fLoNPW , probably because the content area of ​​the child and the parent area remain unchanged?

UPDATE 2

I did it in a confusing way, which looks awful for me, because I have no idea why it "works" in the first place (see strange things happening in the controller):

 app.directive('languagePicker', function(LanguageService) { return { templateUrl : 'LanguagePickerTpl.html', restrict : 'E', scope : { languages : '=' }, controller : function($scope, LanguageService) { console.log($scope); $scope.langs = LanguageService.get(); $scope.model = $scope; } }; }) 

:

 <ui-select ng-model="model.languages" multiple search-enabled="true" theme="select2" style="width: 300px;"> <ui-select-match>{{$item.name}}</ui-select-match> <ui-select-choices repeat="lang.id as lang in langs | filter: { name : $select.search }"> <div ng-bind-html="lang.name | highlight: $select.search"></div> </ui-select-choices> </ui-select> 

I would be very happy if someone could explain what is happening (a “working” example works here http://plnkr.co/edit/B53F9sc7UGkj0uxUpC17 )

+7
javascript angularjs angularjs-directive
source share
3 answers

You need to use the "equals" syntax for your scope. This will save the values ​​populated by the parent area. Thus, your directive becomes:

 app.directive('languagePicker', function() { return { template : '<ui-select ng-model="**PARENT NGMODEL**" multiple search-enabled="true" theme="select2" style="width: 300px;"><ui-select-match >{{$item.name}}</ui-select-match><ui-select-choices repeat="lang.id as lang in langs | filter: { name : $select.search }"><div ng-bind-html="lang.name | highlight: $select.search"></div></ui-select-choices></ui-select>', restrict : 'E', require : 'ngModel', replace : true, scope: { ngModel: "=ngModel" } ... }; }); 

I am sure this will work for you :) Let me know if it is not!

+4
source share

ng-model has special processing, see here under the heading "User control example". Steps:

  • I suggest you use an isolated area; this makes the interface for your component more clear and saves you from side effects. In this case, you want to pass a list of available options (languages):

     scope: { langs: '=' } 

    Using:

     <language-picker ng-model="model.selectedLanguages" langs="langs"/> 
  • Your directive requires (possibly optional) ngModel :

     require: ['ngModel'] 
  • You override the ngModel $render method, for example:

     link: function(scope,elem,attrs,ctrls) { var ngModelCtrl = ctrls[0]; ngModelCtrl.$render = function() { ... }; } 

    The render logic is responsible for passing the model value (here: <language-picker ng-model="model.selectedLanguages"/> , i.e. model.selectedLanguages ) to the view. The simplest thing I can think of is to use an isolated scope and pass in the external value of the model of the isolated scope variable as:

      ngModelCtrl.$render = function() { scope.innerSelection = ngModelCtrl.$viewValue; }; 

    Bind this variable in the template as:

     <ui-select ng-model="innerSelection" ...> ... </ui-select> 
  • Finally, you must make sure that the changes in the internal selection will apply to the external model:

      // still inside link() scope.$watch('innerSelection', function(newval, oldval) { if( newval != oldval ) { // skip the first time ngModelCtrl.$setViewValue(newval); } }); 

This solution may be a little more complicated than others, but allows you to use all the functions of ngModel , for example. validation, parsing / formatting (i.e. data conversion).

+13
source share

You can pass model.selectedLanguages ​​as a directive property, for example:

< language-picker language="model.selectedLanguage" />

And inside the directive, a language property is used. You can decide how much you need.

I also recommend using templateUrl in an html document with this bit of code. It really makes life easier. You can then modify the html document to just put ng-model = "language" after editing your directive as suggested.

There are really a lot of examples in the official documentation. https://docs.angularjs.org/guide/directive

0
source share

All Articles