KnockoutJS添加动态列

时间:2014-08-05 00:02:29

标签: knockout.js

尝试在运行时添加多行教师和助手列,到目前为止我的核心设计基于Ryan Neimeyer的示例代码,我已经适应了这个jsfiddle - http://jsfiddle.net/sfortner/3qLBL/。我的最新更新尝试位于http://jsfiddle.net/sfortner/5E9HJ/,我尝试通过创建新的助手实例来添加列,但所有复选框都绑定到同一个元素,所以每次选中一个框时,都会在列中检查所有。这是我尝试添加助手的新尝试:

// new way - reuses same instance of Assistant for all rows
addAsst: function() {
    this.asstColumns.push(new Assistant(this.selectedAsst(), $('#assistants option:selected').text(), false, "0%", this.newFTE(), this.newType()));

    this.removeAsst(this.selectedAsst());  // remove the assistant from dropdown
    this.selectedAsst("0");                // reset the dropdown to "Select One..."
}

显然,我需要能够为每个教师行添加一个新的/唯一的助手实例,但我相信这会破坏我的设计。如果它有帮助,我将永远不会有超过7名助手,并且总是至少有一名助手。我在想另一种策略可能是构建7个助手列,甚至可能使那些没有助手添加助手的那些,但我也在努力解决如何更新现有的可观察数组。这是我尝试添加助手的旧方法,我只是在“助手”下拉列表中添加了未链接到新助手()实例的文本,就像它在更新版本中一样。但是,它们都不应该像它们那样工作,因为它们需要绑定到一个可以在用户点击它时更新的observable。

// old way - creates new asst, but doesn't use it and doesn't bind anything        
addAsst: function() {
    var asst = new Assistant(this.selectedAsst(), $('#assistants option:selected').text(), this.newFTE(), this.newType());
    this.columns.push({property: $('#assistants option:selected').text(), 
    display: $('#assistants option:selected').text(), readonly: false });

    this.removeAsst(this.selectedAsst());   // remove the assistant from dropdown
    this.selectedAsst("0");                 // reset the dropdown to "Select One..."
}

TIA,史蒂夫

1 个答案:

答案 0 :(得分:1)

<comedian>

好吧,差不多一个月过去了,因为你没有毫无价值的堆栈交换&#34;专家&#34;和&#34;大师&#34;我不会回答我的问题,我被迫接受自己并学习KnockoutJS。所以,我回答了我自己的问题,而且它可能比你给我的答案更好。但对于所谓的专家&#34;和&#34;大师&#34;我发出挑战 - 改进设计。让我们看看你是否辜负了炒作。 :)

</comedian>

顺便说一下,在我的实际应用程序中,助手类还分配了其他几个属性,这就是我制作AsstCol类的原因。 AsstCol实际上是一个单元级实例,也许它可以/应该被命名为AsstCell,但无论如何。它也是教师Asst专栏的一部分,但这是应用程序中最大的问号,但它仍然具有功能。

查看:

<table>
    <tr style="color:white; background-color:grey">
    <td></td>
    <!-- ko foreach: columns -->
    <td colspan="2" data-bind="text: $data"></td>
    <!-- /ko -->
    </tr>
    <tbody data-bind="foreach: teachers">
    <tr>
        <td data-bind="text: name"></td>
        <!-- ko foreach: asstCols -->
        <td class="cellLeft"><input data-bind="checked: isChecked, click: $root.updatePercentage" type="checkbox" /></td>
        <td class="cellRight" data-bind="text: accounting.toFixed(percentage(), 0) + '%'"></td>
        <!-- /ko -->
    </tr>
    </tbody>
</table>
<br />
<table class="reset">
    <tr>
    <td>Teacher:</td>
    <td style="padding-left:5px;">
        <select data-bind="
        options: availableTeachers,
        optionsText: 'name',
        value: selectedTeacher">
        </select>
    </td>
    <td style="padding-left:5px;"><button data-bind="click: addTeacher">Add Teacher</button></td>
    <td style="padding-left:5px;"><button data-bind="click: addAsst">Add Assistant</button></td>
    </tr>
</table>

<ul data-bind="foreach: teachers">
    <li>
    <span data-bind="text: name"></span>
    <ul data-bind="foreach: asstCols">
        <li>
         <span data-bind="text: asstName"></span> :  <span data-bind="text: percentage"></span>
        </li>
    </ul>
    </li>

</ul>

<!-- <textarea style="width:620px; height:300px;" data-bind="text: ko.toJSON($data)"></textarea> -->

查看型号:

// methods
var Teacher = function (id, name, asstCols) {
    this.id = id;
    this.name = name;
    this.asstCols = ko.observableArray(asstCols).extend({ rateLimit: 0 });  // trigger just one re-evaluation of computed observable
};

