如何在 ASP.NET MVC 视图模型中使用淘汰.js?

赏金

已经有一段时间了,我还有一些未解决的问题。我希望通过增加赏金也许这些问题会得到答案。

  1. 如何在淘汰.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. I want to use plugins for instance I want to be able to rollback observables as if a user cancels a request I want to be able to go back to the last value. From my research this seems to be achieved by people making plugins like editables

    How do I use something like that if I am using mapping? I really don’t want to go to a method where I have in my view manual mapping were I map each MVC viewMode field to a KO model field as I want as little inline javascript as possible and that just seems like double the work and that’s why I like that mapping.

  5. I am concerned that to make this work easy (by using mapping) I will lose a lot of KO power but on the other hand I am concerned that manual mapping will just be a lot of work and will make my views contain too much information and might become in the future harder to maintain(say if I remove a property in the MVC model I have to move it also in the KO viewmodel)


Original Post

I am using asp.net mvc 3 and I looking into knockout as it looks pretty cool but I am having a hard time figuring out how it works with asp.net mvc especially view models.

For me right now I do something like this

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


}

我会有一个 Vm,它有一些像 CourseName 这样的基本属性,并且在它之上有一些简单的验证。如果需要,Vm 模型也可能包含其他视图模型。

然后,如果我要使用 html 帮助器来帮助我向用户显示它,我将把这个 Vm 传递给 View。

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

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

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

对于敲出.js,它是完全不同的,因为你现在已经有了它的视图模型,而且从我看到的所有示例来看,它们不使用 html 助手。

你是如何使用 MVC 的这两个特性的?

我找到了 这个视频,它简要地(视频的最后几分钟@18:48)介绍了一种使用视图模型的方法,基本上是使用一个内联脚本,其中包含了在 ViewModel 中分配值的淘汰.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

所以我有一个起点,我想至少将更新当我做了一些更多的游戏,以及如何工作。

我试图通过交互式教程,但使用视图模型代替。

还不知道如何处理这些部分

function AppViewModel() {
this.firstName = ko.observable("Bert");
this.lastName = ko.observable("Bertington");
}

或者

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);
}
90204 次浏览

你可以把你的 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.map 更具体。我通常只会使用它,如果你的视图模型是一种方式(即。Server-> client) ,然后数据以某种不同的格式回发(或根本不回发)。或者,如果您的 javascript 视图模型需要采用与服务器模型大不相同的格式。

我想我已经总结了你们所有的问题,如果我遗漏了什么,请让我知道(如果你能在一个地方总结你所有的问题就好了 =)

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

下载 完整代码

如何在淘汰.js 中使用 html 助手

这很简单:

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

地点:

  • value: CourseId表示您正在将 input控件的 value属性与来自您的模型和脚本模型的 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.edit 插件的兼容性

我原以为会更复杂,但结果证明,集成真的很容易,为了让你的模型可编辑只需添加以下行: (记住,在这种情况下,我使用的是一个混合模型,从服务器和添加扩展在客户端和可编辑简单工作... 这是伟大的) :

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

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

this.isInEditMode = ko.observable(false);

关于你的数组问题

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

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

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

enter image description here

基本上,这里有两个列表,使用 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" })

因为当我提交表单时,我的字段是禁用的,所以值没有传输到服务器,这就是为什么我添加了两个隐藏字段来完成这个任务