【问题标题】:angularjs pass ngModel from wrapper directive to wrapped directiveangularjs 将 ngModel 从 wrapper 指令传递给 Wrapper 指令
【发布时间】:2015-04-28 09:15:50
【问题描述】:

我是 Angular 的新手,仍然痛苦地围绕着自定义指令。

我想重用这段 HTML

<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>

通过将其包装到我的自定义指令中:

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

类似这样的:

app.directive('languagePicker', function() {
            return {
                template : '<ui-select ng-model="**PARENT'S 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
                    ....
            };
        });

但是如何将 ngModel 从我的 language-picker 传递给 ui-select 指令?

更新

使用下面的建议,我让它与 ui-select 一起工作,但外部模型根本没有更新,请参阅plnkr.co/edit/Y43dmMGIc5GxM9fLoNPW,可能是因为它的子范围和父范围保持不变?

更新 2

我让它以一种对我来说看起来很可怕的复杂方式工作,因为我不知道它为什么首先“工作”(请参阅​​控制器中发生的奇怪事情):

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>

如果有人能解释发生了什么,我会非常高兴(“工作”示例在这里 http://plnkr.co/edit/B53F9sc7UGkj0uxUpC17 )

【问题讨论】:

  • 如果我没记错的话,子作用域可以访问其父属性而不做任何事情。如果您关心可重用性,您可能应该遵循下面描述的答案,并通过子指令上的属性传递属性。

标签: javascript angularjs angularjs-directive


【解决方案1】:

ng-model 有一些特殊处理,请参阅标题“自定义控件示例”下的here。步骤是:

  1. 我建议你使用隔离范围;它使您的组件界面更清晰,并使您免受副作用的影响。在这种情况下,您想传递可用选项(语言)的列表:

    scope: {
        langs: '='
    }
    

    用法如下:

    <language-picker ng-model="model.selectedLanguages" langs="langs"/>
    
  2. 您的指令需要(可能是可选的)ngModel:

    require: ['ngModel']
    
  3. 您覆盖ngModel$render 方法,例如:

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

    render 的逻辑负责将模型值(这里的那个:&lt;language-picker ng-model="model.selectedLanguages"/&gt;,即model.selectedLanguages)传递给视图。我能想到的最简单的事情是使用隔离范围将外部模型值传递给隔离范围的变量

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

    在模板中将此变量绑定为:

    <ui-select ng-model="innerSelection" ...>
        ...
    </ui-select>
    
  4. 最后,您必须确保对内部选择的更改将传播到外部模型:

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

此解决方案可能比其他解决方案涉及更多,但可以让您使用 ngModel 的所有功能,例如验证、解析/格式化(即数据转换)。

【讨论】:

  • NgModelController.$render() 是我拼图的最后一块。谢谢!
【解决方案2】:

您需要在指令的范围内使用“等于”语法。这将保留填充在父范围中的值。 所以你的指令变成:

app.directive('languagePicker', function() {
        return {
            template : '<ui-select ng-model="**PARENT'S 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"
            }
            ...
        };
    });

我相信这对你有用:) 如果没有,请告诉我!

【讨论】:

  • 谢谢,它有效!至少它适用于模板中嵌入的&lt;input ng-model="..."/&gt;。对于ui-select,我不确定,因为我现在遇到了multidir 错误,因为我将两个指令与隔离范围相结合,所以还有更多工作要做)
  • 其实我不能让它和ui-select一起工作,外部模型根本没有更新,见plnkr.co/edit/Y43dmMGIc5GxM9fLoNPW
  • 我让它以一种非常复杂的方式工作,我不知道为什么它会工作plnkr.co/edit/B53F9sc7UGkj0uxUpC17
  • @AunAun 干得好。我早些时候确实分叉了,玩了一场,但我没有快乐。不过从你的新品来看,这看起来很棒!
  • 我用过 ng-model="ngModel"
【解决方案3】:

您可以将 model.selectedLanguages 作为属性传递给指令,例如:

&lt; language-picker language="model.selectedLanguage" /&gt;

在指令内部使用语言属性。 你可以决定你需要哪个范围。

我还建议对包含该代码的 html 文档使用 templateUrl。它真的让生活更轻松。 然后,您可以修改 html 文档,在按照建议编辑指令后简单地放置 ng-model="language"。

官方文档中确实有很多例子。 https://docs.angularjs.org/guide/directive

【讨论】:

    【解决方案4】:

    我让它以两种不同的方式工作:

    给定以下“调用”:

          <div
            ng-model="myData"
            required
            boolean-select
          ></div>
    

    要么使用“替换:真”:

      .directive('booleanSelect', function booleanSelect() {
        return {
          restrict: 'A',
          template: '<select ' +
            'class="form-control" ' +
            'ng-options="v.value as v.label for v in yesNoOptions" ' +
            'ng-required="required"><option value="">Select</option></select>',
          replace: true,
          link: function ($scope, $element, $attrs) {
            $scope.required = $attrs.hasOwnProperty('required');
    
            $scope.yesNoOptions = [{
              value: true,
              label: 'Yes',
            }, {
              value: false,
              label: 'No',
            }];
          },
        };
      })
    

    不需要ng-model 放在模板中,因为它替换了“内联”,从“调用者”合并属性,因此将使用调用者的ng-model .

    或者,这样:

      .directive('booleanSelect', function booleanSelect() {
        return {
          restrict: 'A',
          template: '<select ' +
            'class="form-control" ' +
            'ng-model="ngModel" ' +
            'ng-options="v.value as v.label for v in yesNoOptions" ' +
            'ng-required="required"><option value="">Select</option></select>',
          scope: {
            ngModel: '=ngModel',
          },
          link: function ($scope, $element, $attrs) {
            $scope.required = $attrs.hasOwnProperty('required');
            $scope.yesNoOptions = [{
              value: true,
              label: 'Yes',
            }, {
              value: false,
              label: 'No',
            }];
          },
        };
      })
    

    这里我们不替换,但是我们得到ngModel 根本不需要控制器,只是作为一个范围值。然后我们可以在模板中使用它。我们不能使用替换,因为会有ng-model 的冲突。

    我个人更喜欢第一个选项! (并且在 dom 中少了一个元素)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-15
      • 2015-08-31
      • 2013-08-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多