【问题标题】:AngularJS $watch only firing on intitializeAngularJS $watch 仅在初始化时触发
【发布时间】:2017-03-09 10:58:27
【问题描述】:

我有一个 Angular 应用程序,它使用 d3 来显示项目,并且有一个 infoPanel 显示被点击项目的信息。这是可行的,但单击该项目以激活/打开信息面板不起作用。

在d3数据控制文件中,我有:

'use strict';

var settings ;


angular.module('App')
    .directive('data', ['sessionmgmt', 'thedata', 'gui', function (session, data, gui) {
        return {
            link: function postLink(scope, element, attrs) {

                var promise = angular....getData...;
                promise.then(function () {

                    settings = session.settings;

                    svg = ...

                    createItems(svg, items);
                }, function () {});
            }
        };
  }]);

function createItems(svg, items) {


    let item = svg.append("g")
        .attr("class", "nodes")
        .selectAll(".node")
        .data(items, function (d) {
            return d.id;
        });


    appendIcons(item);

}

function appendIcons(nodes){
  nodes = nodes.enter().append("g")
    .attr("class", "node");

    nodes.append("title")
      .text(function (d) {
          return d.data['label'];
      });

  nodes.on("click",function(d){
    if(settings.selectedItem === d.id){
      settings.selectedItem = null;
    }else{
      settings.selectedItem = d.id;
    }

  });

}

然后在我的 infoPanel 控制器中,我有:

'use strict';

angular.module('App')
  .controller('ContentpanelCtrl', ['$scope', 'sessionmgmt', function ($scope, sessionmgmt) {
    var contentPanel = this;

    this.settings = sessionmgmt.settings;

    $scope.activate = function activate() {
      contentPanel.settings.contentPanelOpen = true;
    };


    $scope.getSelected = function getSelected(){
      return contentPanel.settings.itemSelected;
    } 

    $scope.isNodeSelected = function isSelected(){
      return contentPanel.settings.itemSelected !== null;
    } 

    $scope.$watch("contentPanel.settings.itemSelected", function(newVal, oldVal) {
      console.log(newVal);
      if (newVal !== oldVal){
        $scope.activate();
      }
    });


  }]);

第一次加载页面时,console.log 正在运行,但是当我点击这些项目时,这个手表没有被触发。 我知道“点击”正在工作,因为 getSelected 在我的信息面板上显示了正确的数据。 'watch' 和 getSelected 都使用完全相同的设置变量....难住了。

【问题讨论】:

  • 在您的控制器中添加点击功能,并在您的 html 中为指向该功能的项目添加 ng-click。当你在外面使用 jQuery 进行操作时,我认为 Angular 并没有意识到这种变化。
  • 您是否尝试过将作用域传递给createItems,然后是appendIcons 函数并在点击事件后调用scope.$apply()
  • @Razzildinho - 这真是个好建议!谢谢你。我仍然没有让 $watch 函数工作,但不需要,因为我的 click 函数现在运行 contentPanel.settings.contentPanelOpen = true; 并且 scope.$apply 使其可见。
  • 如果您将其作为答案发布,我会将其标记为已接受。

标签: javascript angularjs d3.js


【解决方案1】:

有两种方法可以做到这一点。第一个是我在评论中建议的,您将指令的范围作为参数传递给在点击事件中调用scope.$apply();createItemsappendIcons 函数。

来自指令:

createItems(svg, items, scope);

以及其他功能:

function createItems(svg, items, scope) {

    let item = svg.append("g")
       /* ... */

    appendIcons(item, scope);

}

function appendIcons(nodes, scope){
    nodes = nodes.enter().append("g")
        /* ... */

    nodes.on("click",function(d){
        if(settings.selectedItem === d.id){
            settings.selectedItem = null;
        }else{
            settings.selectedItem = d.id;
        }
        scope.$apply();
    });

}

另一种方法是更改​​您的$scope.$watch() 函数。您要注意的contentPanel.settings.itemSelected 不是范围的一部分,因为您使用的是this 而不是$scope,我假设它是controllerAs 的一部分。要让手表正常工作,您需要使用一个函数作为手表函数中的第一个参数,如下所示:

$scope.$watch(function(){
    return contentPanel.settings.itemSelected;
}, function(newVal, oldVal) {
    if (newVal !== oldVal){
        $scope.activate();
    }
});

【讨论】:

    【解决方案2】:

    作为对我评论的补充。

    在控制器中:

    $scope.toggleSelectedItem = function(item) {
       if(contentPanel.settings.itemSelected && 
          contentPanel.settings.itemSelected === item.id){
         contentPanel.settings.itemSelected = null;
       } 
       else {
         contentPanel.settings.itemSelected = item.id;
       }
    }
    

    HTML:

    <div ng-repeat="item in items" ng-click="toggleSelectedItem(item)">
       <!--Content-->
    </div>
    

    【讨论】:

    • 谢谢。我的点击功能是在数据控制指令文件中定义的。点击函数无权访问 $scope 变量
    • 你能发布整个指令吗?
    • 我现在已经在我的问题中包含了大部分指令文件。
    【解决方案3】:

    也许您可以尝试将确切的对象而不是字符串传递给观察者?

    $scope.$watch(function() {
      if (contentPanel && contentPanel.settings) {
        return contentPanel.settings.itemSelected;
      }
    }, function(newVal, oldVal) {
      console.log(newVal);
      if (newVal !== oldVal){
        $scope.activate();
      }
    });
    

    【讨论】:

    • 感谢您的建议。您推荐的第一个功能无权访问 contentPanel ... 我把 console.log(contentPanel.graphSettings);在那个函数中,它输出 undefined。
    • @FrazerKirkman 从你更新的代码来看,这个函数应该可以工作,为了安全我已经更新了空检查
    • 我不确定你的意思:(
    猜你喜欢
    • 2021-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多