繁体   English   中英

如何与ASP.NET MVC ViewModels一起使用Knockout.js?

[英]How to use knockout.js with ASP.NET MVC ViewModels?

赏金

已经有一段时间了,我仍然有几个悬而未决的问题。 我希望通过悬赏,也许这些问题会得到解答。

  1. 您如何将Knockout.js与html助手一起使用
  2. 为什么需要准备好文档才能使其正常工作(有关更多信息,请参见第一个编辑)

  3. 如果我在视图模型中使用基因剔除映射,该怎么做? 由于映射,我没有功能。

     function AppViewModel() { // ... leave firstName, lastName, and fullName unchanged here ... this.capitalizeLastName = function() { var currentVal = this.lastName(); // Read the current value this.lastName(currentVal.toUpperCase()); // Write back a modified value }; 
  4. 我想使用插件,例如,我希望能够回滚observables,就好像用户取消了我希望能够返回到上一个值的请求一样。 根据我的研究,这似乎是通过制作可编辑的插件来实现的

    如果我正在使用映射,该如何使用类似的东西? 我真的不想去我在我的视图中手动映射的方法,因为我想将每个MVC viewMode字段映射到KO模型字段,因为我想要尽可能少的内联javascript,这似乎是工作的两倍,那是为什么我喜欢那种映射。

  5. 我担心要使此工作容易(通过使用映射)会失去很多KO功能,但另一方面,我担心手动映射会很繁重,并且会使我的视图包含太多信息,并且将来可能会变得难以维护(例如,如果我删除了MVC模型中的属性,那么我也必须在KO视图模型中移动它)


原始帖子

我正在使用asp.net mvc 3,我正在研究淘汰赛,因为它看起来很酷,但是我很难弄清楚它如何与asp.net mvc一起工作,尤其是视图模型。

现在对我来说,我做这样的事情

  public class CourseVM { public int CourseId { get; set; } [Required(ErrorMessage = "Course name is required")] [StringLength(40, ErrorMessage = "Course name cannot be this long.")] public string CourseName{ get; set; } public List<StudentVm> StudentViewModels { get; set; } } 

我将拥有一个具有一些基本属性(如CourseName)的Vm,并且在其之上将进行一些简单的验证。 如果需要,Vm模型也可能包含其他视图模型。

然后,我会将这个Vm传递给View,否则我将使用html helper来帮助我将其显示给用户。

 @Html.TextBoxFor(x => x.CourseName) 

我可能会有一些foreach循环或一些将数据从“学生视图模型”集合中获取的方法。

然后,当我提交表单时,我将使用jquery并serialize array并将其发送到控制器操作方法,该方法会将其绑定回viewmodel。

有了kickout.js,一切都不同了,因为您现在有了它的视图模型,从我看到的所有示例中,它们都没有使用html helper。

您如何在Knockout.js中使用MVC的这两个功能?

我找到了这个视频,并且它简短地( 视频的最后几分钟@ 18:48)通过基本上具有一个内联脚本进入了使用视图模型的方式,该脚本具有被分配了ViewModel中的值的kickout.js视图模型。

这是唯一的方法吗? 在我的示例中包含视图模型集合怎么样? 我是否必须有一个foreach循环或某种东西来提取所有值并将其分配给淘汰赛?

至于html助手,视频对此没有任何说明。

这是两个使我感到困惑的地方,因为似乎没有多少人谈论它,这使我困惑于当示例只是一些硬编码的值示例时,初始值和一切如何进入视野。


编辑

我正在尝试达林·迪米特洛夫(Darin Dimitrov)提出的建议,但这似乎行得通(尽管我必须对他的代码进行一些更改)。 不知道为什么我必须准备好使用文档,但是如果没有它,一切都会准备就绪。

 @model MvcApplication1.Models.Test @{ Layout = null; } <!DOCTYPE html> <html> <head> <title>Index</title> <script src="../../Scripts/jquery-1.5.1.js" type="text/javascript"></script> <script src="../../Scripts/knockout-2.1.0.js" type="text/javascript"></script> <script src="../../Scripts/knockout.mapping-latest.js" type="text/javascript"></script> <script type="text/javascript"> $(function() { var model = @Html.Raw(Json.Encode(Model)); // Activates knockout.js ko.applyBindings(model); }); </script> </head> <body> <div> <p>First name: <strong data-bind="text: FirstName"></strong></p> <p>Last name: <strong data-bind="text: LastName"></strong></p> @Model.FirstName , @Model.LastName </div> </body> </html> 

我不得不将其包装在一个jQuery文档周围,以使其能够正常工作。

我也收到此警告。 不知道这是怎么回事。

 Warning 1 Conditional compilation is turned off -> @Html.Raw 

因此,我有一个起点,我想至少会在我进行更多尝试以及其工作方式后更新。

我正在尝试阅读交互式教程,但改用ViewModel。

尚不确定如何解决这些问题

function AppViewModel() {
    // ... leave firstName, lastName, and fullName unchanged here ...

    this.capitalizeLastName = function() {
        var currentVal = this.lastName();        // Read the current value
        this.lastName(currentVal.toUpperCase()); // Write back a modified value
    };

要么

 function AppViewModel() { // ... leave firstName, lastName, and fullName unchanged here ... this.capitalizeLastName = function() { var currentVal = this.lastName(); // Read the current value this.lastName(currentVal.toUpperCase()); // Write back a modified value }; 


编辑2

我能够找出第一个问题。 没有第二个问题的线索。 不过。 任何人有任何想法吗?

  @model MvcApplication1.Models.Test @{ Layout = null; } <!DOCTYPE html> <html> <head> <title>Index</title> <script src="../../Scripts/jquery-1.5.1.js" type="text/javascript"></script> <script src="../../Scripts/knockout-2.1.0.js" type="text/javascript"></script> <script src="../../Scripts/knockout.mapping-latest.js" type="text/javascript"></script> <script type="text/javascript"> $(function() { var model = @Html.Raw(Json.Encode(Model)); var viewModel = ko.mapping.fromJS(model); ko.applyBindings(viewModel); }); </script> </head> <body> <div> @*grab values from the view model directly*@ <p>First name: <strong data-bind="text: FirstName"></strong></p> <p>Last name: <strong data-bind="text: LastName"></strong></p> @*grab values from my second view model that I made*@ <p>SomeOtherValue <strong data-bind="text: Test2.SomeOtherValue"></strong></p> <p>Another <strong data-bind="text: Test2.Another"></strong></p> @*allow changes to all the values that should be then sync the above values.*@ <p>First name: <input data-bind="value: FirstName" /></p> <p>Last name: <input data-bind="value: LastName" /></p> <p>SomeOtherValue <input data-bind="value: Test2.SomeOtherValue" /></p> <p>Another <input data-bind="value: Test2.Another" /></p> @* seeing if I can do it with p tags and see if they all update.*@ <p data-bind="foreach: Test3"> <strong data-bind="text: Test3Value"></strong> </p> @*took my 3rd view model that is in a collection and output all values as a textbox*@ <table> <thead><tr> <th>Test3</th> </tr></thead> <tbody data-bind="foreach: Test3"> <tr> <td> <strong data-bind="text: Test3Value"></strong> <input type="text" data-bind="value: Test3Value"/> </td> </tr> </tbody> </table> 

控制者

  public ActionResult Index() { Test2 test2 = new Test2 { Another = "test", SomeOtherValue = "test2" }; Test vm = new Test { FirstName = "Bob", LastName = "N/A", Test2 = test2, }; for (int i = 0; i < 10; i++) { Test3 test3 = new Test3 { Test3Value = i.ToString() }; vm.Test3.Add(test3); } return View(vm); } 

我想我已经汇总了您所有的问题,如果我错过了什么,请告诉我( 如果您可以将所有问题汇总到一个地方,那就很好 =))

注意。 与添加的ko.editable插件的兼容性

下载完整代码

您如何将Knockout.js与html助手一起使用

这很容易:

@Html.TextBoxFor(model => model.CourseId, new { data_bind = "value: CourseId" })

哪里:

  • value: CourseId表明您绑定的value的财产input与控制CourseId从你的模型属性和脚本模型

结果是:

<input data-bind="value: CourseId" data-val="true" data-val-number="The field CourseId must be a number." data-val-required="The CourseId field is required." id="CourseId" name="CourseId" type="text" value="12" />

为什么需要准备好文档才能使其正常工作(有关更多信息,请参见第一个编辑)

我还不明白为什么您需要使用ready事件来序列化模型,但是看来这只是必需的 (不过不必担心)

如果我在视图模型中使用基因剔除映射,该怎么做? 由于映射,我没有功能。

如果我正确理解,您需要向KO模型添加新方法,那么很容易合并模型

有关更多信息,请在“从不同来源映射”部分中

function viewModel() {
    this.addStudent = function () {
        alert("de");
    };
};

$(function () {
    var jsonModel = '@Html.Raw(JsonConvert.SerializeObject(this.Model))';
    var mvcModel = ko.mapping.fromJSON(jsonModel);

    var myViewModel = new viewModel();
    var g = ko.mapping.fromJS(myViewModel, mvcModel);

    ko.applyBindings(g);
});

关于您收到的警告

警告1条件编译已关闭-> @ Html.Raw

您需要使用引号

与ko.editable插件的兼容性

我以为它将变得更加复杂,但是事实证明,集成真的很容易,为了使您的模型可编辑,只需添加以下行:(请记住,在这种情况下,我使用的是服务器和服务器之间的混合模型在客户端中添加扩展名,可编辑项就可以正常工作了。。。

    ko.editable(g);
    ko.applyBindings(g);

从这里开始,您只需要使用插件添加的扩展来玩您的绑定,例如,我有一个按钮可以像这样开始编辑我的字段,并在此按钮中开始编辑过程:

    this.editMode = function () {
        this.isInEditMode(!this.isInEditMode());
        this.beginEdit();
    };

然后,我使用以下代码提交和取消按钮:

    this.executeCommit = function () {
        this.commit();
        this.isInEditMode(false);
    };
    this.executeRollback = function () {
        if (this.hasChanges()) {
            if (confirm("Are you sure you want to discard the changes?")) {
                this.rollback();
                this.isInEditMode(false);
            }
        }
        else {
            this.rollback();
            this.isInEditMode(false);
        }
    };

最后,我有一个字段指示这些字段是否处于编辑模式,这只是绑定enable属性。

this.isInEditMode = ko.observable(false);

关于数组问题

我可能会有一些foreach循环或一些将数据从“学生视图模型”集合中获取的方法。

然后,当我提交表单时,我将使用jquery并序列化数组并将其发送到控制器操作方法,该方法会将其绑定回viewmodel。

您可以使用KO进行相同的操作,在以下示例中,我将创建以下输出:

在此处输入图片说明

基本上,这里有两个列表,这些列表是使用Helpers创建的,并与KO绑定,它们绑定了一个dblClick事件,该事件在被触发时,从当前列表中删除所选项目,并将其添加到另一个列表中,当您发布到Controller ,每个列表的内容作为JSON数据发送并重新附加到服务器模型

块金:

外部脚本

控制器代码

    [HttpGet]
    public ActionResult Index()
    {
        var m = new CourseVM { CourseId = 12, CourseName = ".Net" };

        m.StudentViewModels.Add(new StudentVm { ID = 545, Name = "Name from server", Lastname = "last name from server" });

        return View(m);
    }

    [HttpPost]
    public ActionResult Index(CourseVM model)
    {
        if (!string.IsNullOrWhiteSpace(model.StudentsSerialized))
        {
            model.StudentViewModels = JsonConvert.DeserializeObject<List<StudentVm>>(model.StudentsSerialized);
            model.StudentsSerialized = string.Empty;
        }

        if (!string.IsNullOrWhiteSpace(model.SelectedStudentsSerialized))
        {
            model.SelectedStudents = JsonConvert.DeserializeObject<List<StudentVm>>(model.SelectedStudentsSerialized);
            model.SelectedStudentsSerialized = string.Empty;
        }

        return View(model);
    }

模型

public class CourseVM
{
    public CourseVM()
    {
        this.StudentViewModels = new List<StudentVm>();
        this.SelectedStudents = new List<StudentVm>();
    }

    public int CourseId { get; set; }

    [Required(ErrorMessage = "Course name is required")]
    [StringLength(100, ErrorMessage = "Course name cannot be this long.")]
    public string CourseName { get; set; }

    public List<StudentVm> StudentViewModels { get; set; }
    public List<StudentVm> SelectedStudents { get; set; }

    public string StudentsSerialized { get; set; }
    public string SelectedStudentsSerialized { get; set; }
}

public class StudentVm
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Lastname { get; set; }
}

CSHTML页面

@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>CourseVM</legend>

        <div>
            <div class="editor-label">
                @Html.LabelFor(model => model.CourseId)
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.CourseId, new { data_bind = "enable: isInEditMode, value: CourseId" })
                @Html.ValidationMessageFor(model => model.CourseId)
            </div>

            <div class="editor-label">
                @Html.LabelFor(model => model.CourseName)
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.CourseName, new { data_bind = "enable: isInEditMode, value: CourseName" })
                @Html.ValidationMessageFor(model => model.CourseName)
            </div>
            <div class="editor-label">
                @Html.LabelFor(model => model.StudentViewModels);
            </div>
            <div class="editor-field">

                @Html.ListBoxFor(
                    model => model.StudentViewModels,
                    new SelectList(this.Model.StudentViewModels, "ID", "Name"),
                    new
                    {
                        style = "width: 37%;",
                        data_bind = "enable: isInEditMode, options: StudentViewModels, optionsText: 'Name', value: leftStudentSelected, event: { dblclick: moveFromLeftToRight }"
                    }
                )
                @Html.ListBoxFor(
                    model => model.SelectedStudents,
                    new SelectList(this.Model.SelectedStudents, "ID", "Name"),
                    new
                    {
                        style = "width: 37%;",
                        data_bind = "enable: isInEditMode, options: SelectedStudents, optionsText: 'Name', value: rightStudentSelected, event: { dblclick: moveFromRightToLeft }"
                    }
                )
            </div>

            @Html.HiddenFor(model => model.CourseId, new { data_bind="value: CourseId" })
            @Html.HiddenFor(model => model.CourseName, new { data_bind="value: CourseName" })
            @Html.HiddenFor(model => model.StudentsSerialized, new { data_bind = "value: StudentsSerialized" })
            @Html.HiddenFor(model => model.SelectedStudentsSerialized, new { data_bind = "value: SelectedStudentsSerialized" })
        </div>

        <p>
            <input type="submit" value="Save" data-bind="enable: !isInEditMode()" /> 
            <button data-bind="enable: !isInEditMode(), click: editMode">Edit mode</button><br />
            <div>
                <button data-bind="enable: isInEditMode, click: addStudent">Add Student</button>
                <button data-bind="enable: hasChanges, click: executeCommit">Commit</button>
                <button data-bind="enable: isInEditMode, click: executeRollback">Cancel</button>
            </div>
        </p>
    </fieldset>
}

剧本

<script src="@Url.Content("~/Scripts/jquery-1.7.2.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout-2.1.0.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout.mapping-latest.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/ko.editables.js")" type="text/javascript"></script>

<script type="text/javascript">
    var g = null;
    function ViewModel() {
        this.addStudent = function () {
            this.StudentViewModels.push(new Student(25, "my name" + new Date(), "my last name"));
            this.serializeLists();
        };
        this.serializeLists = function () {
            this.StudentsSerialized(ko.toJSON(this.StudentViewModels));
            this.SelectedStudentsSerialized(ko.toJSON(this.SelectedStudents));
        };
        this.leftStudentSelected = ko.observable();
        this.rightStudentSelected = ko.observable();
        this.moveFromLeftToRight = function () {
            this.SelectedStudents.push(this.leftStudentSelected());
            this.StudentViewModels.remove(this.leftStudentSelected());
            this.serializeLists();
        };
        this.moveFromRightToLeft = function () {
            this.StudentViewModels.push(this.rightStudentSelected());
            this.SelectedStudents.remove(this.rightStudentSelected());
            this.serializeLists();
        };
        this.isInEditMode = ko.observable(false);
        this.executeCommit = function () {
            this.commit();
            this.isInEditMode(false);
        };
        this.executeRollback = function () {
            if (this.hasChanges()) {
                if (confirm("Are you sure you want to discard the changes?")) {
                    this.rollback();
                    this.isInEditMode(false);
                }
            }
            else {
                this.rollback();
                this.isInEditMode(false);
            }
        };
        this.editMode = function () {
            this.isInEditMode(!this.isInEditMode());
            this.beginEdit();
        };
    }

    function Student(id, name, lastName) {
        this.ID = id;
        this.Name = name;
        this.LastName = lastName;
    }

    $(function () {
        var jsonModel = '@Html.Raw(JsonConvert.SerializeObject(this.Model))';
        var mvcModel = ko.mapping.fromJSON(jsonModel);

        var myViewModel = new ViewModel();
        g = ko.mapping.fromJS(myViewModel, mvcModel);

        g.StudentsSerialized(ko.toJSON(g.StudentViewModels));
        g.SelectedStudentsSerialized(ko.toJSON(g.SelectedStudents));

        ko.editable(g);
        ko.applyBindings(g);
    });
</script>

注意:我刚刚添加了以下几行:

        @Html.HiddenFor(model => model.CourseId, new { data_bind="value: CourseId" })
        @Html.HiddenFor(model => model.CourseName, new { data_bind="value: CourseName" })

因为当我提交表单时,我的字段被禁用,所以值没有传输到服务器,这就是为什么我添加了几个隐藏字段来解决问题的原因

您可以将ASP.NET MVC视图模型序列化为javascript变量:

@model CourseVM
<script type="text/javascript">
    var model = @Html.Raw(Json.Encode(Model));
    // go ahead and use the model javascript variable to bind with ko
</script>

您可以浏览淘汰文档中的许多示例。

为了在服务器映射后获得其他计算的属性,您将需要在客户端进一步增强视图模型。

例如:

var viewModel = ko.mapping.fromJS(model);

viewModel.capitalizedName = ko.computed(function() {...}, viewModel);

因此,每次从原始JSON映射时,都需要重新应用计算出的属性。

此外,映射插件还提供了增量更新视图模型的功能,而不是每次来回创建视图模型时都可以重新创建视图模型(使用fromJS的附加参数):

// Every time data is received from the server:
ko.mapping.fromJS(data, viewModel);

然后,在仅映射的属性的模型上执行增量数据更新。 您可以在映射文档中阅读有关此内容的更多信息。

您在对Darin的FluentJSON软件包的评论中提到了这一点 我是它的作者,但是它的用例比ko.mapping更具体。 我通常只会在您的视图模型是一种方式(即服务器->客户端)然后以某种不同的格式回发数据(或根本不回发)的情况下使用它。 或者,如果您的javascript视图模型需要采用与服务器模型完全不同的格式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM