指令未使用 controllerAs 和 bindToController 使用异步数据更新视图

Posted

技术标签:

【中文标题】指令未使用 controllerAs 和 bindToController 使用异步数据更新视图【英文标题】:Directive is not updating the view with async data, using controllerAs and bindToController 【发布时间】:2015-07-29 23:42:29 【问题描述】:

我在获取更新视图的指令时遇到了一些问题。

在我的控制器中,我为<tiki-list> 指令的属性设置了初始值。然后,2 秒后,我将更新 vm.listObjectSelected 以测试其异步行为。

但是,视图并未反映更新。

控制器

    var listObject = ["display":"display1", "value":"value1", "display":"display2", "value":"value2", "display":"display3", "value":"value3"]

    vm.listObject = listObject
    vm.listObjectSelected = ["display":"display1", "value":"value1"]

    $timeout(function()

        vm.listObjectSelected = ["display":"display1", "value":"value1", "display":"display3", "value":"value3"]

    , 2000)

HTML

<tiki-list max="" type="multi" list="editController.listObject" selected="editController.listObjectSelected"></tiki-list>

指令

(function()

    'use strict';

    angular.module("tiki").directive("tikiList", tikiList)

    function tikiList(helper)

        var directive = 

            restrict:"EA",
            scope:

                list: "=", //the object to repeat over, this contains 2 array's
                retunObject: "=", //the array that is outputted
                selected: "=", //preselected values
                max: "=", //maximum range, other elements are greyed out, starts at 0
                title:"@title", //the title of this list
                type:"@type", //[single, multi]

            ,
            templateUrl:"js/directive/list.html",
            link:link,
            bindToController: true,
            controllerAs:"vm",
            controller:controller

        

        return directive

        function link(scope, el, attr, ctrl)

            scope.vm.onClick = onClick

            // preprocess the "list" if there is a "selected" attribute
            // the "selected" attribute is an object that contains the selected items
            // return a "selectedItems" array containing the indeces of matching display names
            // add the .active property to the "list" object to the correct indeces

            if(scope.vm.selected)

                var selectedItems = helper.isItemInList(helper.createArrayFromProperty(scope.vm.selected, "display"), helper.createArrayFromProperty(scope.vm.list, "display"))

                for(var i = 0; i < selectedItems.length; i++)

                    scope.vm.list[selectedItems[i]].active = true

                

            

            // add the property .disabled to the "list" if there is a max attribute
            // the property will be added to all indeces that exceed the max value

            if(scope.vm.max)

                for(var y = 0; y < scope.vm.list.length; y++)

                    if(y >= scope.vm.max)

                        scope.vm.list[y].disabled = true

                    

                

            

            function onClick(index)

                // only allow items that are in range of the "max" attribute are allowed to be clicked

                if(!scope.vm.max || index < scope.vm.max)

                    if(scope.vm.type === "single")

                        angular.forEach(scope.vm.list, function(val, key)

                            scope.vm.list[key].active = false

                        )

                        scope.vm.list[index].active = true

                    

                    if(scope.vm.type === "multi")

                        scope.vm.list[index].active = !scope.vm.list[index].active

                    

                

            

            scope.vm.listing = scope.vm.list

        


    

    controller.$inject = [];

    function controller()




    


)()

指令模板

  <ul class="listOptions">
    <li class="listOptions-title" ng-class="'show':title">vm.title</li>
    <li ng-click="vm.onClick($index)" ng-class="'active':list.active, 'disabled':list.disabled" ng-repeat="list in vm.listing track by $index">list.display</li>
  </ul>

我认为它与 controllerAs 有关,但我无法理解它。

谢谢,

【问题讨论】:

你为什么总是使用scope.vm?如果您将controllerAsbindToController 结合使用,则可以直接访问this 对象上的隔离范围属性,例如this.selected。使用指令定义的bindToController 属性,您的隔离作用域的所有属性都会自动绑定到控制器而不是作用域。无需使用scope.vm 我正在使用 scope.vm,因为我正在尝试遵循此样式指南:github.com/johnpapa/angular-styleguide#directives 您在$timeout 内所做的事情是在角度摘要循环之外。您需要在vm.listObjectSelected = ... 之后在$timout 内调用$scope.$digest() 我认为因为在您的 指令 中,当您将控制器 listObject 设置为新数组时,scope.vm.listingscope.vm.list 不会指向相同的引用。因此,尽管scope.vm.list 已更新,但您的 scope.vm.listing 不会更新。只需将您的ng-repeat 设置为使用您的scope.vm.list 而不是scope.vm.listing,它应该可以工作。 Working fiddle 我误解了您的控制器中的listObjectSelectedlistObject。您应该使用$watch 来注意listObjectSelected 的变化。 Working fiddle. 【参考方案1】:

我认为原因是 Array 是一个引用类型。当您在服务或异步步骤中更改数据时,数据点会转到内存中的新位置,但指令或控制器中的数据不会更改。

不要像这样写你的函数:

$timeout(function()

    vm.listObjectSelected = ["display":"display1", "value":"value1", "display":"display3", "value":"value3"]

, 2000)

你应该尝试这样做:

$(timeout(function()vm.listObjectSelected.push(you need data here),200)

或者你可以使用一个promise,你可以返回一个promise并在指令中获取它,使用

promise.then(function()//let data = service.data again

希望对你有帮助。

【讨论】:

以上是关于指令未使用 controllerAs 和 bindToController 使用异步数据更新视图的主要内容,如果未能解决你的问题,请参考以下文章

带有 ControllerAs 和 TypeScript 类的 AngularJs 指令

使用 ControllerAs 时出现 Angular 嵌套指令的问题

AngularJS ControllerAs 语法和控制器注入变量

Vue指令:v-bind动态属性绑定

第六节:Vue指令:v-bind动态属性绑定

Vue 自定义指令 - 对象属性未定义