Knockout-在共享observableArray时,在模板中获取和设置selectbox值的策略



我刚刚开始尝试JavaScript、JQuery和Knockout.js(我通常在Java的服务器端工作),它非常出色——然而,我遇到了困难,我非常怀疑我是否在"照本宣科"地做我想做的事。

我在这里为它制作了一把小提琴:http://jsfiddle.net/kcyhw/

我的任务:

  • 我需要创建一个可以在整个网站上重复使用的模板。Fx。一个包含创建、编辑和删除用户的可能性的模板,这将使在"用户配置"窗口中创建用户变得容易,或者作为向导的一部分。无论哪种方式,控制一切的逻辑都应该是一样的。然而,除了共享相同的数据数组Observable之外,选择选项当然不应该相互观察。现在,它完全是一个选择框。我使用JQuery.serialize将整个表单转换为键值,并发送到服务器,因此,重要的是,我不仅要获得值,还要将其"保存"在选择框的值属性中

我的问题:

  • 我根本不知道Knockout.js和选择框是如何连接的。在选择框和属性部分中,所有对象都会根据各自的值(id和全名)进行精细显示。当将serialize与jQuery一起使用时,它只会打印:"perselect="。。。所以它没有得到值

我尝试了以下操作:

  • 在数据绑定中使用optionValue是有效的,它绑定到"值",但是,我可以看到它"接管"了我的绑定,并杀死了我从对象中检索到的文本。我把它取下来,继续。。。

  • 计算值,但它没有成功,因为模板想要(据我所知)一个文本对象,并且函数不能引用这样的对象中的其他属性。

  • 创建了自己的绑定,这样我就可以获得对元素(选择框)和所有其他绑定值的引用。在这个函数中,我尝试使用jQuery在传入的元素上设置属性"value",但它不起作用。我还可以看到,绑定被调用了4次(这可能是因为它调用init,然后为我创建的每个包含选择框的模板进行更新)。

对我来说,这看起来像是我制造了一个该死的烂摊子,如果一些聪明的人能为我指明解决这个问题的正确方向,我将不胜感激。资源、代码片段、建议。。。不管你有什么。

代码:

<html>
<head>
<script src="javascript/jquery-1.10.2/jquery-1.10.2.js"></script>
<script src="javascript/knockout-2.3.0/knockout-2.3.0.js"></script>
<script src="javascript/knockout.mapping-master-2.0/knockout.mapping-latest.js"></script>
<script type="text/javascript" src="javascript/json2-2.0/json2.js"></script>
<title>A Knockout Demo</title>

<script>
    /**
     * JQuery Function
     */
    $(document).ready(function() {
        // Domain Object
        var Person = function(id, fullname) {
            var self = this;
            self.id = id;
            self.fullname = fullname;
        };
        // Knockout Model
        var KoModel = function() {
            var self = this;
            // Declare observables
            self.persons = ko.observableArray();
            // Allows observables to share an array without observing each other
            self.createPersonSelector = function(namevalue) {
                var person = new Object();
                person.selectedPerson = ko.observable();
                person.name = namevalue;
                return person;
            }
            // Prints a serialized string which could be sent to the server
            self.printFormElements = function(formElements) {
                alert($(formElements).serialize());
            }
            // Will change the person select value, to a real value
            self.changePersonSelectValue = function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                var value = valueAccessor(), allBindings = allBindingsAccessor();
                // Next, whether or not the supplied model property is observable, get its current value
                var valueUnwrapped = ko.unwrap(value);
                // Now manipulate the DOM element
                var $eleme = $(element);
                if ($eleme == null) {
                    return;
                }
                // Change to item number two in the list *doesn't work*.
                $eleme.val(2);
            };
            // Person selectbox value binding
            ko.bindingHandlers.personSelect = {
            init : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                self.changePersonSelectValue(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
            },
            update : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                self.changePersonSelectValue(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
            }
            };
            // Put some test-data into the array
            self.persons.push(new Person(1, 'Martin Smith'));
            self.persons.push(new Person(2, 'Andy Gregersen'));
            self.persons.push(new Person(3, 'Thomas Peep'));
        };
        // Apply bindings
        ko.applyBindings(new KoModel());
    });
