针对同一页面中多个视图模型的Knockout JS模式,维护DOM结构

时间:2014-08-26 02:38:08

标签: javascript jquery html knockout.js

我最近开始使用淘汰赛。我试图在一个看似非传统方式的项目中使用它。我需要维护服务器端代码发出的DOM ID和结构。但是,我想利用knockout来促进客户端操作,方法是利用模式将页面上的字段作为javascript对象引用。

我有一个概念验证片段,但我想知道当我开始添加更多部分时它的扩展程度。我想得到一些反馈,看看更有经验的淘汰赛用户必须说些什么。

这些jsfiddlejsfiddle中的示例: e.g:

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;        
    };
    */
}

1 个答案:

答案 0 :(得分:0)

为我需要解决的问题类型实施了解决方案。

我基本上打破了4个部分的任务,以便绑定到不灵活的生成的HTML。这将在绑定时维护特定的DOM ID(没有为这些目的生成动态html)(这是为每个object-viewModel绑定抽象完成的):

  1. 创建一个模型dataObject以绑定到ViewModel(在页面加载后,从客户端读取现有HTML标记。还存储每个&#34;对象的信息&#34;在此dataObject中,包括由knockout返回的ViewModel和fieldset knockout应该用于绑定。
  2. 使用jQuery和dataObject信息应用绑定 - 可以与步骤1结合使用。
  3. 根据初始dataObject值创建ViewModel。
  4. 使用dataObject的viewModel
  5. 将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]);