【问题标题】:AngularJS simplistic directiveAngularJS 简单指令
【发布时间】:2013-10-26 15:23:33
【问题描述】:

我正在尝试为 bootstrap-select 创建一个指令:

http://silviomoreto.github.io/bootstrap-select/(它允许创建漂亮的选择)

根据以下教程,将 jquery 插件转换为 angular 指令应该很容易:http://www.youtube.com/watch?v=9Bu73oQQSio 但事实上,目前我还没有停留在第一步,我无法让最基本的功能正常工作。我尝试了不同的方法:

<select multiple selectpicker>
  <option selectpickeroption ng-repeat="MyModel in MyModels">
    {{ MyModel.MyProperty }}
  </option>
</select>

<selectpicker multiple="multiple">
    <option ng-repeat="MyModel in MyModels">
        {{ MyModel.MyProperty }}
    </option>
</selectpicker>

一起

Application.directive('selectpicker', [function () {
    return {
        restrict: 'A',
        compile: function() {
            return function(scope, element, attributes, controller) {
                element.selectpicker(); // launch `.selectpicker()` plugin
                // Lunching selectpicker at current point is way to early,
                // at this point all <option ng-repeat="MyModel in MyModels">..</option>
                // contain the following text `{{ MyModel.MyProperty }}`
            };
        }
    };
}]);

但它们都不起作用,它只是不想集成到 Angular 中。有没有一种简单的方法可以以最小的努力将这个简单的插件包装成角度?因为我花了半天时间试图让它工作

:(

【问题讨论】:

    标签: jquery angularjs bootstrap-select


    【解决方案1】:

    这是一个时间问题 - 您的 selectpicker 指令在 &lt;option&gt; 元素创建之前运行。

    要解决此问题,您需要延迟调用 element.selectpicker(),直到创建 &lt;option&gt; 元素。

    对 cme​​ts 的回应:

    @Lu4 #1:你是对的,通常在 Angular 中可以避免使用$timeout。 我已经更新了使用$watch 的答案(如果您使用的是Angular 1.2+,那么您可能想要使用$watchCollection)。

    @dluz #2:据我所知,$timeout 在这种情况下足够可靠,但更新后的答案应该可以解决对此的任何担忧。

    在这里使用document.ready() 是不合适的,因为这与加载页面的浏览器状态有关,而不是 Angular 如何与 DOM 一起工作。因为我们的 Angular 代码已经在运行,所以知道 ready 事件已经触发 - document.ready() 必须在 Angular 运行之前触发。

    Lu4 遇到的问题与 Angular 如何处理指令有关 - 它从外向内对每个元素进行操作,在移动到子元素及其指令之前初始化所有指令(为简单起见,我们忽略 terminal指令)。所以当selectpicker 指令被调用时,Angular 还没有处理子元素。

    更新答案:

    更新您的 HTML 以将 MyModels 作为您要观看的集合传递到 selectpicker 指令中。

    <select multiple selectpicker="MyModels">
        ...
    </select>
    

    然后更新指令以在selectpicker 属性的值上设置$watch

    从我的快速测试中,$watch 将在 DOM 更新后触发,您应该可以调用 selectpicker()

    myApp.directive('selectpicker', [
        return {
            function(){
                restrict: 'A',
                scope: true,
                link: function(scope, element, iAttrs, controller){
                    console.log('selectpicker::link: element=', element.eq(0).children().length);
                    
                    scope.$watchCollection(iAttrs['selectpicker'], function(newValue){
                        console.log('selectpicker::$watch: element=',  element.eq(0).children().length);
                        element.selectpicker();
                    });
                }
            };
        }]);
    

    使用这种方法,$watch 将在集合设置在范围上或替换集合时触发。在 Angular 1.2 中,您应该使用 $watchCollection,以便在数组中的项目数发生变化时通知您。


    原答案:

    要解决此问题,您需要使用$timeout 服务将您对element.selectpicker() 的呼叫延迟到下一个“滴答”。

    myApp.directive('selectpicker', ['$timeout',
        function($timeout){
            return {
                restrict: 'A',
                link: function(scope, element, iAttrs, controller){
                    // Log the number of children of the <select>.
                    // This will be 0 because Angular hasn't processed the children yet.
                    console.log('selectpicker::link: element=', element.eq(0).children().length);
                    
                    var initSelectpicker = function(){
                        // Now the children have been created.
                        console.log('selectpicker::init: element=', element.eq(0).children().length);
                        
                        element.selectpicker();
                    }
                    
                    $timeout(initSelectpicker, 0, false);
                }
            };
        }]);
    

    这应该可以让事情开始,但您必须小心 - 如果 MyModels 发生更改,那么您将必须确保 selectpicker 也保持最新。

    【讨论】:

    • 谢谢,我正在考虑使用超时,但对这种方法没有内部动机,认为可能有一些“正确”的方式,这是为这种目的使用超时的常见做法吗?
    • 感谢@Sly_cardinal 的评论。你是对的。关于$timeout这个方案,你觉得靠谱吗?!我不太喜欢通过超时来依赖延迟事件。不可能只在 document.ready() 方法或类似的方法内触发 element.selectpicker(); 以确保仅在 DOM 准备好时触发它?!
    【解决方案2】:

    您需要使用指令的link: 函数调用插件。在链接阶段,您只能在元素上注册任何侦听器并使用范围设置任何监视。 (不是编译阶段)

    如果您仔细查看您提到的 YouTube 视频教程,他会在 link: 函数中做所有事情。

     myApp.directive('selectpicker', [function () {
            return {
                restrict: 'A',
                link: function (scope, element, iAttrs, controller) { 
    
                  console.log(element);
                  element.selectpicker(); 
    
                }
    
            };
        }]);
    

    通常,如果您需要转换/更改模板的 DOM 元素,则只需使用compile 阶段。另一方面,link 函数将允许指令在特定克隆的 DOM 元素实例上注册侦听器,以及将内容从范围复制到 DOM。

    您可以在这里找到更多信息 - http://docs.angularjs.org/guide/directive

    【讨论】:

    • 其实你写的和Lu4写的是等价的。在您链接“编译函数”部分的指令文档中:“编译函数可以有一个返回值,它可以是一个函数或一个对象。返回一个(链接后)函数相当于通过'链接注册链接函数' 配置对象的属性..."
    • 谢谢 dluz,Sly_cardinal,是的,这些声明是等价的,link: function(...){...} 只是一个捷径
    猜你喜欢
    • 1970-01-01
    • 2015-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多