我有一组对象,用于使用 ng-repeat 呈现单选按钮列表。我的问题是如何绑定每个问题的选定值并将它们绑定到某种类型的模型。这是我到目前为止的 plunker,http ://plnkr.co/edit/oXr6Un?p=preview但代码只是循环通过单选按钮列表并获取选定的值。我真的希望在用户选择答案时绑定该对象并更改其属性。我怎样才能做到这一点?请理解我对 Angularjs 很陌生。谢谢大家,ck
3 回答
就像上面的@Max 所说,您需要在输入上设置 ng-model ......但这并不是您需要做的全部。 我已经分叉了你的 plunk 并对其进行了更新,以向你展示一些有用的东西
需要注意的几点:
- 您不需要“showAnswers”服务,并且在使用 Angular 时,您永远不必通过 JQuery 从输入中获取值。
- 您需要在输入中添加 ng-model,但您需要
$parent.answer
在 ng-model 中使用,因为输入位于ng-repeat
具有自己的子范围的 an 内部。 - 您不需要
for=""
包装他们正在标记的输入的标签。
重要变化:
<li ng-repeat="answer in question.answers">
<label>
<input type="radio" ng-model="$parent.question.selectedAnswer"
value="{{ answer.answerText }}" name="quest_{{$parent.$index}}_answers" />{{ answer.answerText }}</label>
</li>
和
$scope.selectedAnswers = function(){
var answers = [];
angular.forEach($scope.questions, function(question) {
answers.push(question.selectedAnswer);
});
return answers;
};
我希望这会有所帮助。
您必须在输入(http://docs.angularjs.org/api/ng.directive:input.radio)上设置 ng-model。这样,您将获得两种方式的数据绑定。
以下是我将如何编写指令版本:http ://plnkr.co/edit/Otikw0fX8rwx2gM8DMMl?p=preview
我简化了模型。每个问题及其可能的答案如下所示:
{ question: "Why is the sky blue?",
answerChoices: [ "blah blah 1", "Rayleigh scattering", "blah blah 3" ] },
对于每个这样的对象,我们将通过单选按钮动态添加一个“答案”元素。稍后再谈。
我精简了工厂。它只是返回一个对象数组:
.factory('questionFactory', function() {
return [
{ question: ...
控制器现在可以简单地执行此操作:
$scope.questions_and_answers = questionFactory;
现在我们定义了模型。接下来,我想考虑一下我需要哪些指令,以及每个指令的模板和行为是什么。我保留了你的 3 个指令——测验、问题、答案——但我将答案重命名为 possibleAnswer。
作为个人选择,我认为让“测验”指令包含问题和答案的主要结构会更好,因此我将答案的 ng-repeat 从问题指令的模板中移到了测验中指令的模板。我还留下了 quiz 一个元素,但我将 question 和 possibleAnswer 更改为属性,因为我个人喜欢看到更熟悉的标记结构,例如
<li ng-repeat ...> 此处的东西 </li>
而不是不熟悉的
<li answer ng -重复...></li>
.directive('quiz', function() {
...
template:'<ul class="unstyled">' +
'<li ng-repeat="item in questionsAndAnswers">' +
'<span question="{{item.question}}"></span>' +
'<ul class="unstyled">' +
'<li ng-repeat="possibleAnswer in item.answerChoices">' +
'<span possible-answer="{{possibleAnswer}}" item=item id="{{$parent.$index}}"></span>' +
'</li>' +
'</ul>' +
'</li></ul>'
问题指令模板现在只处理问题:
template:'<strong class="question">{{ question }}</strong>'
而 possibleAnswer 指令只处理一个可能的答案(就像你已经拥有的那样):
template: '<label><input type="radio" ng-value="possibleAnswer"' +
' ng-model="item.answer" name="q{{id}}" />{{possibleAnswer}}</label>'
这些模板中发生了很多事情,为了理解我所做的,我们现在需要讨论指令范围继承。我对所有指令都使用了隔离范围——即scope: { ... }
——因为这通常是可重用组件的最佳选择。(其他选择见这里)问题和答案选择不会被指令修改,所以我们不需要2路绑定,只需要单路绑定(父作用域->指令作用域),所以'@'是合适的选择:
.directive('question', function() {
...
scope: { question: '@' },
.directive('possibleAnswer', function() {
...
scope: { possibleAnswer: '@', ... },
我们需要向指令传递来自父范围的问题和可能答案的评估副本,因此我们使用 {{}} 表示法:
<span question="{{item.question}}">
<span possible-answer="{{possibleAnswer}}" ...
现在是棘手的部分。与单个问题关联的单选按钮都需要相同的名称,并且正如@Max 已经提到的,我们需要将所有单选按钮双向绑定回父模型。我通过使用问题 ID 的单向绑定解决了“同名”问题,这实际上是 ng-repeat 问题 $index:
<span possible-answer="{{possibleAnswer}}" id="{{$parent.$index}}" ...>
.directive('possibleAnswer', function() {
...
scope: { id: '@', ... },
...
template: '...<input type="radio" ... name="q{{id}}"...
由于我们想要相同的问题 ID/索引(而不是答案选择 ID/索引),我们使用 $parent 查找问题范围并获取其 ng-repeat $index,因此是 $parent.$index。另一种方法是向主模型中的每个对象添加一个“id”属性(如您所见)并将其传递给指令:
<span possible-answer="..." id="{{item.id}}" ...>
(实际上,由于您将在下面看到我们将 'item' 传递到指令中,因此这样的 id 属性将免费出现,因此不需要“id='...'”模板。)我更喜欢使模型尽可能简单。由于我们使用的是对象数组,因此数组索引(ng-repeat $index)就足够了,所以我没有在主模型上使用 id 属性。
要获得 2-way 数据绑定,我们需要使用 '='。我们想要绑定到一个新的“答案”属性,我们将动态添加到主模型中的对象(我之前提到过)。所以我们需要一个对模型中适当对象的引用,同时我们正在循环选择答案。然后我们将这个新的动态属性分配给 ng-model:
<li ng-repeat="item in questionsAndAnswers">' + // pass this 'item' to possibleAnswer
...
'<ul ..>' +
'<li ng-repeat="possibleAnswer in item.answerChoices">' +
<span possible-answer="{{possibleAnswer}}" item=item ...">
.directive('possibleAnswer', function() {
...
scope: { item: '=', ... },
...
template: '...<input type="radio" ... ng-model="item.answer" ...' // new property
注意:根据输入无线电 API 页面上的 Disqus 评论,应使用“ng-value”而不是“value” 。
template: '...<input type="radio" ng-value="possibleAnswer" ...
对于 quiz element 指令,我认为如果传入模型,而不是假设某个范围属性存在于父范围中,该组件的可重用性更高,因此:
<quiz questions-and-answers=questions_and_answers></quiz>
由于 possibleAnswer 指令需要修改模型,我们也必须在这里使用 2-way binding,因此 '=':
.directive('quiz', function() {
...
scope: { questionsAndAnswers: '='},
最后,控制器:我删除了函数 selectedAnswer() 并稍微修改了 index.html 中的 ng-repeat 以获得相同的功能:
<div ng-repeat="item in questions_and_answers">
A{{$index}}: {{item.answer}}
所以控制器又好又瘦(推荐的最佳实践)——一行代码。
没有使用 jQuery,所以我删除了它。(我也没有看到 bootstrap.js 的任何用途,而且它抛出了一个错误,所以我也将其删除了。)
这对我来说是一个很好的练习,感谢 OP。(对不起,如果这个答案太长了。我被带走了,但把它全部写下来有助于我澄清一些事情。)