【问题标题】:How to use "with" binding in Knockout JS with nested View Models?如何在带有嵌套视图模型的 Knockout JS 中使用“with”绑定?
【发布时间】:2016-07-28 15:25:53
【问题描述】:

我正在尝试编写一个模板来为多人填写表格。我不想进入“foreach”绑定或模板。我只是想使用一个按钮将所有数据保存在具有 5 个输入的简单表单中,该按钮会将人添加到 observableArray 并为下一个人清除表单。

我在下面粘贴了我的整个代码:

<html>
<head>
    <title>Test Knockouts</title>
</head>
<body>
    <div id="rootDiv">
        <ul data-bind="with: Person">
            <li data-bind="event: { click: $data.setupPersonTypeForm.bind($data, 'Member') }">
                <a href="#" aria-controls="home">
                    <span class="radio-label">Member</span>
                </a>
            </li>
            <li data-bind="event: { click: $data.setupPersonTypeForm.bind($data, 'Guest') }">
                <a href="#" aria-controls="messages">
                    <span class="radio-label">Guest</span>
                </a>
            </li>
            <li data-bind="event: { click: $data.setupPersonTypeForm.bind($data, 'Employee') }">
                <a href="#" aria-controls="profile">
                    <span class="radio-label">Employee</span>
                </a>
            </li>
        </ul>
        <div>
            Person Name:
            <input type="text" data-bind="text: PersonName" /><br />
            Person Age:
            <input type="text" data-bind="text: PersonAge" /><br />
            Person Type:
            <input type="text" data-bind="text: PersonType" /><br />
            Person Country:
            <input type="text" data-bind="text: PersonCountry" /><br />
            Person NetWorth:
            <input type="text" data-bind="text: PersonNetWorth" />
        </div>
    </div>

    <script src="jquery-2.1.4.js"></script>
    <script src="knockout-3.4.0.js"></script>

    <script type="text/javascript">

        $(document).ready(function (e) {

            var element = document.getElementById("rootDiv");
            ko.applyBindings(rootVM, element);

            var RootVM = function (data) {
                var self = this;

                self.Id = ko.observable();
                self.Name = ko.observable();
                self.Location = ko.observable();
                self.TypeId = ko.observable();
                self.Age = ko.observable();

                self.Person = ko.observableArray([new PersonVM()]);
            }
            var rootVM = new RootVM();


            var PersonVM = function (data) {
                var self = this;

                self.Id = ko.observable();
                self.PersonName = ko.observable();
                self.PersonType = ko.observable();
                self.PersonAge = ko.observable();
                self.PersonCountry = ko.observable();
                self.PersonNetWorth = ko.observable();

                self.PersonTypeIdPlaceholder = ko.observable();
                self.ShowEmployeeTitleField = ko.observable(false);

                self.setupPersonTypeForm = function (personType) {

                    self.ShowEmployeeTitleField(false);

                    switch (personType) {

                        case "Member":
                            self.PersonTypeIdPlaceholder("Member ID");
                            break;
                        case "Guest":
                            self.PersonTypeIdPlaceholder("Guest ID");
                            break;
                        case "Employee":
                            self.PersonTypeIdPlaceholder("Employee ID");
                            self.ShowEmployeeTitleField(true);
                            break;
                    }
                }

                self.setupPersonTypeForm('Member');

                self.Property = ko.observableArray([new PropertyVM()]);
            }

            var PropertyVM = function (data) {
                var self = this;

                self.Id = ko.observable();
                self.PropertyPlace = ko.observable();
                self.PropertySize = ko.observable();
                self.PropertyName = ko.observable();
                self.PropertyAge = ko.observable();
            }
        });
    </script>
</body>
</html>

我收到以下错误:

knockout-3.4.0.js:72 Uncaught ReferenceError: Unable to process binding "with: function (){return Person }" 消息:Person is not 定义

我哪里错了?

任何帮助将不胜感激!

