【问题标题】:Chosen Angular directive doesn't fire $watch for Controller As选择的 Angular 指令不会为 Controller As 触发 $watch
【发布时间】:2015-12-20 03:52:07
【问题描述】:

我从 here 关注 Lukas Rubalke @simpulton 的工作,还找到并阅读了 this SO 帖子,以扩展他的工作。

我已经关注了这两个并显示了选择的选择框。我遇到的问题是我需要调用 'chosen:updated' 来刷新 Chosen,以便来自控制器的数据将填充选择选项(数据来自 $promise / web 服务)。

我使用一个控制器来获取数据并创建 5 个不同的选择框。 (5 个不同的过滤器列表)。我正在使用 Controller As 语法和 ui-router。

据我所知,鉴于我遵循的示例,它应该可以工作,但是

element.trigger('chosen:updated');

永远不会被调用,并且 Chosen 选择当然不会显示选项。检查 DOM 时,选项就在那里,它们是从控制器数据创建的。我只是无法让“更新”的电话开火。

这是指令:

angular.module('reporting.directive')

.directive("chosen", function () {

    var linker = function (scope, element, attr) {
        var list = attr['chosen'];

        scope.$watch(list, function () {
            element.trigger('chosen:updated');
        });


        element.chosen({
            single_backstroke_delete: false,
            search_contains: true,
            no_results_text: "Oops, nothing found!",
            width: "100%"
        });
    };

    return {
        restrict: 'A',
        link: linker
    }
})
;

和 HTML / 模板:

<div class="col-xs-12 col-md-2">
    <div class="filterboxtext">Cost Center</div>
    <select id="costcenter" class="chosen-select" chosen="selectsController.datacostcenter" data-placeholder="ALL" multiple
            ng-model="csSelect1" ng-options="cc2 as cc2 for cc2 in selectsController.datacostcenter"></select>
</div>
<div class="col-xs-12 col-md-2">
    <div class="filterboxtext">Provider Name</div>
    <select id="providername" class="chosen-select" chosen="selectsController.dataprovname" data-placeholder="ALL" multiple ng-change="$state.forceReload()"
            ng-model="csSelect2" ng-options="provname as provname for provname in selectsController.dataprovname"></select>
</div>

<SNIP> 

<div class="col-xs-12 col-md-2">
    <input id="hubmenuitemdescription" value="" />
</div>

编辑

这是控制器

angular.module('reporting.controller')

.controller('SelectsController', function (SelectDataRetrival) {
    var selectsController = this;

    selectsController.datacostcenter   = SelectDataRetrival.query({ stuff: stuff });
    selectsController.dataprovname     = SelectDataRetrival.query({ stuff: stuff2 });
})
;

编辑 2 我已经确认 $watch 在 $promise 解决之前触发了element.trigger('chosen:updated');。我有范围内的数据变量,并以我理解的方式引用它。 (下面更新代码)。

