我正在构建一个新的SPA前端,以取代现有企业遗留的过时且需要更新的系统大杂烩.我是棱角分明的新人,想看看社区能否给我一些看法.我会陈述我的问题,然后问我的问题.
我必须基于来自.jsinclude的数据生成几个复选框系列,其数据如下:
$scope.fieldMappings.investmentObjectiveMap = [
{'id':"CAPITAL PRESERVATION", 'name':"Capital Preservation"},
{'id':"STABLE", 'name':"Moderate"},
{'id':"BALANCED", 'name':"Moderate Growth"},
// etc
{'id':"NONE", 'name':"None"}
];
Run Code Online (Sandbox Code Playgroud)
复选框是使用a创建的ng-repeat,如下所示:
<div ng-repeat="investmentObjective in fieldMappings.investmentObjectiveMap">
...
</div>
Run Code Online (Sandbox Code Playgroud)
但是,我需要复选框表示的值映射到不同的模型(不仅仅是2对方式绑定到fieldmappings对象).为此,我创建了一个指令,它接受一个destarray最终映射到模型的目标数组.我也知道我需要处理一些非常具体的gui控件,例如如果检查了其他任何内容则取消选中"None",或者如果其他所有内容都未选中则选中"None".此外,"无"在每组复选框中都不是一个选项,因此指令需要足够通用以接受验证函数,该函数可以checked根据已经点击的内容来调整复选框组输入的状态,但足够聪明如果没有调用选项,不要破坏"NONE".我开始通过添加一个在控制器中调用函数的ng-click来做到这一点,但是在查看堆栈溢出时,我读到人们说将DOM操作代码放在控制器中是不好的 - 它应该在指令中.所以我需要另一个指令吗?
到目前为止:(html):
<input my-checkbox-group
type="checkbox"
fieldobj="investmentObjective"
ng-click="validationfunc()"
validationfunc="clearOnNone()"
destarray="investor.investmentObjective" />
Run Code Online (Sandbox Code Playgroud)
指令代码:
.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);
}
}
});
}
};
})
Run Code Online (Sandbox Code Playgroud)
.js控制器片段:
.controller( 'SuitabilityCtrl', ['$scope', function ( $scope ) {
$scope.clearOnNone = function() {
// naughty jQuery DOM manipulation code that
// looks at checkboxes and checks/unchecks as needed
};
Run Code Online (Sandbox Code Playgroud)
上面的代码完成并正常工作,除了顽皮的jquery代码clearOnNone(),这就是我写这个问题的原因.
这是我的问题:在所有这些之后,我想我自己 - 如果我只是用我的控制器中编写的jQuery手动处理所有这些GUI逻辑和验证垃圾,我就可以完成.在编写这些复杂的指令时,未来的开发人员将不得不解决的问题在于,如果我刚刚编写了jQuery代码,我们99%的人会一目了然地理解这些指令,那么在什么时候变得愚蠢呢?其他开发者如何划清界限?
我看到整个堆栈溢出.例如,这个问题似乎可以通过十几行简单的jQuery来回答,但他选择以角度的方式来做,有一个指令和一个部分...对于一个简单的问题似乎很多工作.
我不希望这个问题违反规则,具体来说,我想我想知道:我应该如何编写检查是否已选择"无"的代码(如果它作为此组中的选项存在)复选框),然后相应地选中/取消选中其他框?一个更复杂的指令?我无法相信我是唯一一个必须实现代码的开发人员,而这些代码只是为了满足一个自以为是的框架而需要更复杂的代码.我需要使用另一个util库吗?
我按照 Jim 的建议将其发布在 Programmers.StackExchange.com 上。与此同时,我确定了一个解决方案来处理所有棘手的 DOM 操作。
我尝试了两种方法 - 在控制器中处理 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;
}
});
}
};
Run Code Online (Sandbox Code Playgroud)
(通过控制器)- 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>
Run Code Online (Sandbox Code Playgroud)
(通过控制器)-指令代码:
.directive("checkboxGroup", 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);
}
}
});
}
};
})
Run Code Online (Sandbox Code Playgroud)
然后我决定我讨厌这些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>
Run Code Online (Sandbox Code Playgroud)
(通过指令)-指令代码:
.directive("checkboxGroup", function () {
return {
restrict: "A",
scope: {
destarray: "=", // the source of all the checkbox values
fieldobj: "=", // the array the values came from
groupname: "@", // the logical name of the group of checkboxes
labelvalue: "@" // the value that corresponds to this checkbox
},
link: function (scope, elem, 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);
}
}
});
}
};
})
Run Code Online (Sandbox Code Playgroud)
回想起来,我想我更喜欢将所有代码放在一个指令中,尽管我认为它比通过 jQuery 将所有处理扔到控制器中更不直观且更复杂。它从控制器中删除了clearOnNone()函数,这意味着处理此功能的所有代码都在html标记和指令中。
我不喜欢像这样的代码this.attributes.labelvalue.nodeValue.trim(),我仍然在我的指令中使用了它。对于像我这样的场景,业务部门有某些要求(没有其他方式来表达)是乏味和麻烦的,我不知道是否真的有一种“干净”的方法来编码它。