我和KnockoutJs一起玩MVC3几个星期了,我一直想知道什么
说我有一个mvc动作,它返回一个简单的列表
public ActionResult AddFoos()
{
List<Foo> funds = new List<Foo>();
.. etc
return Json(funds.ToList(), JsonRequestBehavior.AllowGet);
}
然后传递到视图模型
var viewModel = {
fooChocies: ko.mapping.fromJS([]),
fooOptions: ko.mapping.fromJS([]),
loadInitialData: function () {
ko.mapping.fromJS(serverData, dataMappingOptions, viewModel.fooOptions);
},
};
在我的Foo类型中,我也有显示或隐藏ui元素的属性
var Foo = function (data, preselect) {
var self = this;
self.id = ko.observable(data.id);
self.Name = ko.observable(data.Name);
self.number = ko.observable('');
self.showProducts = ko.observable(false); <---
self.displayBigPanel = ko.observable(false); <---
}
我的方法是动态创建表单元素
通过ModelBinder并创建一个List&lt; Foo&gt;作为控制器动作的参数。
最后问题 ...
当用户导航回此页面时,我需要使用用户制作的fooChoices恢复用户界面。
似乎我有两个选择来重建用户选择(都通过扩展方法)
使用原始json,如
所示ko.toJSON(viewModel.fooChoices))
除基本模型属性外,还提供有关隐藏和显示UI元素的信息,
sb.Append("viewModel.fooCghoices= ko.mapping.fromJS(" + json + ");");
sb.Append("ko.applyBindings(viewModel);");
return new HtmlString(sb.ToString());
因此将客户端ui信息发送到服务器并返回
或
直接操作ViewModel,模拟用户操作
sb.Append("viewModel.fooChoices.push(new Foo(1509));");
sb.Append("viewModel.fooChoices()[0].selectedSubFoo = new Foo(273);");
sb.Append("viewModel.fooChoices()[0].showProducts(true);");
在任何一种情况下,它感觉有点偏离,并且那里有更好的模式。想知道一种方式是否比上述方式更好或者没有方法。
非常感谢
答案 0 :(得分:1)
目前,您的控制器方法返回Foo
列表。考虑创建一个更复杂的对象,同时保存你的Foos和你的选择。
public class FooViewModel
{
public List<Foo> Foos { get; set; };
public UserChoices { get; set; }
}
更改控制器方法,使其返回FooViewModel
。这意味着用户选择将与您感兴趣的任何Foos一起返回。
public ActionResult AddFoos()
{
// Are there any choices stored in session?
// Use those first, otherwise create a new UserChoices object
UserChoices choices =
Session["User.Choices"] as UserChoices ?? new UserChoices();
List<Foo> funds = new List<Foo>();
.. etc
FooViewModel vm = new FooViewModel() { Foos = funds; UserChoices = choices };
// Return the whole object, containing Choices and Foos
return Json(vm, JsonRequestBehavior.AllowGet);
}
另外,请考虑某种动作过滤器,以便您轻松传递完整的对象。 ObjectFilter是一个很好的方法。它允许您轻松传递复杂的对象结构,而无需依赖特定的标记。
http://www.c-sharpcorner.com/blogs/863/passing-json-into-an-asp-net-mvc-controller.aspx
控制器方法上方的ObjectFilter。非常简单,只是声明控制器应该尝试将名为fooStuff
的任何传入参数视为类型FooViewModel
。
[HttpPost,
ObjectFilter(Param = "fooStuff", RootType = typeof(FooViewModel)),
UnitOfWork]
public JsonResult ProcessFoos(FooViewModel fooStuff) {
通过定义相应的JavaScript视图模型,您可以将整个事物转换为json字符串并将其传递给完全填充的控制器方法。
因此,相应的js vm的例子是: -
var fooViewModel = function(data) {
var self = this;
self.Foos = ko.observableArray(data.Foos);
self.UserChoices = ko.observable(data.UserChoices);
// Don't worry about properties or methods that don't exist
// on the C# end of things. They'll just be ignored.
self.usefulJSOnlyMethod = function() {
// behaviour
};
}
var userChoice = function(data) {
var self = this;
self.DinnerId = ko.observable(data.DinnerId);
}
对由ObjectFilter
修饰的控制器方法的典型调用将是这样的(假设self是fooViewModel
): -
var queryData = ko.mapping.toJSON(self);
$.ajax(
//...
data: queryData,
来自js vm的任何匹配(同名,相同类型区分大小写)属性将自动以控制器方法的fooStuff
参数结束。是时候保存这些选择: -
另请注意,我在会话中坚持用户选择。这将允许他们被任何其他可能需要它们的控制器方法拾取(例如上面的AddFoos)。
[HttpPost,
ObjectFilter(Param = "fooStuff", RootType = typeof(FooViewModel)),
UnitOfWork]
public JsonResult ProcessFoos(FooViewModel fooStuff)
{
// hey! I have a fully mapped FooViewModel right here!
// ( _fooServices.ProcessFoos will return updated version of viewmodel )
FooViewModel vm = _fooServices.ProcessFoos(fooStuff);
// What about those choices?
// Put them in the session at this point in case anyone else comes asking
// after them.
Session["User.Choices"] = vm.UserChoices;
return Json(vm);
}
因为我们: -
....此时恢复选择很简单。引用包含用户所选选项的视图模型部分。
<select id="dinnerChoice"
data-bind="value: UserChoices.DinnerId"
>
</select>