我仍然对为什么更新 optionData 时没有调用 scope.$watch('selectsController.optionData', ... 感到困惑(就像我相信它应该那样)。

更新的控制器:

angular.module('reporting.controller')

.controller('SelectsController', function (SelectDataRetrival,$q) {
    var selectsController = this;

    selectsController.optionData = [];

    var datacostcenter   = SelectDataRetrival.query({ stuff: stuff });
    var dataprovname     = SelectDataRetrival.query({ stuff: stuff2 });

    $q.all([datacostcenter.$promise, dataprovname.$promise])
            .then(function (results) {
                for (var i = 0; i < results.length; i++) {
                    selectsController.optionData.push(results[i]);
                }
                console.log("controller finished, data assigned"); 
            });
})
;

更新的 HTML:

<div class="col-xs-12 col-md-2">
    <div class="filterboxtext">Cost Center</div>
    <select id="costcenter" class="chosen-select" chosen data-placeholder="ALL" multiple
            ng-model="csSelect1" ng-options="cc2 as cc2 for cc2 in selectsController.optionData[0]"></select>
</div>
<div class="col-xs-12 col-md-2">
    <div class="filterboxtext">Provider Name</div>
    <select id="providername" class="chosen-select" chosen data-placeholder="ALL" multiple ng-change="$state.forceReload()"
            ng-model="csSelect2" ng-options="provname as provname for provname in selectsController.optionData[1]"></select>
</div>

更新指令:

angular.module('reporting.directive')

.directive("chosen", function () {

    var linker = function (scope, element, attr) {

        scope.$watch(
            function (scope) {
                return scope.selectsController.optionData
            }, function () {
                element.trigger('chosen:updated');
                console.log("fired for " + attr["id"]);
            });



        element.chosen({
            single_backstroke_delete: false,
            search_contains: true,
            no_results_text: "Oops, nothing found!",
            width: "100%"
        });
    };

    return {
        restrict: 'A',
        link: linker
    }
})
;

【问题讨论】:

  • attr['chosen'] 计算为一个永远不会改变的字符串。 list 作为原始值变量永远不会改变
  • 我添加了控制器代码以供参考。

标签: javascript angularjs


【解决方案1】:

我想出了如何让它发挥作用。我不是 100% 的原因,但我确定以下更改会导致选择的选择更新,并通过触发 $watch 由于数据/范围更改而按预期更新。

我将数据发送到隔离范围内的指令中,然后观察该变量。在将其分配给范围之前,我还必须等待承诺解决(通过 Controller As)。如果我不等待承诺,我将不会得到更新。

指令:

angular.module('reporting.directive')

.directive("chosen", function () {

    var linker = function (scope, element, attr) {

        scope.$watch("data", function () {
                element.trigger('chosen:updated');
                console.log("fired for " + attr["id"]);
            });

        element.chosen({
            single_backstroke_delete: false,
            search_contains: true,
            no_results_text: "Oops, nothing found!",
            width: "100%"
        });
    };

    return {
        restrict: 'A',
        link: linker,
        scope: {
            data: "="
        }
    }
})
;

HTML / 模板:

<div class="col-xs-12 col-md-2">
    <div class="filterboxtext">Cost Center</div>
    <select id="costcenter" class="chosen-select" chosen data-placeholder="ALL" multiple
            ng-model="csSelect1" ng-options="cc2 as cc2 for cc2 in selectsController.datacostcenter" data="selectsController.datacostcenter"></select>
</div>
<div class="col-xs-12 col-md-2">
    <div class="filterboxtext">Provider Name</div>
    <select id="providername" class="chosen-select" chosen data-placeholder="ALL" multiple ng-change="$state.forceReload()"
            ng-model="csSelect2" ng-options="provname as provname for provname in selectsController.dataprovname" data="selectsController.dataprovname"></select>
</div>

控制器:

angular.module('reporting.controller')

.controller('SelectsController', function (SelectDataRetrival,$q) {
    var selectsController = this;

    SelectDataRetrival.query({ stuff: stuff })
        .$promise.then(function (results) {
            selectsController.datacostcenter = results;
        });
    SelectDataRetrival.query({ stuff: stuff2 })
        .$promise.then(function (results) {
            selectsController.dataprovname = results;
        });

})
;

【讨论】:

    【解决方案2】:

    我正在分享一些有用的angularJS概念,请参考以下几点。

    1. Promise 在控制器调用的生命周期中只执行一次。
    2. 当我们对任何变量应用 watch 时,watch 将触发该变量的所有赋值。

    var list = attr['chosen'];

    scope.$watch(list, function () { element.trigger('选择:更新'); });

    list = attr['再次选择'];

    在这种情况下 list 被分配了 2 次,所以 watch 会触发两次。

    【讨论】:

      【解决方案3】:

      您的 var list 未绑定到 $scope 并且无法被观看。先绑定到$scope。

      var linker = function (scope, element, attr) {
          scope.list = attr['chosen'];
      
          scope.$watch("list", function () {
              element.trigger('chosen:updated');
          });
      }
      

      【讨论】:

      • 我看到了错误,看看 Lukas 的版本是如何满足它在示波器上的需要的。我对代码进行了更改,但仍然没有刷新选择框。我添加了一个 console.log 来查看 $watch 是否被调用,确实如此。那么为什么我的选择中没有选项?如果我手动调用$('#costcenter').trigger('chosen:updated'),选项就会出现。我会继续努力,欢迎任何额外的指点。
      • 似乎 $watch 在 $promise 返回之前第一次被调用,因此数组为空且没有选项。为什么在 $promise 解决后第二次没有调用 $watch ?我错过了什么?
      • @ThaigoPXP 谢谢你的帮助,你让我对范围有了更深入的思考。
      猜你喜欢
      • 2014-01-04
      • 2014-12-31
      • 1970-01-01
      • 1970-01-01
      • 2018-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多