[英]How to use knockout.js with ASP.NET MVC ViewModels?
赏金
已经有一段时间了,我仍然有几个悬而未决的问题。 我希望通过悬赏,也许这些问题会得到解答。
为什么需要准备好文档才能使其正常工作(有关更多信息,请参见第一个编辑)
如果我在视图模型中使用基因剔除映射,该怎么做? 由于映射,我没有功能。
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 };
我想使用插件,例如,我希望能够回滚observables,就好像用户取消了我希望能够返回到上一个值的请求一样。 根据我的研究,这似乎是通过制作可编辑的插件来实现的
如果我正在使用映射,该如何使用类似的东西? 我真的不想去我在我的视图中手动映射的方法,因为我想将每个MVC viewMode字段映射到KO模型字段,因为我想要尽可能少的内联javascript,这似乎是工作的两倍,那是为什么我喜欢那种映射。
我担心要使此工作容易(通过使用映射)会失去很多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 };
我能够找出第一个问题。 没有第二个问题的线索。 不过。 任何人有任何想法吗?
@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
插件的兼容性
这很容易:
@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(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; }
}
@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.