AngularJS - 我的部分视图的控制器

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");
    });
}

当表单有多个

这个事件是广播/发射吗? - Arun P Johny
@ArunPJohny 我使用了 $rootScope.$emit('myCustomEvent')。 - Bruno
谁触发了这个事件,是在表单内还是来自其他地方? - Arun P Johny
你能分享一下触发事件的代码吗? - Arun P Johny
@ArunPJohny能否确认一下,拥有多个具有相同控制器的元素是错误的? - Bruno
显示剩余5条评论
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上有很多可以学习的!


1
我也会为每个可重复的对象使用指令。 - maxdec
@Thom,你能否解释一下这段代码中return语句的用法吗?我以前从未见过这种技巧。 - graycrow

4
以下是我如何实现带有递归字段的表单(基于此 SO 答案:https://dev59.com/vGUo5IYBdhLWcg3w7TCT#15663410)的方法:
结果[图片链接]:

enter image description here

加载 Home.html 部分并具有 ng-view 的 视图控制器:
app.controller('HomeController', ['$scope', '$http', function ($scope, $http) {
    $scope.msg = 'Home Page Message';
}]);

在Home.html中的 表单控制器(form controller):

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模板(可以针对每种类型的字段拥有一个模板,或者在主模板上使用switch case语句来处理field.type属性)

<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 的代码吗? - Bruno
这是最后一块代码!它包含了自身的递归,但你首先需要在主视图中引用它。 - jpmorin

1
这里有一个示例代码,可以帮助您解决这个场景问题...

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

这只是说明如何实现的示例。如果不知道您对表单配置数据有多少控制,就很难提供更全面的答案。另外,如果您想知道,最好在想要进行$broadcast的任何地方注入$rootScope($emit向上遍历DOM树,而$broadcast向下遍历。因此,从$rootScope进行广播将覆盖整个应用程序)。希望这能帮到您。

完美地符合我的需求,加一。 - J-Dizzle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接