【问题讨论】:

    标签: jquery html knockout.js knockout-3.0


    【解决方案1】:

    从这一行开始:

    ko.applyBindings(rootVM, element);
    

    ...rootVMundefined,你还没有给它赋值。你想把它移到之后你的

    var rootVM = new RootVM();
    

    ...这又需要之后

    var PersonVM = function (data) { ... };
    

    ...因为它使用PersonVM

    只有 声明 被提升,而不是分配。当你有:

    (function() {
        console.log(foo);
    
        var foo = 42;
    })();
    

    ...您看到的是undefined,而不是 42,因为该代码被解释为:

    (function() {
        var foo = undefined;
    
        console.log(foo);
    
        foo = 42;
    })();
    

    这在你的代码中发生。

    至少还有几个其他问题:

    1. 您使用 Person 好像它是一个单一的东西,但您将它定义为一个东西数组。解决上述问题后,您会遇到无法绑定 click 事件的问题,因为数组没有 setupPersonTypeForm 方法(您的 Person 有,而不是它所在的数组)。

    2. 您正在使用列表下方仅存在于Person 上的属性,但仅使用列表中的with: Person

    如果您的目标是创建人员列表,并且能够使用表单在该列表中添加/编辑人员,my answer over here 将展示如何做几乎相同的事情,只是使用“项目”而不是“人员” ”。

    这是该答案中为上下文重复的简单示例:

    function Project(title, priority) {
      this.title = ko.observable(title || "");
      this.priority = ko.observable(priority || "Medium");
    }
    
    
    function ProjectViewModel() {
      var self = this;
      this.priorityOptions = ko.observableArray(["High", "Medium", "Low"]);
      this.projects = ko.observableArray();
      this.editing = ko.observable(new Project());
      this.addProject = function() {
        this.projects.push(this.editing());
        this.editing(new Project());
      };
    }
    
    ko.applyBindings(new ProjectViewModel(), document.body);
    <div>
      <div>
        <label>
          Title:
          <input type="text" data-bind="value: editing().title, valueUpdate: 'input'">
        </label>
      </div>
      <div>
        <label>
          Priority:
          <select data-bind='options: priorityOptions, value: editing().priority'></select>
        </label>
      </div>
      <div>
        <button type="button" data-bind="click: addProject, enable: editing().title">Add Project</button>
      </div>
      <hr>
      <div>Projects:</div>
      <div data-bind="foreach: projects">
        <div>
          <span data-bind="text: title"></span>
          (<span data-bind="text: priority"></span>)
        </div>
      </div>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

    在您提出的评论中:

    1. 如果每个项目都有一些子类别,我想在每个特定项目中添加几个子类别项目。您将 addSubCategory 函数放置在哪个视图模型中。您如何跟踪每个孙子项目?

    你只是重复相同的结构,嵌套。例如,我们可以很容易地在“项目”中添加“任务”列表。

    1. 为什么我们不能使用“with”绑定?淘汰赛有限制吗?

    如果你愿意,你可以。我没有在原始示例中,因为我在一个项目中只有两个字段,但您可以将 data-bind="with: editing" 放在上面以将它们包装在 with 中。

    这是一个使用任务和使用with 绑定的示例(我已将上面的editing 更改为editProject,因为现在我们也有editTask):

    function Task(label, difficulty) {
      this.label = ko.observable(label);
      this.difficulty = ko.observable(difficulty || "Normal");
    }
    
    function Project(title, priority) {
      this.title = ko.observable(title || "");
      this.priority = ko.observable(priority || "Medium");
      this.tasks = ko.observableArray();
      this.editTask = ko.observable(new Task());
      this.addTask = function() {
        this.tasks.push(this.editTask());
        this.editTask(new Task());
      };
    }
    
    
    function ProjectViewModel() {
      var self = this;
      this.priorityOptions = ko.observableArray(["High", "Medium", "Low"]);
      this.difficultyOptions = ko.observableArray(["Hard", "Normal", "Easy"]);
      this.projects = ko.observableArray();
      this.editProject = ko.observable(new Project());
      this.addProject = function() {
        this.projects.push(this.editProject());
        this.editProject(new Project());
      };
    }
    
    ko.applyBindings(new ProjectViewModel(), document.body);
    ul {
      margin-top: 0;
      margin-bottom: 0;
    }
    <div>
      <!-- Added a new div with a 'with' binding -->
      <div data-bind="with: editProject">
        <div>
          <label>
            Title:
            <input type="text" data-bind="value: title, valueUpdate: 'input'">
          </label>
        </div>
        <div>
          <label>
            Priority:
            <select data-bind='options: $parent.priorityOptions, value: priority'></select>
          </label>
        </div>
        <div style="padding-left: 3em">
          Tasks for this project:
          <div style="padding-left: 2em">
            New task:
            <div data-bind="with: editTask">
              <div>
                <label>
                  Difficulty:
                  <select data-bind='options: $root.difficultyOptions, value: difficulty'></select>
                </label>
              </div>
              <div>
                <label>
                  Label:
                  <input type="text" data-bind="value: label, valueUpdate: 'input'">
                </label>
              </div>
            </div>
            <button type="button" data-bind="click: addTask, enable: editTask().label">Add Task</button>
          </div>
          <div data-bind="foreach: tasks">
            <div>
              [<span data-bind="text: difficulty"></span>]
              <span data-bind="text: label"></span>
            </div>
          </div>
        </div>
      </div>
      <div>
        <button type="button" data-bind="click: addProject, enable: editProject().title">Add Project</button>
      </div>
      <hr>
      <div>Projects:</div>
      <div data-bind="foreach: projects">
        <div>
          <span data-bind="text: title"></span>
          (<span data-bind="text: priority"></span>)
        </div>
        <ul data-bind="foreach: tasks">
          <li>[<span data-bind="text: difficulty"></span>]: <span data-bind="text: label"></span></li>
        </ul>
      </div>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

    【讨论】:

    • 按照你的建议,我先放:var PropertyVM,然后是 var PersonVM,然后是 var RootVM,然后是喜欢的东西: var element = document.getElementById("rootDiv");和 ko.applyBindings(new RootVM(), element); - 但同样的错误仍然存​​在。
    • @zapper:请参阅底部的进一步说明。该代码有几个问题。您需要单独调试它们(这不是适合 SO 的活动)。
    • @zapper:我还添加了一个简单的示例,我怀疑它与您尝试做的几乎相同,并附有一个(我的)答案的链接来解释它。
    • 感谢您的解决方案。但是,在我的问题中,集合中有一个集合。因此,在您的示例中, 1. 如果每个项目都有一些子类别,我想在每个特定项目中添加几个子类别项目。您将 addSubCategory 函数放置在哪个视图模型中。您如何跟踪每个孙子项目? 2. 为什么我们不能使用“with”绑定?淘汰赛有限制吗?
    • @zapper:我已经更新了答案。但通常,如果你问“为什么我们不能做 X,是不是 Y 的限制?”其中 Y 是一个经过深思熟虑的工具,答案是“你可以”或“你不想”,而不是“是的,这是一个限制。”
    猜你喜欢
    • 2014-07-31
    • 1970-01-01
    • 2013-01-03
    • 1970-01-01
    • 2016-05-04
    • 2014-03-06
    • 1970-01-01
    • 2014-07-05
    • 2013-09-04
    相关资源
    最近更新 更多