我必须根据来自.js include的数据生成几个复选框系列,其数据如下:
$scope.fieldMappings.investmentObjectiveMap = [ {'id':"CAPITAL PRESERVATION",'name':"Capital Preservation"},{'id':"STABLE",'name':"Moderate"},{'id':"BALANCED",'name':"Moderate Growth"},// etc {'id':"NONE",'name':"None"} ];
使用ng-repeat创建复选框,如下所示:
<div ng-repeat="investmentObjective in fieldMappings.investmentObjectiveMap"> ... </div>
但是,我需要复选框表示的值映射到不同的模型(不仅仅是双向绑定到fieldmappings对象).为了实现这一点,我创建了一个指令,它接受目标数组destarray,最终映射到模型.我也知道我需要处理一些非常具体的gui控件,例如如果检查了其他任何内容则取消选中“None”,或者如果其他所有内容都未选中则选中“None”.此外,“无”在每组复选框中都不是一个选项,因此指令需要足够通用以接受验证功能,该功能可以根据已经点击的内容调整复选框组输入的选中状态,但是智能如果没有名为“NONE”的选项,就不会破坏.我开始通过添加一个在控制器中调用函数的ng-click来做到这一点,但是在查看堆栈溢出时,我读到人们说将DOM操作代码放在控制器中是不好的 – 它应该在指令中.那我需要另一个指令吗?
至今:
(HTML):
<input my-checkBox-group type="checkBox" fieldobj="investmentObjective" ng-click="validationfunc()" validationfunc="clearOnNone()" destarray="investor.investmentObjective" />
指令代码:
.directive("myCheckBoxGroup",function () { return { restrict: "A",scope: { destarray: "=",// the source of all the checkBox values fieldobj: "=",// the array the values came from validationfunc: "&" // the function to be called for validation (optional) },link: function (scope,elem,attrs) { if (scope.destarray.indexOf(scope.fieldobj.id) !== -1) { elem[0].checked = true; } elem.bind('click',function () { var index = scope.destarray.indexOf(scope.fieldobj.id); if (elem[0].checked) { if (index === -1) { scope.destarray.push(scope.fieldobj.id); } } else { if (index !== -1) { scope.destarray.splice(index,1); } } }); } }; })
.js控制器片段:
.controller( 'SuitabilityCtrl',['$scope',function ( $scope ) { $scope.clearOnNone = function() { // naughty jQuery DOM manipulation code that // looks at checkBoxes and checks/unchecks as needed };
上面的代码完成并正常工作,除了clearOnNone()中的顽皮jquery代码,这就是我写这个问题的原因.
这是我的问题:在所有这些之后,我想我自己 – 如果我只是用我的控制器中编写的jQuery手动处理所有这些GUI逻辑和验证垃圾,我就可以完成.在编写这些复杂的指令时,未来的开发人员将不得不解决的问题在于,如果我刚刚编写了jQuery代码,我们99%的人会一目了然地理解它,那么在什么时候变得愚蠢呢?其他开发者如何划清界限?
我看到整个堆栈溢出.例如,this question似乎可以用十几行简单的jQuery来回答,但是他选择了角度方式,指令和部分……对于一个简单的问题似乎需要做很多工作.
我不希望这个问题违反规则,所以具体来说,我想我想知道:我应该如何编写代码来检查是否选择了“无”(如果它作为选项存在于此组中复选框),然后相应地选中/取消选中其他框?更复杂的指令?我无法相信我是唯一一个必须实现代码的开发人员,而这些代码只是为了满足一个固定的框架而需要更复杂的代码.我需要使用另一个util库吗?
我尝试了两种方法 – 在控制器中处理DOM事件,并通过指令处理它:
(通过控制器) – .js代码:
$scope.clearOnNone = function(groupName,$event) { var chkBoxArr = $('input[name^=' + groupName + ']'),nonNoneValChecked = false,targetElem = null,labelText = ""; // get the target of the click event by looking at the <label> sibling's text targetElem = event.target.nextElementSibling.textContent.trim(); // if target was the None option,uncheck all others if (targetElem === "None") { chkBoxArr.each(function() { labelText = this.nextElementSibling.textContent.trim(); if (labelText !== "None") { this.checked = false; } }); } // if the target was anything BUT the None option,uncheck None else { chkBoxArr.each(function() { labelText = this.nextElementSibling.textContent.trim(); if (labelText === "None") { this.checked = false; } }); } };
(通过控制器) – HTML代码:
<div ng-repeat="investmentObjective in fieldMappings.secondaryInvestmentObjectiveMap"> <input checkBox-group type="checkBox" name="secondaryInvestmentObjective" ng-click="validationfunc('secondaryInvestmentObjective',$event)" validationfunc="clearOnNone('secondaryInvestmentObjective',$event)" fieldobj="investmentObjective" destarray="suitabilityHolder.suitability.secondaryInvestmentObjective" /> <label class="checkBox-label" popover-title="{{investmentObjective.name}}" popover="{{investmentObjective.help}}" popover-trigger="mouseenter">{{investmentObjective.name}} </label> </div>
(通过控制器) – 指令代码:
.directive("checkBoxGroup",1); } } }); } }; })
然后我决定讨厌event.target.nextElementSibling.textContent.trim()行…我觉得我应该仔细检查所有这些方法是否存在,或者使用try / catch.所以我重写了指令以包含来自控制器的逻辑:
(通过指令) – HTML代码:
<div ng-repeat="otherInvestment in fieldMappings.otherInvestmentsMap"> <input type="checkBox" checkBox-group groupname="otherInvestment" labelvalue="{{otherInvestment.name}}" fieldobj="otherInvestment" destarray="suitabilityHolder.suitability.otherInvestment" /> <label class="checkBox-label" popover-title="{{otherInvestment.name}}" popover="{{otherInvestment.help}}" popover-trigger="mouseenter">{{otherInvestment.name}} </label> </div>
(通过指令) – 指令代码:
.directive("checkBoxGroup",// the array the values came from groupname: "@",// the logical name of the group of checkBoxes labelvalue: "@" // the value that corresponds to this checkBox },attrs) { // Determine initial checked Boxes // if the fieldobj.id exists in the destarray,check this checkBox if (scope.destarray.indexOf(scope.fieldobj.id) !== -1) { elem[0].checked = true; } // Update array on click elem.bind('click',function () { // store the index where the fieldobj.id exists in the destarray var index = scope.destarray.indexOf(scope.fieldobj.id),// get the array of checkBoxes that form this checkBox group chkBoxArr = $('input[groupname^=' + scope.groupname + ']'); // Add if checked if (elem[0].checked) { if (scope.labelvalue === "None") { // loop through checkBoxes and uncheck all the ones that are not "None" chkBoxArr.each(function() { // have to noodle through the checkBox DOM element to get at its attribute list // - is there a cleaner way? var tmpLabelValue = this.attributes.labelvalue.nodeValue.trim(); if (tmpLabelValue !== "None") { this.checked = false; } }); } // if the target was anything BUT the None option,uncheck None else { chkBoxArr.each(function() { var tmpLabelValue = this.attributes.labelvalue.nodeValue.trim(); if (tmpLabelValue === "None") { this.checked = false; } }); } if (index === -1) { // add the id to the end of the dest array // **will not maintain original order if several are unchecked then rechecked** scope.destarray.push(scope.fieldobj.id); } } // Remove if unchecked else { if (index !== -1) { scope.destarray.splice(index,1); } } }); } }; })
回想起来,我想我更喜欢在指令中包含所有代码,尽管我认为它比通过jQuery在控制器中抛出所有处理更不直观和复杂.它从控制器中删除了clearOnNone()函数,这意味着在html标记和指令中处理此功能的所有代码.
我不喜欢this.attributes.labelvalue.nodeValue.trim()这样的代码,我仍然在我的指令中结束了.对于像我这样的场景,业务部门有一定的要求(没有其他方式可以放置它)既繁琐又繁琐,我不知道真的有一种“干净”的方式来编写它.