如何在嵌套视图模型的 Knockout JS 中使用 "with" 绑定?
How to use "with" binding in Knockout JS with nested View Models?
我正在尝试编写一个模板来为多人填写表格。我不想进入 "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 }" Message: Person is not
defined
我哪里错了?
如有任何帮助,我们将不胜感激!
从这一行开始:
ko.applyBindings(rootVM, element);
...rootVM
是 undefined
,您尚未为其分配任何值。您想将其移动到 after your
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;
})();
这在您的代码中一直在发生。
至少还有几个其他问题:
您正在使用 Person
就好像它是一个单一的事物,但您将其定义为一组事物。解决上述问题后,您会遇到无法绑定 click
事件的问题,因为数组没有 setupPersonTypeForm
方法(您的 Person
有,而不是数组里面).
您正在使用仅存在于 Person
的列表下方的属性,但仅使用列表中的 with: Person
。
如果您的目标是拥有一个人员列表,并且能够使用表单 add/edit 列表中的人员, 展示了如何做一些几乎相同的事情,只是 "projects" 而不是 "people".
这是为上下文复制的答案中的简单示例:
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>
在您提出的评论中:
- If we had some sub-category for each Project, and i would like to add several of the sub-category items within each specific project. In which viewmodel do you place the addSubCategory function. How do you keep track of each grandchild item?
你只是重复相同的结构,嵌套。例如,我们可以很容易地在 "project" 中添加 "tasks" 列表。
- Why can't we use the "with" binding? Is there a limitation in knockout for that?
你喜欢就可以。我没有在原始示例中使用,因为我在一个项目中只有两个字段,但是您可以将 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>
我正在尝试编写一个模板来为多人填写表格。我不想进入 "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 }" Message: Person is not defined
我哪里错了?
如有任何帮助,我们将不胜感激!
从这一行开始:
ko.applyBindings(rootVM, element);
...rootVM
是 undefined
,您尚未为其分配任何值。您想将其移动到 after your
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;
})();
这在您的代码中一直在发生。
至少还有几个其他问题:
您正在使用
Person
就好像它是一个单一的事物,但您将其定义为一组事物。解决上述问题后,您会遇到无法绑定click
事件的问题,因为数组没有setupPersonTypeForm
方法(您的Person
有,而不是数组里面).您正在使用仅存在于
Person
的列表下方的属性,但仅使用列表中的with: Person
。
如果您的目标是拥有一个人员列表,并且能够使用表单 add/edit 列表中的人员,
这是为上下文复制的答案中的简单示例:
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>
在您提出的评论中:
- If we had some sub-category for each Project, and i would like to add several of the sub-category items within each specific project. In which viewmodel do you place the addSubCategory function. How do you keep track of each grandchild item?
你只是重复相同的结构,嵌套。例如,我们可以很容易地在 "project" 中添加 "tasks" 列表。
- Why can't we use the "with" binding? Is there a limitation in knockout for that?
你喜欢就可以。我没有在原始示例中使用,因为我在一个项目中只有两个字段,但是您可以将 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>