大规模MVC Web应用程序 - 使用Knockout和Razor

时间:2013-02-08 00:44:12

标签: jquery asp.net-mvc architecture knockout.js knockout-mapping-plugin

我正在开发一个大型Web应用程序,我遇到了使用knockout和MVC正确构建它的问题。

我目前有一个基本视图模型,我将其传递到我的所有视图中,其中包含有关当前登录用户的基本数据

//Example Code
public class BaseViewModel {
    public String FullName;
    public String Email;

    //A bunch of other similar values
}

然后我拥有特定于网站上每个页面的视图模型(即.profile)继承BaseViewModel类

//profile.cshtml
public class UserProfileViewModel : BaseViewModel {
    public String Address;
    public String PhoneNumber;

    //Other similar values
}

//entity.cshtml
public class UserEntityViewModel : BaseViewModel {
    public String EntityValue;
    public Int32 EntityNumber;

    //Other similar values
}

我使用knockout observables在javascript中重新定义了我的整个数据模型,这样我就可以在MVC模型中创建任何类型的对象。然后我在javascript中定义了几个与我的MVC视图模型基本相同的viewmodel,以允许加载,创建,编辑,删除功能。

我发现这在我想要从个人资料页面创建新实体的情况下非常有用。我可以创建一个viewmodel的新实例并将其绑定到一个新的jquery对话框,当单击OK按钮时,我可以在我的knockout viewmodel中调用一个将保存或创建实体的事件。

当我想将数据加载到特定页面时(即我想使用我的挖掘数据模型填充配置文件页面),这种架构不能很好地工作。我遇到了一些问题,我需要确定我所在的页面并将特定的viewmodel绑定到该页面。我真的不认为以下代码非常优雅。

$(function() {
    if ($('.tweak-list').length) {
        var id = $('.tweak-list').attr('data-songid');
        var vm = new my.tweaksviewmodel({ songid: id });
        ko.applyBindings(vm);
    }
});

有没有人对我应该如何构建这个有任何建议?我认为最好在javascript中创建一个BaseViewModel并使用knockout映射插件http://knockoutjs.com/documentation/plugins-mapping.html自动创建我的各种数据模型。也就是说,它并没有真正解决确定模型绑定到哪个页面的问题,以便它可以加载适当的数据。

修改1

这个架构还有一个限制,我不能利用模态弹出窗口来添加数据,因为我需要将viewmodel重新绑定到每个模态..

有人有什么想法吗?

1 个答案:

答案 0 :(得分:4)

我建议按照以下方式进行构建:

为每个页面创建一个具有特定KnockOut ViewModel的不同JavaScript模块:

var app = app || {};
app.pages = app.pages || {};

app.pages.userProfile = (function () {

    function UserProfileViewModel() {
        //view model specific code
    };

    function init() {
        ko.applyBindings(new UserProfileViewModel());
    };

    return {
        init: init
    };
}());

指定应在剃刀视图中使用哪个JS模块:

@model dynamic

@{
    ViewBag.CurrentPageJsHandler = "app.pages.userProfile";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<p>Page specific content</p>

将特定于页面的模块初始化代码添加到布局文件中:

<html>
<body>   
    <div id="content">
        @RenderBody()
    </div>      
    @if (@ViewBag.CurrentPageJsHandler != null)
    {
        <script>
            $(function() {
                app.currentPage = @ViewBag.CurrentPageJsHandler;
                app.currentPage.init();
            });
        </script>
    }    
</body>
</html>

这样,您可以将所有与页面相关的代码封装在不同的模块中。由于每个JS模块都有唯一的名称,因此您可以将它们全部绑定在一起并包含在布局中。