【问题标题】:Binding data on a model for dynamic nested forms in angularjs在angularjs中为动态嵌套表单的模型绑定数据
【发布时间】:2014-02-04 09:51:30
【问题描述】:

我从一个 json 对象生成嵌套表单,比如 formObject 并将值绑定到 json 对象本身。我正在递归地解析值并在提交时提取实际数据,例如 dataObject。

我可以像这样以线性形式检索数据对象。 http://jsfiddle.net/DrQ77/80/.

<select ng-model="Answers[question.Name]" ng-options="option for option in question.Options">

与上述相反,http://jsfiddle.net/DrQ77/92/ 有一些递归。我已将 question 重命名为 element 以代表问题和部分。每个部分都可以有多个问题,再次,多个部分(这就是我所说的嵌套)。我最终想要的是具有任何嵌套级别的以下形式的对象。

Answers=[{
    section:"Personal",
    values:[{GenderQuestion:"Male"},{MaritalStatus:"Married"},{section:"Sub Personal",values:[{LivingWith:"Alone"}]}]
}, {
    section:"Random",
    values:[{ColorQuestion:"Red"}],
},
{SectionLess:"opt1"}]

这是一个解决方案,我可以在提交时得到它,$scope.Answers 来自第一个小提琴(我认为)不允许这种嵌套。但是当我必须更新现有的数据对象时,我觉得需要在渲染它之前将数据对象映射到 formObject 上,然后在提交时再次解析。 现在这不是 MVC,看起来并不优雅(由于递归),我认为这有一种“角度方式”。

有没有人试过这个并让它以更好的方式工作?我该如何解决?

【问题讨论】:

  • 我不太明白你的方法有问题。也许是因为 JSFiddle 上的示例不够复杂 - 没有提到“递归”并且需要“在提交时再次解析” - $scope.Answers 不断更新并随时可以提交,因为Angular 的数据绑定。但也许是因为我太困了 :) 无论如何,如果你能把小提琴做得更精致(但不要太多 - hello-worldish 风格赞赏)以充分说明你当前方法的缺点,那就太酷了。
  • 正如@vucalur 所说,我也不太明白问题出在哪里。我也没有在示例中看到任何递归。您能否更新您的问题并更好地解释您的问题是什么?
  • 是的,试着解释一下!很抱歉第一次没有详细说明问题陈述,我对简洁有奇怪的想法!
  • @vucalur,现在问题更清楚了吗?还是我需要再详细说明一下?

标签: javascript angularjs model-view-controller


【解决方案1】:

不是很漂亮,但这是您已经提出的解决方案的替代方案。

http://jsfiddle.net/DrQ77/84/

function QuestionController($scope) {
    $scope.answers = {};
    $scope.tempOption = "";
    $scope.questions = [
    {
        "text": "Gender?",
        "name": "GenderQuestion",
        "options": ["Male", "Female"]},
    {
        "text": "Favorite color?",
        "name": "ColorQuestion",
        "options": ["Red", "Blue", "Green"]}
    ];

    $scope.showAnswers = function () {
      console.log($scope.answers);
    };

    $scope.pumpOption = function (name, tempOption) {
      $scope.answers[name] = tempOption;
    };
};

<ul ng-repeat="question in questions">
    <li>
        <div>{{question.text}}</div>
        <select ng-model="tempOption" ng-options="opt for opt in question.options" ng-change="pumpOption(question.name, tempOption)">
        </select>
    </li>
</ul>    

我们将 select 标记中选定选项的值绑定到 $scope.tempOption 变量。

然后我们监听这个 select 标签上发生的 ng-change 事件,我们在其中运行一个函数,该函数接受 $scope.tempOption 变量加上与 select 标签关联的 {{question.name}}。

此函数然后将 answers[name] 设置为 $scope.tempOption 的当前值。

希望这对你有用,祝你好运:)

【讨论】:

  • 是的,这行得通,但是当我拥有带有嵌套对象的 json 数据时,递归下的事情看起来很丑陋。我目前有a solution like,但它不是 MVC 并且需要做一些工作来通过相同的表单更新字段。
【解决方案2】:
var model = {
    colors:["Red","Blue","Green","Black","White"],
    genders:["Male", "Female"],
    topic:["Gender", "Color"],
    category:["Favorite", "Least favorite"],
};

function makeQuestion(topic, category){
    return (category+1 ? model.category[category] + ' ' : '') 
    + ' ' + model.topic[topic] + '?'
}

function QuestionController($scope){

    $scope.Answers = {};
    $scope.Questions = [
        {
            "Text": makeQuestion(0),
            "Name": "GenderQuestion",
            "Options": model.genders 
        },{
            "Text": makeQuestion(1,0),
            "Name": "ColorQuestion",
            "Options": model.colors.slice(0,3)
        },{
            "Text": makeQuestion(0,1),
            "Name": "SexistQuestion",
            "Options": model.genders
        },{
            "Text": makeQuestion(1,1),
            "Name": "RacistQuestion",
            "Options":model.colors.slice(3)
        }
    ];

    $scope.ShowAnswers = function()
    {
        console.log($scope.Answers);
    };
}    

