如何以编程方式设置ng-options(可能是指令)?

时间:2014-09-16 20:38:02

标签: angularjs angularjs-directive

我有一个select元素用于创建时区列表。这种简单的角度方式是:

<select ng-model="item.timezone" ng-options="timezone for timezone in timezones"></select>

然后确保控制器已将$scope.timezones设置为时区字符串数组。

此选择出现在很多地方,我不希望每个控制器都必须加载它。所以我转向指令:

<select ng-model="item.timezone" timezones="true"></select>

然后使用指令渲染各种选项:

    .directive('timezones',function () {
    return {
        restrict: 'A',
        require: '?^ngModel',
        link: function ($scope,element,attrs,ngModel) {
            element.empty();
            _.each(moment.tz.names(),function (name) {
                element.append('<option value="'+name+'">'+name+'</option>');
            });
    // this formatter does nothing, is just there so I can be sure it is being called with the correct value
    ngModel.$formatters.push(function(modelValue){
        return(modelValue);
    });             
        }
    };
})

问题是angular现在使用我的“timezones”指令处理角度“select”指令来处理它。这导致我对ng-model的设置被完全忽略,并且select的值设置为“”。

如果我在指令中执行此操作,如何识别模型值并选择正确的元素,或者有更好的方法来执行此操作?

更新:

我尝试在范围内设置timezones并使用该指令在元素上设置ng-options,但它仍为空白:

    .directive('timezones',function () {
    return {
        restrict: 'A',
        require: '?^ngModel',
        link: function ($scope,element,attrs,ngModel) {
            $scope.timezones = moment.tz.names();
            element.attr("ng-options","timezone for timezone in timezones");
        }
    };
})

在这种情况下,我得到一个空的选择,只有一个空白选择。

4 个答案:

答案 0 :(得分:1)

在我自己的指令上,我一直在努力解决这个问题,我终于解决了这个问题。

我知道解决方案是使用“子属性指令”的编译功能,因为它在编译“父元素指令”之前被触发,但我专注于将ngOptions属性添加到tElement,没有意识到所有指令编译函数之间共享tAttrs

所以我认为你可以解决它如下:

.directive('timezones', function() {
    return {
        controller: function($scope) {
            $scope.timezones = moment.tz.names();
        },
        compile: function (tElement, tAttrs) {
            var ngOptions = 'timezone for timezone in timezones';
            tAttrs.ngOptions = ngOptions;
        },
        restrict: 'A'
    };

答案 1 :(得分:0)

@deitch:尝试将指令中的选择部分作为&#34;模板&#34;,并使用&#39;隔离范围&#39;配置指令。将时区传递到指令的范围。

然后你的指令看起来像这样:

&#13;
&#13;
<timezone zone="item.timezone"></timezone>
&#13;
&#13;
&#13;

我写了一个例子作为一个掠夺者:http://plnkr.co/edit/7Ysn8K0Wwvo8CbtkMhVv?p=preview

&#13;
&#13;
  .directive('timezone', function() {
    return {
      restrict: 'A',
      require: 'ngModel',
      scope: {
        myTimezone: '=',
        allTimezones: '@allTimezones'
      },
      template: '<p>Directive timezone: {{myTimezone}}</p>' +
        '<p>{{ allTimezones }}</p>' +
        '<select ng-model="myTimezone" ng-options="timezone for timezone in {{allTimezones}}"></select>'
    };
  });
&#13;
<div data-timezone data-my-timezone="data.myTimezone" data-all-timezones="{{timezones}}"></div>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

我最终无法解决它,所以我完全传递了select指令,并使用编译阶段生成我自己的选项:

    .directive('timezones',function () {
    return {
        restrict: 'A',
        require: '?^ngModel',
        compile: function (element,attrs) {
            _.each(moment.tz.names(),function (name) {
                element.append('<option value="'+name+'">'+name+'</option>');
            });
        }
    };
})

它只是有效。

答案 3 :(得分:0)

您需要做的是使用隔离范围创建指令并将模型作为binding传递。您可以在指令模板中创建select,并在指令范围内使用时区。

.directive('timezonesdir', function (timeZonesFactory) {
  return {
    restrict: 'AE',
    template: '<select ng-model="timezone" ng-options="timezone for timezone in timezones"></select>',
    scope: {
        timezone: '='
    },
    link: function (scope) {
        scope.timezones = // timezones
    }
};

如您所说,如果您将此指令用作select元素的属性,则此方法无效,您必须在span或div或其他内容中使用它。

<span timezone="item.timezone" timezonesdir></span>

我已经为你创建了一个jsfiddle示例。

http://jsfiddle.net/limowankenobi/xo4r9q3j/

我为时区添加了一个工厂,将它们从指令中解耦。