如何将 knockout.js 与 ASP.NET MVC ViewModel 结合使用?

2023-12-13

Bounty

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

  1. 如何将 html 助手与 knockout.js 一起使用
  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. 我想使用插件,例如我希望能够回滚可观察量,就像用户取消请求一样我希望能够返回到最后一个值。根据我的研究,这似乎是通过人们制作插件来实现的可编辑的

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

  5. 我担心为了使这项工作变得容易(通过使用映射),我会失去很多 KO 能力,但另一方面,我担心手动映射会带来大量工作,并使我的视图包含太多信息,并且将来可能会变得更难维护(比如说,如果我删除 MVC 模型中的属性,我也必须在 KO 视图模型中移动它)


Original Post

我正在使用 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 模型中也可能包含其他视图模型。

然后我会将此虚拟机传递给视图,我将使用 html 助手来帮助我将其显示给用户。

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

我可能有一些 foreach 循环或其他东西来从学生视图模型集合中获取数据。

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

对于knockout.js,一切都不同了,因为你现在有了它的视图模型,并且从我看到的所有示例中,它们不使用html助手。

如何将 MVC 的这两个功能与 knockout.js 结合使用?

I found 这个视频它简要地(视频的最后几分钟@ 18:48)介绍了一种使用视图模型的方法,基本上有一个内联脚本,该脚本具有在 ViewModel 中分配值的 knockout.js 视图模型。

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

至于 html 助手,视频中只字未提。

这两个领域让我感到困惑,因为似乎没有多少人谈论它,这让我很困惑,当示例只是一些硬编码的值示例时,初始值和所有内容是如何进入视图的。


Edit

我正在尝试达林·迪米特洛夫(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() {
    this.firstName = ko.observable("Bert");
    this.lastName = ko.observable("Bertington");
}

or

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
    };

Edit 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已添加插件

Download完整代码

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

这很容易:

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

Where:

  • 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序列化模型的事件,但似乎很简单required(不过不用担心)

如果我在视图模型中使用剔除映射,我该如何做这样的事情?因为我没有映射的功能。

如果我理解正确的话,你需要向 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);

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

    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);
        }
    };

最后,我有一个字段来指示字段是否处于编辑模式,这只是为了绑定启​​用属性。

this.isInEditMode = ko.observable(false);

关于你的数组问题

我可能有一些 foreach 循环或其他东西来从学生视图模型集合中获取数据。

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

您可以对 KO 执行相同的操作,在下面的示例中,我将创建以下输出:

enter image description here

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

Nuggets:

  • 牛顿软件
  • jQuery
  • 淘汰赛
  • 淘汰赛映射

外部的scripts.

控制器代码

    [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);
    }

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>
}

Scripts

<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" })

因为当我提交表单时,我的字段被禁用,因此值没有传输到服务器,这就是为什么我添加了几个隐藏字段来实现这一点

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将 knockout.js 与 ASP.NET MVC ViewModel 结合使用? 的相关文章

随机推荐