Knockout JS模式用于同一页面中的多个视图模型,维护DOM结构
Knockout JS pattern for multiple viewmodels in same page maintaining DOM structure
我最近开始使用knockout js。我试图在一个项目中以一种非传统的方式使用它。我需要维护服务器端代码发出的DOM id和结构。但是,我想利用knockout来促进客户端操作,方法是利用模式将页面上的字段引用为javascript对象。
我有一个概念验证片段工作,但我想知道当我开始添加更多的部分时,它将如何扩展。我想得到一些反馈,看看更有经验的淘汰赛用户有什么要说的。
的例子在这些jsfiddle, jsfiddle:例如:
HTML<!--
* We would then have a whole other section like fieldset0 after fieldset0 for a different people category
* We need to use the ids returned by the page, just add knockout on top without modifying DOM structure (except for sorting people based on sort order field, after removing item2 and adding again, it should go after item3 - if all 3 were on the page)
-->
<fieldset name="fieldset0" id="el_fieldset0">
<fieldset name="fieldset1" id="el_fieldset1">
<h1><span id="person1"></span></h1>
<input id="hasdata1" type="hidden" value="Yes" />
<label for="name1">name</label>
<input id="name1" value="andrew" />
<label for="age1">age</label>
<input id="age1" value="30" />
<label for="grade1">grade</label>
<select id="grade1">
<option selected>A</option>
<option>B</option>
<option>C</option>
<option>F</option>
</select> <a id="remove1" class="remove" href="javascript:void(0)">remove</a>
</fieldset>
<fieldset name="fieldset2" id="el_fieldset2">
<h1><span id="person2"></span></h1>
<input id="hasdata2" type="hidden" value="Yes" />
<label for="name2">name</label>
<input id="name2" value="brandon" />
<label for="age2">age</label>
<input id="age2" value="40" />
<label for="grade2">grade</label>
<select id="grade2">
<option>A</option>
<option selected>B</option>
<option>C</option>
</select> <a id="remove2" class="remove" href="javascript:void(0)">remove</a>
</fieldset>
<fieldset name="fieldset3" id="el_fieldset3">
<h1><span id="person3"></span></h1>
<input id="hasdata3" type="hidden" value="" />
<label for="name3">name</label>
<input id="name3" value="calvin" />
<label for="age3">age</label>
<input id="age3" value="50" />
<label for="grade3">grade</label>
<select id="grade3">
<option>A</option>
<option>B</option>
<option selected>C</option>
</select> <a id="remove3" class="remove" href="javascript:void(0)">remove</a>
</fieldset> <a id="addItem" class="add" href="javascript:void(0)">add</a>
</fieldset>
Javascript var myNamespace = {};
$(document).ready(function () {
//assign fieldset variables
myNamespace.$fieldset0 = $("#el_fieldset0"); // parent for all items
//individual items
myNamespace.$fieldset1 = $("#el_fieldset1");
myNamespace.$fieldset2 = $("#el_fieldset2");
myNamespace.$fieldset3 = $("#el_fieldset3");
//set the number of objects
myNamespace.personObjectCount = 3;
//set utilityArrays
myNamespace.personObjects = [];
myNamespace.availablePersonObject = [];
//assign data-bind attributes to html
for (var i = 0; i < myNamespace.personObjectCount; i++) {
var objectIndex = i + 1;
var currentFielsetStr = "$fieldset" + objectIndex;
//myNamespace[currentFielsetStr] = $("#el_fieldset"+ objectIndex);
myNamespace[currentFielsetStr].find("input[id^='name']").attr("data-bind", "value: personName");
myNamespace[currentFielsetStr].find("input[id^='age']").attr("data-bind", "value: personAge");
myNamespace[currentFielsetStr].find("select[id^='grade']").attr("data-bind", "value: personGrade");
myNamespace[currentFielsetStr].find("span[id^='person']").attr("data-bind", "text: personTitle");
myNamespace[currentFielsetStr].find("input[id^='hasdata']").attr("data-bind", "value: hasData");
//hasdata identifies visibility and should wipe fields when set to false
myNamespace[currentFielsetStr].attr("data-bind", "visible: hasData() === 'Yes'");
//remove link associates with removeItem function
myNamespace[currentFielsetStr].find("a[id^='remove']").attr("data-bind", "click: removeItem");
//create each viewmodel object
myNamespace.personObjects.push({
"name": myNamespace[currentFielsetStr].find("input[id^='name']").val(),
"age": myNamespace[currentFielsetStr].find("input[id^='age']").val(),
"grade": myNamespace[currentFielsetStr].find("select[id^='grade']").val(),
"hasData": myNamespace[currentFielsetStr].find("input[id^='hasdata']").val(),
"parentFieldset": myNamespace[currentFielsetStr]
});
/*
//attach event handlers
myNamespace[currentFielsetStr].on("click", ".remove", function () {
//retrieve the ko context
var context = ko.contextFor(this);
debugger;
//alert(context);
});
*/
//populate available person array
if (myNamespace.personObjects[i].hasData !== "Yes") {
debugger;
myNamespace.availablePersonObject.push(myNamespace.personObjects[i]);
}
}
//add link associates with addItem function, disables if at maximum count/no positions available -- only one per section -- should try to move to binding outside of these (make a root viewmodel for the page containing individual sections?)
//debugger;
myNamespace.$fieldset0.find("a[id^='addItem']")
.attr("data-bind", "click: addItem, enabled: myNamespace.availablePersonObject.length > 0")
.click(function () {
debugger;
if (myNamespace.availablePersonObject.length > 0) {
//add first item in lsit of available person object (later, based on sort order)
myNamespace.availablePersonObject[0].parentFieldset.find("input[id^='hasdata']").val("Yes").change();
myNamespace.availablePersonObject.splice(0, 1);
}
if (myNamespace.availablePersonObject.length < 1) {
//disable add button
}
});
for (var i = 0; i < myNamespace.personObjects.length; i++) {
//perform knockout bindings
ko.applyBindings(new myViewModel(myNamespace.personObjects[i]), myNamespace.personObjects[i].parentFieldset[0]);
}
});
function myViewModel(personObject) {
var self = this;
self.personName = ko.observable(personObject.name);
self.personAge = ko.observable(personObject.age);
self.personGrade = ko.observable(personObject.grade);
self.hasData = ko.observable(personObject.hasData);
self.personTitle = ko.computed(function () {
return self.personName() + " : " + self.personGrade();
});
self.removeItem = function () {
debugger;
var _blank = "";
//var _blank = undefined;
self.hasData(_blank);
self.personName(_blank);
self.personAge(_blank);
self.personGrade(_blank);
myNamespace.availablePersonObject.push(personObject);
};
/*
self.addItem = function() {
//move this to outside the individual element and add to the parent Section
//or maybe don't have the fields at all
debugger;
};
*/
}
为我需要解决的问题类型实现了解决方案。
为了绑定到不灵活的生成的HTML,我基本上把任务分成了4部分。这将在绑定时维护特定的DOM id(没有动态html生成用于这些目的)(这是为每个对象-视图模型绑定抽象完成的):
- 创建一个绑定到ViewModel的模型数据对象(这是在页面加载后从现有的HTML标记中读取的,客户端)。还要在此数据对象中存储每个"对象"的信息,包括通过knockout返回的ViewModel和用于绑定的fieldset。
- 使用jQuery和dataObject信息应用绑定——可以与步骤1结合使用
- 根据初始数据对象值创建ViewModel。
- 使用dataObject的viewModel对页面应用knockout绑定
这里列出的粗略示例http://jsfiddle.net/vcbbg81/vv3zjag4/79/
$("#pageMenu1").before("<!-- ko stopBinding: true -->");
$("#pageMenu1").after("<!-- /ko -->");
//$("#employmentMenu1").attr("data-bind", "checked: employmentMenuItem");
myNamespace.vmPage = {
"employmentMenuItem": $("#employmentMenu1").attr("data-bind", "checked: employmentMenuItem").prop("checked"),
"selfEmploymentMenuItem": $("#selfEmploymentMenu1").attr("data-bind", "checked: selfEmploymentMenuItem").prop("checked"),
"otherMenuItem": $("#otherMenu1").attr("data-bind", "checked: otherMenuItem").prop("checked"),
"noneMenuItem": $("#noneMenu1").attr("data-bind", "checked: noneMenuItem").prop("checked")
};
//myNamespace.vmPage = pageObjects;
myNamespace.vmPage.viewModel = new aPageViewModel(myNamespace.vmPage);
ko.applyBindings(myNamespace.vmPage.viewModel, $("#pageMenu1")[0]);
相关文章:
- 角度指令没有更新模型视图
- “渲染骨干模型视图”返回未定义的结果
- 轮询更新主干模型/视图的请求
- 节点.js中的模型-视图-控制器模式
- 图像数据中的更改未反映在模型视图中
- 自定义 ngModel 指令以支持 jquery 插件中的模型>视图绑定
- 优化显示简单项目列表的模型/视图
- 建议:在JavaScript中难以使用模型视图控制器
- 将三维世界矢量转换为模型视图矩阵
- 在主干模型/视图上处理更复杂的验证逻辑(必填字段等)的最佳方式
- AngularJS在POST http请求上更新模型/视图
- web应用程序模型视图中的Java脚本
- 我的第一个骨干模型/视图.我的思路对吗?
- Knockoutjs的日期时间字段更新不会刷新模型视图
- Backbone.js中的嵌套集合/模型视图管理
- Javascript“this"模型视图演示器设计中的问题
- 模型视图控制器-javascript mvc框架设计实践,用于编辑就地界面
- 模型视图控制器-任何使用javascript mvc的人
- 模型视图控制器-我们是否在使用JavaScriptMVC(MVVM)框架,如Backbone.js、Angular等
- 模型视图控制器-AngularJS中是否可以在经典的javascript函数中使用数据绑定