好吧,我在开玩笑。 但是您是否尝试过使用平面相关对象关系表方法而不是嵌套?

{
  sections:[
    { "Name": 'Personal', "questions":[0], sub:[1] },
    { "Name": 'SubPersonal', "questions":[3], sub:[]},
    { "Name": 'Random',   "questions":[1,2], sub:[] }
  ],
  questions:[
    { "Name":"Gender",     "Text":"Gender?",         "Options":["Male", "Female"] },
    { "Name":"Color",      "Text":"Favorite Color?", "Options":["Red","Blue","Green"] },
    { "Name":"LivingWith", "Text":"Living With?",    "Options":["Alone","Someone"] },
    { "Name":"Random",     "Text":"SectionLess",     "Options":["opt1", "opt2"] }
  ]
}

【讨论】:

  • 是的,但话又说回来,我必须写一些东西来生成原始对象。我不想在平面表中表示我的数据模型!
  • 您还期望如何编写递归 JSON 对象关系?我的意思是,JSON.stringify 不允许循环引用是有原因的。 Parent = { child:{ parent:{ child:{ parent:{ child: {明白我的意思吗?} } } } }
【解决方案3】:

只需创建 $scope.Answer = {}; 并将 ng-model 链接到它。 http://jsfiddle.net/2AwLM/40/

【讨论】:

  • 是的,这是一种方式,但仍然不能解决嵌套问题。例如。我不能使用 Answers[0].values[0].GenderQuestion 而不引用它的元素 Answer[0].values[0]
【解决方案4】:

好的——这并不像我最初想象的那么简单,所以我希望这就是你所追求的——你会想要使用一个动态生成其模板 HTML 的指令(更好的是,使用 templateUrl 和一个函数传入类型)

TLDR:请参阅http://jsfiddle.net/cirrusinno/SzaNW/2 的小提琴

HTML 很简单

<div ng-controller="questionsController">

<!-- for each question, render section or question -->
<div ng-repeat="item in Questions">
    <!-- we ignore section here as it's a root section -->
    <question item="item"></question>
</div>

您需要创建一个可以呈现问题和部分的指令。它还需要跟踪该部分,以便构建答案

.directive('question', ['$compile', '$templateCache', function ($compile, $templateCache) {
    var generateHtmlTemplate = function (item, section) {
        return item.type != 'section' ?
        // render the question    
        'question ' + item.name + '<br/>' +
            '<select ng-model="optionValue" ng-options="opt for opt in item.options" ng-change="pushAnswer(item.name, section, optionValue)"></select>' :
        // or the template for a section
        '<br/><hr/>section ' + item.sectionName + '<br/>' +
            '<div ng-repeat="q in item.questions"><question item="q" section="item"></question></div><hr/>';
    }

    var currentSection = null;

    return {
        scope: {
            item: '=',
            section: '='
        },
        restrict: 'E',
        link: function ($scope, element, attrs) {
            // check current section
            if ($scope.item.type == 'section') {
                // set new current section
                $scope.currentSection = $scope.item;
            } else {
                // use section passed in from parent section
                $scope.currentSection = $scope.section;
            }

            // get template html
            var t = generateHtmlTemplate($scope.item, $scope.currentSection);

            // set the scope function to set the value
            $scope.pushAnswer = function (q, section, value) {
                // build the Answers object here however you want...
                if (section != null) {
                    console.log('pushAnswer q=' + q.name + ' - section=' + section.sectionName + ' - value=' + value);
                } else {
                    console.log('pushAnswer q=' + q.name + ' - section=rootSection - value=' + value);
                }

            };

            // chuck it into element as template
            element.html(t);

            // compile it up and return it
            $compile(element.contents())($scope);
        },
    };
}])

【讨论】:

  • 好的,但是映射现有模型是个问题。我曾想过为单个元素制作指令,以便为 ng-model 中的嵌套元素插入父模型对象。我想我应该试试看!
  • 我刚刚使用了其他问题中给出的现有模型 - 如果您有单独的模型,请发布它,我们会将其应用于指令。在任何情况下,该指令都会深入到模型中。创建“一刀切”的指令将是一项复杂得多的任务。我也想过使用两个指令,一个用于部分,一个用于问题,但我开始使用一个,它只是逐渐起作用。也许两个指令会更干净,因为范围变量会更干净一些(即:一个部分不需要 currentSection,它只是将它传递给一个子问题)。
猜你喜欢
  • 2013-09-16
  • 1970-01-01
  • 2015-05-01
  • 2012-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多