</script>
<script type="text/html" id="person-template">
<span>Choose ID: </span><select data-bind="options: $root.persons, optionsText: 'id', personSelect: true, value:selectedPerson, attr: {'name': name, 'id': name}"></select></br>
<span>ID:</span> <span data-bind="text: selectedPerson().id"></span></br>
<span>Full Name: </span> <span data-bind="text: selectedPerson().fullname"></span></br>
</script>
<body>
    <h1>Person Select One</h1>
    <form data-bind="submit: printFormElements">
        <div
            data-bind="template: { name: 'person-template', data:createPersonSelector('personselect')}"></div>
        <button type="submit">Submit</button>
        </br>
    </form>
    <h1>Person Select Two</h1>
    <form data-bind="submit: printFormElements">
        <div
            data-bind="template: { name: 'person-template', data:createPersonSelector('personselecttwo')}"></div>
        <button type="submit">Submit</button>
        </br>
    </form>
</body>
</html>

对我来说,最简单的回答方法是改变我可能会做的很多不同的事情。阻碍您的主要问题是,您正在使用jQuery来处理可以由KO以更容易的方式处理的事情。

下面是我要更改的内容,以查看完整的结果。看看这个fiddle(它根本不使用jQuery)。

将您的模型简化为以下内容:

var KoModel = function() {
    var self = this;
    // Declare observables
    self.persons = ko.observableArray();
    // Prints a serialized string which could be sent to the server
    self.printFormElements = function() {
        alert(ko.mapping.toJSON(self));
    }
    // Hold selected person
    self.selectedPersons = ko.observableArray();
};

需要注意的几点:

  • "print"函数现在使用映射插件,它非常适合序列化视图模型
  • 它要短得多。不再需要"CreatePersonSelector"one_answers"changePersonSelectValue"函数,也不再需要自定义绑定
  • selectedPersons现在是一个可观察的,并且是一个数组,因为视图可能是多选的
  • 附带说明一下,我将添加测试值放置在ViewModel之外

这对应于以下启动模板的视图:

<div data-bind="template: { name: 'person-template' }"></div>

我暂时删除了data位。这意味着该代码的每个实例都将绑定到(相同的)$root视图模型。如果你不想这样,我建议创建一个容器视图模型来容纳几个KoModel s。

模板如下:

<span>Choose ID: </span>
<select data-bind="options: persons, optionsText: 'fullname', selectedOptions: selectedPersons"></select><br />
<!-- ko foreach: selectedPersons -->
<span>ID:</span> <span data-bind="text: id"></span><br />
<span>Full Name: </span> <span data-bind="text: fullname"></span><br />
<!-- /ko -->

这是一个笑话:

  • data-bind要简单得多。您不需要篡改value属性,因为Knockout会将每个option绑定到数组中的特定项
  • 这使您可以自由使用文本的fullname
  • selectedOptions位告诉Knockout在视图模型中存储所选项目的位置
  • 所选择的选项显示在foreach中,因为select可能是multiple选择

现在视图模型中的ko.mapping.toJSON(self)调用将生成如下内容:

{
    "persons": [{
        "id": 1,
        "fullname": "Martin Smith"
    }, {
        "id": 2,
        "fullname": "Andy Gregersen"
    }, {
        "id": 3,
        "fullname": "Thomas Peep"
    }],
    "selectedPersons": [{
        "id": 2,
        "fullname": "Andy Gregersen"
    }]
}

正如您所看到的,所选人员的列表就在那里,将发送到服务器。默认情况下,其余部分都在那里,但映射插件可以进行非常详细的配置。

希望这能帮助并解决您的问题!

最新更新