var AsstCol = function (id, asstName, isChecked, percentage) {
    this.id = id;
    this.asstName = ko.observable(asstName);
    this.isChecked = ko.observable(isChecked);
    this.percentage = ko.observable(percentage);
};

var Assistant = function (id, name) {
    this.id = id;
    this.name = name;
};

var viewModel = function (teachers, assistants, columnList) {
    var self = this;
    self.teachers = ko.observableArray(teachers).extend({ rateLimit: 0 });
    self.columns = ko.observableArray();

    // set initial columns
    for (var index in columnList) {
    self.columns.push(columnList[index].asstName());
    };

    self.assistants = ko.observableArray(assistants).extend({ rateLimit: 0 });
    self.selectedTeacher = ko.observable("0");
    self.availableTeachers = ko.observableArray([
    new Teacher(5, "Doug", [{}]),
    new Teacher(6, "Kevin", [{}])
    ]);

    // methods
    // passes current item as the first parameter
    self.updatePercentage = function (asstCol) {
    var totalChecked = 0, percentage = 0;

    ko.utils.arrayForEach(self.teachers(), function (teacher) {
        for (var i = 0; i < self.columns().length; i++) {
        if (teacher.asstCols()[i].id == asstCol.id) {
            if (teacher.asstCols()[i].isChecked()) {
            totalChecked++;
            break;
            }
        }
        }
    });

    percentage = 100 / totalChecked;

    ko.utils.arrayForEach(self.teachers(), function (teacher) {
        for (var i = 0; i < self.columns().length; i++) {
        if (teacher.asstCols()[i].id == asstCol.id) {
            if (teacher.asstCols()[i].isChecked())
            teacher.asstCols()[i].percentage(percentage);
            else
            teacher.asstCols()[i].percentage(0);
        }
        }
    });

    return true;    // return default browser behavior to allow check/uncheck
    };

    // operations
    self.addTeacher = function () {
    self.teachers.push(new Teacher(ko.unwrap(self.selectedTeacher().id), ko.unwrap(self.selectedTeacher().name), undefined));

    // add each of the asst columns
    ko.utils.arrayForEach(self.assistants(), function (assistant) {
        self.teachers()[self.teachers().length - 1].asstCols.push(new AsstCol(assistant.id, assistant.name, false, 0));
    });
    self.teachers.sort(function(a, b) {
        return a.name.toLowerCase() == b.name.toLowerCase() ? 0 : (a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1);
    });
    self.columns.sort();
    ko.utils.arrayForEach(self.teachers(), function (teacher) {
        teacher.asstCols.sort(function(a, b) {
        return a.asstName().toLowerCase() == b.asstName().toLowerCase() ? 0 : 
        (a.asstName().toLowerCase() < b.asstName().toLowerCase() ? -1 : 1);
        });
    });

    // remove teacher from dropdown and reset the selection
    self.availableTeachers.remove(function (item) { return item.id == ko.unwrap(self.selectedTeacher().id); });
    self.selectedTeacher("0");
    };
    self.addAsst = function () {
    self.assistants.push(new Assistant(22, "Abby"));
    self.assistants.sort(function(a, b) {
        return a.name.toLowerCase() == b.name.toLowerCase() ? 0 : (a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1);
    });
    ko.utils.arrayForEach(self.teachers(), function (teacher) {
        teacher.asstCols.push(new AsstCol(22, "Abby", false, 0));
    });
    self.columns.push("Abby");        
    };
};

var initialTeachers = [
    new Teacher(1, "Jeff", [
    new AsstCol(20, "Susie", true, 50),
    new AsstCol(21, "Bobby", true, 33)
    ]),
    new Teacher(2, "Joe", [
    new AsstCol(20, "Susie", false, 0),
    new AsstCol(21, "Bobby", true, 33)
    ]),
    new Teacher(3, "Josie", [
    new AsstCol(20, "Susie", true, 50),
    new AsstCol(21, "Bobby", true, 33)
    ])
];

var initialAssistants = [
    new Assistant(21, "Bobby"),
    new Assistant(20, "Susie")
];

var vm = new viewModel(initialTeachers, initialAssistants, initialTeachers[0].asstCols());
ko.applyBindings(vm);

CSS:

table {
    border-spacing: 0px;
    border-collapse: collapse;
}
td, th {
    border: solid 1px black;
    padding: 2px;
}
.reset td, th {
    border: 0;
    padding: 0;
}
.cellLeft {
    border: 0;
    border-left:solid 1px black;
    border-bottom:solid 1px black;
    padding:1px 1px 1px 4px;
}
.cellRight {
    border: 0;
    border-right:solid 1px black;
    border-bottom:solid 1px black;
    padding:1px; padding:1px 4px 1px 1px;
}

JSFiddle