KnockoutJS - 使用映射插件的基于约定的自动映射无法按预期工作

时间:2013-05-19 10:01:52

标签: knockout.js knockout-mapping-plugin

我正试图抓住KnockoutJS。我从mix11(推特上的朋友之友)中复制了Steven Sanderson的研讨会示例,取得了一些成功。

我正在尝试扩展它,以便我可以从ASP.NET MVC4控制器获取JSON,并自动将数据绑定到viewModel。

我设法通过手动将我的JSON对象映射到带有observable的挖出对象来快速工作,但是,这是一个复杂度低的简单模型。当我真正使用它时,模型可能会更复杂,手动映射将不那么有吸引力。

我认为这是一个ASP.NET MVC4页面是无关紧要的,因为我在标记中获得了有效的JSON。

以下是完整标记:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>Person</title>
    <link href="/KnockoutSample/Content/site.css" rel="stylesheet"/>

    <script src="/KnockoutSample/Scripts/jquery-2.0.0.js"></script>

    <script src="/KnockoutSample/Scripts/modernizr-2.6.2.js"></script>
<script src="/KnockoutSample/Scripts/knockout-2.2.1.debug.js"></script>
<script src="/KnockoutSample/Scripts/knockout.mapping-latest.js"></script>

</head>
<body>




<h2>Person</h2>
<p>Full name: <span data-bind="text: fullName" ></span></p>

<p>First name: <input type="text" data-bind="value: firstName" /></p>
<p>Last name: <input type="text" data-bind="value: lastName" /></p>


<h2>Friends (<span data-bind="text: friends().length"></span>)</h2>
<ol data-bind="template: { name: 'friendsTemplate', foreach:friends}"></ol>

<script id="friendsTemplate" type="text/html">
    <li>
        <input data-bind="value: fullName"/>
        <button data-bind="click: removeFriend">Remove</button>
        <label><input type="checkbox" data-bind="checked: isOnTwitter" />Is On Twitter</label>
        <input type="text" placeholder="Please enter username" data-bind="value: twitterName, visible: isOnTwitter" />
    </li>
</script>
<button data-bind="click: addFriend, enable: friends().length < 5">Add Friend</button>
<button data-bind="click: save">Save</button>



<script type="text/javascript">

    function friend() {

        function instanceOfConstructor(newFriend) {
            return {
                fullName: newFriend.fullName,
                isOnTwitter: newFriend.isOnTwitter,
                twitterName: newFriend.TwitterName,
                removeFriend: function () {
                    viewModel.friends.remove(this);
                }
            };
        }
        function paramatisedConstructor(name, onTwitter, twitterName) {
            return {
                fullName: ko.observable(name),
                isOnTwitter: ko.observable(onTwitter),
                twitterName: ko.observable(twitterName),
                removeFriend: function () {
                    viewModel.friends.remove(this);
                }
            };
        }

        switch (arguments.length) {
            case 1 :
                return instanceOfConstructor(arguments[0]);
            case 3 :
                return paramatisedConstructor(arguments[0], arguments[1], arguments[2]);
        }
    }


    var viewModel = {
        firstName : ko.observable(),
        lastName: ko.observable(),
        friends: ko.observableArray(),
        addFriend: function () {
            this.friends.push(new friend("New Friend", false, null));
        },
        save: function () {
            $.ajax({
                url: "/KnockoutSample/Main/Person",
                type: "POST",
                data: ko.toJSON(this),
                contentType: "application/json",
            }).success(function(result){
                alert(result.message);
            }).fail(function (data) {
                alert(data);
            });
        }
    };

    viewModel.fullName = ko.dependentObservable(function () {
        return this.firstName() + " " + this.lastName();
    }, viewModel);

    ko.applyBindings(viewModel);

    var initialData = '{"firstName":"Ian","lastName":"Robertson","friends":[{"isOnTwitter":false,"TwitterName":"","fullName":"Friend One"},{"isOnTwitter":true,"TwitterName":"@FriendTwo","fullName":"Friend Two"}]}';

    var tmp = ko.mapping.fromJSON(initialData);

    //Convention based auto-mapping does not work
    //ko.mapping.fromJSON(initialData, viewModel);

    //Manual mapping does work
    viewModel.firstName(tmp.firstName());
    viewModel.lastName(tmp.lastName());

    $.each(tmp.friends(), function (i, _friend) {
        viewModel.friends.push(new friend(_friend));
    });

</script>

</body>
</html>

我希望最终可以避免手动映射:

    //Convention based auto-mapping does not work
    //ko.mapping.fromJSON(initialData, viewModel);

    //Manual mapping does work
    viewModel.firstName(tmp.firstName());
    viewModel.lastName(tmp.lastName());

    $.each(tmp.friends(), function (i, _friend) {
        viewModel.friends.push(new friend(_friend));
    });

关于如何使用映射插件来避免手动映射的任何指示都将非常感激。

更新:

<script type="text/javascript">

function friend(name, onTwitter, twitterName) {
    return {
        fullName: ko.observable(name),
        isOnTwitter: ko.observable(onTwitter),
        TwitterName: ko.observable(twitterName),
        removeFriend: function () {
            viewModel.friends.remove(this);
        }
    };
}

var initialData = '@Html.Raw(ViewBag.InitialData)';

var viewModel = {
    firstName: ko.observable(),
    lastName: ko.observable(),
    friends: ko.observableArray()
};

viewModel = ko.mapping.fromJSON(initialData, viewModel);

viewModel.save = function () {
    $.ajax({
        url: "@Url.Action("Person")",
        type: "POST",
        data: ko.toJSON(this),
        contentType: "application/json",
    }).success(function (result) {
        alert(result.message);
    }).fail(function (data) {
        alert(data);
    });
};

viewModel.addFriend = function () {
    this.friends.push(new friend("New Friend", false, null));
};

try {

    viewModel.fullName = ko.dependentObservable(function () {
        return this.firstName() + " " + this.lastName();
    }, viewModel);

    $.each(viewModel.friends(), function (i, _friend) {
        _friend.removeFriend = function () {
            viewModel.friends.remove(this);
        }
    });

    ko.applyBindings(viewModel);

} catch (e) {
    alert(e);
}

这几乎是我想要实现的解决方案。我将继续尝试改进的唯一方法是使用jquery $ .each函数将“removeFriend”函数添加到数组中的每个friend元素。

1 个答案:

答案 0 :(得分:1)

您可以使用fromJS和toJS函数将viewmodel映射到原始数据并返回到viewmodel。

var tmp = ko.mapping.fromJS(ko.mapping.toJS(viewModel));