7

我有一个在我的 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=text, 或 aselect时, inputController, 或selectController被多次实例化。

为什么您不希望每个实例都发生 $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}}" />

@任何人:

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

4

3 回答 3

5

我认为这样做的 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 文档,然后花时间查看其他人的指令 - GitHub 上有很多可供学习的东西!

于 2013-04-14T19:06:21.820 回答
4

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

结果[图片链接]:

在此处输入图像描述

使用 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>
于 2013-04-11T17:34:07.847 回答
1

这是一个可以帮助您解决这种情况的方法...

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

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

让我知道这是否对您有帮助。

于 2013-04-12T14:31:12.810 回答