【问题标题】:AngularJS - Controller of my partialAngularJS - 我的部分控制器
【发布时间】:2013-03-31 00:37:25
【问题描述】:

我有一个在我的 DOM 中多次追加的模板。

<div ng-controller="theController">
    content does not matter
</div>

所以控制器被实例化了很多次。 这是一个问题,因为如果我在控制器中放置一个观察者

theController = function($scope) {
    $scope.$on('myVar', function() {
        // run one time for each time the template is repeated
    })
}

关于如何避免这种情况的任何想法? 提前致谢。

更新

好的,我会尽量说清楚。

也许我有一个表单,它是根据异步请求的响应动态构建的。

<form ng-controller="formController">
    <div ng-repeat="f in fields">
        <ng-inclide src="f.fields"></ng-include>
    </div>
</form>

控制器类似于:

function formController($scope) {
    $scope.fields = [{ template:'', ... }];
    // data come from an ajax request... 
    // here are static for the sake of simplicity.
}

所以我不知道表单中会附加哪些字段。

表单字段结构存储在 html 部分中...类似于:

<div ng-controller="inputController">
    <label> .... </label>
    <input type="text" .... />
</div>

<div ng-controller="selectController">
    <label> .... </label>
    <select>
        ....
    </select>
</div>

function selectController($scope){
    $scope.$on("myCustomEvent", function(event) {
        cionsole.info("Options were updated");
    });
}

当表单有多个input type=textselect 时,inputControllerselectController 会被多次实例化。

为什么不希望每个实例都发生 $watch?

当特定事件发生时,我想更新页面中选择之一的选项。

我得到的是更新页面中的所有选择。

从评论中,我了解到在同一页面中拥有更多具有相同控制器的元素是错误的。 所以在我看来,目前唯一可用的解决方案是避免为表单的每个元素定义一个控制器,对吧?

更新 2

$emitinputController中使用:

function inputController() {
    $scope.fireclick = function(p) {
        if (p == 'specificInput') {
            /* this is a temporary work around 
            I used to bind the event only with a specific field */

            $scope.$emit("myCustomEvent");      
        }
    }
}

这是html部分中使用的输入字段的完整代码:

<input type="text" ng-click="fireclick(f.name);" name="{{f.name}}" />

@任何人:

至少可以确认(并最终说明原因)在同一个页面上使用同一个控制器有更多元素是错误的

【问题讨论】:

  • 控制器实例不能(也不应该)在多个元素之间共享。你想做什么?为什么你希望$watch 出现在每个实例上?控制器本质上是您在 DOM 中使用它们的地方的本地 - 它们不是应用程序范围的 - 所以如果多次触发 $watch 是一个问题,那么您可能做错了什么。
  • 是广播/发射事件
  • @ArunPJohny 我使用了 $rootScope.$emit('myCustomEvent')
  • 谁在触发这个事件,是在表单中还是从其他地方
  • 你能分享触发事件的代码吗

标签: javascript templates model-view-controller controller angularjs


【解决方案1】:

我认为 Angular 的做法是使用指令。我会在您的主视图的 ng-repeat 中执行类似 ng-switch 的操作,并让 ng-switch 仅包含适当的指令...假设存在“input-text”指令和“input-dropdown”指令:

<div ng-swtich on="field.type" ng-repeat="field in fields">
    <div ng-switch-when="text"><input-text ...></div>
    <div ng-switch-when="dropdown"><input-dropdown ...></div>
</div>

相信这样你就不会遇到和现在一样的问题了。我实际上并没有设置你想要做什么,但我 99% 确定你应该使用指令!它们非常适合您正在做的事情,并且可重复使用。

我使用http://angularlist.com 上的指令来处理收视率,我可以自信地说,当我在页面上有多个收视率时它们不会交叉 - 也就是说,我没有在那里看任何东西,只是回应到事件……其实,让我测试一些东西(测试…………)是的!我在我的评级指令正在编辑的模型值中添加了一个手表,当点击一个评级时,只有一个观察者被触发 - 有问题的控制器的那个。这不在现场,只是我在家里的开发服务器,但如果它对您有帮助,这里是指令:

app.directive("angularStars", function() {
  return {
    restrict: 'E',
    scope: {
      model: '=ngModel',
      notifyId: '=notifyId'
    },
    replace: true,
    transclude: true,
    template: '<div><ol class="angular-stars">' + '<li ng-class="{active:model>0,over:over>0}">1</li>' + '<li ng-class="{active:model>1,over:over>1}">2</li>' + '<li ng-class="{active:model>2,over:over>2}">3</li>' + '<li ng-class="{active:model>3,over:over>3}">4</li>' + '<li ng-class="{active:model>4,over:over>4}">5</li>' + '</ol></div>',
    controller: function($scope, $attrs, $http) {
      $scope.over = 0;

      // TEST WATCH
      $scope.$watch('model', function() {
        console.log('modelChange', $scope.model);
      });

      $scope.setRating = function(rating) {
        $scope.model = rating;
        $scope.$apply();
        if ($attrs.notifyUrl !== void 0 && $scope.notifyId) {
          return $http.post($attrs.notifyUrl, {
            id: $scope.notifyId,
            rating: rating
          }).error(function(data) {
            if (typeof data === 'string') {
              alert(data);
            }
            return $scope.model = 0;
          });
        }
      };
      return $scope.setOver = function(n) {
        $scope.over = n;
        return $scope.$apply();
      };
    },
    link: function(scope, iElem, iAttrs) {
      if (iAttrs.notifyUrl !== void 0) {
        return angular.forEach(iElem.children(), function(ol) {
          return angular.forEach(ol.children, function(li) {
            li.addEventListener('mouseover', function() {
              return scope.setOver(parseInt(li.innerHTML));
            });
            li.addEventListener('mouseout', function() {
              return scope.setOver(0);
            });
            return li.addEventListener('click', function() {
              return scope.setRating(parseInt(li.innerHTML));
            });
          });
        });
      }
    }
  };
});

指令很难理解 - 我仍然主要是在黑暗中与它们一起拍摄,尝试了解它们是如何工作的 - 但毫无疑问 - 你需要的力量在指令中。我强烈建议阅读AngularJS docs on writting directives,然后花时间查看其他人的指令 - GitHub 上有很多可供学习的内容!

【讨论】:

  • 我也会对每个可重复的对象使用指令。
  • @Thom,你能解释一下这段代码中 return 语句的用法吗?我以前从未见过这种技术。
【解决方案2】:

这里有一个可以帮助你解决这个场景的问题......

http://plnkr.co/edit/RvFrmPd4rlAM8aeFSwg7?p=preview

这只是说明如何实现这一点。在不知道您对表单配置数据有多少控制权的情况下,实际上不可能提供更全面的答案。如果你想知道,最好在任何你想做 $broadcast 的地方注入 $rootScope ($emit 上升到 DOM 树,而 $broadcast 下降.. 所以 $rootScope 中的 $broadcasting 将到达整个应用程序。)

如果这对你有帮助,请告诉我。

【讨论】:

  • 绝对是我想要的,+1
【解决方案3】:

这是我如何设法使用递归字段制作表单(基于此 SO 答案:https://stackoverflow.com/a/15663410/1036025

结果 [image link]:

视图控制器使用 ng-view 加载 Home.html 部分:

app.controller('HomeController', ['$scope', '$http', function ($scope, $http) {
    $scope.msg = 'Home Page Message';
}]);

Home.html 中的表单控制器

app.controller('NestedFormCtrl', ['$scope', function ($scope) {
    $scope.formData = [
        {label:'First Name', type:'text', required:'true'},
        {label:'Last Name', type:'text', required:'true'},
        {label:'Coffee Preference', type:'dropdown', options: ["HiTest", "Dunkin", "Decaf"]},
        {label: 'Address', type:'group', Fields:[
            {label:'Street1', type:'text', required:'true'},
            {label:'City', type:'text', required:'true'},
            {label:'State', type:'dropdown',  options: ["California", "New York", "Florida"]}
        ]}
    ];

    $scope.$watch('formData[3].Fields[1].label', function(newval, oldval) {
        if (oldval !== newval) {
            console.log('watch', oldval, newval);
        }
    });

    // this was added after and is not shown in the image
    $scope.$watch('formData', function(newval, oldval) {
        if (oldval !== newval) {
            console.log('watch', oldval, newval);
        }
    }, true);

    $scope.changefield = function() {
        $scope.formData[3].Fields[1].label = 'Postal Code';
    }

    $scope.customevent = function(field) {
        var type = field.type;
        // do something for this type
        console.log('customevent', field);
    };
}]);

Home 部分视图(这里 ng-include 中的模板路径可以是您的字段的属性,或者您可以使用 switch case 并显示您选择的输入/选择:

<h1>{{msg}}</h1>
<ul ng-controller="NestedFormCtrl">
    <li><button ng-click="changefield()">Change</button></li>
    <li ng-repeat="field in formData" ng-include="'views/field.html'"></li>
</ul>

field.html 模板(每种类型的字段都有一个模板,或者在 field.type 属性上有一个带有 switch case 的主模板)

<button ng-click="customevent(field)">{{field.label}}</button>
<ul>
    <li ng-repeat="field in field.Fields" ng-include="'views/field.html'"></li>
</ul>

【讨论】:

  • 你好,谢谢你的回答……你能分享一下views/field.html的代码吗?
  • 这是最后一段代码!它包含自身以进行递归,但您首先将其包含在主视图中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多