我正在尝试将部分视图呈现为字符串,因此可以将其作为HTML返回到jquery ajax调用。经过大量的搜索,我发现了这段代码。
public string RenderAsString(string viewName, string modelName, object model)
{
// Set up your spark engine goodness.
var settings = new SparkSettings().SetPageBaseType(typeof(SparkView));
var templates = new FileSystemViewFolder(Server.MapPath("~/Views"));
var engine = new SparkViewEngine(settings) { ViewFolder = templates };
// "Describe" the view (the template, it is a template after all), and its details.
var descriptor = new SparkViewDescriptor().AddTemplate(@"Shared\" + viewName + ".spark");
// Create a spark view engine instance
var view = (SparkView)engine.CreateInstance(descriptor);
// Add the model to the view data for the view to use.
view.ViewData[modelName] = model;
// Render the view to a text writer.
var writer = new StringWriter(); view.RenderView(writer);
// Convert to string
return writer.ToString();
}
但是当执行以下行时:
var view = (SparkView)engine.CreateInstance(descriptor);
我收到以下错误:
动态视图编译失败。一个 对象引用是必需的 非静态字段,方法或属性 “DomainModel.Entities.Region.Id.get。
这是我的部分观点:
<ViewData Model="Region" />
<div id="${ Region.Id }" class="active-translation-region-widget" >
<label>${Region.RegionName}</label>
${ Html.CheckBox("Active") }
</div>
似乎没有认出这个模型。
P.S。当我从父视图中调用视图时如此
<for each="var region in Model">
<ActiveTranslationRegion Region="region" if="region.Active==true"></ActiveTranslationRegion>
</for>
它呈现完美。我做错了什么?
答案 0 :(得分:2)
仅仅看一下,我认为以下几行就是问题:
<ViewData Model="Region" />
相反它应该是:
<viewata model="Region" />
注意小写的“模型”。这是因为model
是一种特殊情况,因为它在幕后执行强类型视图模型的强制转换。最上面的一个将在生成的视图类中定义一个名为Model
的变量,并为其赋值Region
。使用下面的小写选项实际上会创建一个Model
变量,但也会将其强制转换为来自Region
字典的强类型ViewData
实例。
注意在代码中使用Model
时,就像在for each
循环中一样,它必须是大写的,在您的情况下是正确的。再一次,这是唯一的特殊情况,因为它从ViewData
字典中提取强类型模型。
另一件事 - <viewata model="Region" />
必须在父视图中声明,并且每页只能定义一次,因此您无法在局部视图中重新定义它。如果它是局部视图,你应该通过传入模型的一部分来使用它,就像你在上面的第二个例子中所做的那样。
上述异常的原因是因为它试图将Id
属性作为Region
Type 之外的静态项,而不是查询{{1}您的实例 Id
上的属性,作为您的viewmodel的一部分。
作为旁注,获取所需位置的代码有点受损。你可以通过查看一些Direct Usage Samples来找到更合适的方法来做你想做的事情,但我知道这可能只是一个看起来有效的尖峰......:)
更新以回复您的follow up question/answer
我很确定问题在于将Region
传递给以下调用:
Region
......再次命名。是的,我之前说过,每个视图只能有一个<ActiveTranslationRegion Region="region" if="region.Active==true">
,所以你需要做的是从你的部分顶部删除以下内容:
model
这就是造成问题的原因。然后,我会将项目重命名为部分内容,如下所示:
<viewdata model="Region" />
然后你的部分看起来像这样:
<ActiveTranslationRegion ActiveRegion="region" if="region.Active==true">
注意我正在使用<form action="/Translation/DeactivateRegion" class="ui-widget-content active-translation-region-widget">
<input type="hidden" name="Id" value="${ActiveRegion.Id}" />
<label class="region-name">${ ActiveRegion.RegionName }</label>
<input class="deactivate-region-button" type="image" src=${Url.Content("~/Content/Images/Deactivate.png")} alt="Deactivate" />
</form>
,因为在Spark解析器中,ActiveRegion
被声明为变量,并在当前范围内赋值ActiveRegion
当你循环region
时。没有必要虔诚地坚持for loop
- 因为你已经离开并传递了model
的一部分,现在你声明为model
。哦,如果你真的想要,你可以坚持使用ActiveRegion
这个名字,我只是改变它以表明意见,并且因为你的代码中有一个名为Region
的{{1}}我不是一个古怪的问题的忠实粉丝,使用相同的名称作为一个类型可以带来的变量。而且它使它更清晰。
调用Html.RenderPartial方法的缺点并不是很明显。你失去的一件事是Spark提供的3遍渲染。如果你使用标签语法(这是更好的)你将能够将部分内部的部分堆叠到多个级别向下传递变量,这些变量向堆栈中的每个部分提供所需的部分。它真正 强大 - 开始思考数据网格类型结构,其中行和单元格是从模型中提供所需变量的各个部分,所有这些都在单独的可管理视图文件中保持良好和干净。不要停留在那里,开始考虑基于变量或三个列布局来定位页眉和页脚内容,这三个列布局创建一个仪表板,可以在单独堆叠的部分上呈现多个深度的所有排序。
当您使用bog标准的ASP.NET MVC Helper方法Type
时,您将失去所有这些灵活性 - 小心这样做,很有可能像上面那样的解决方案。
如果有效,请告诉我。
一切顺利
Rob G
答案 1 :(得分:0)
我重构了代码和视图。最后,我真正想要实现的是有一个父视图(未显示)迭代IEnumerable,并且每次迭代渲染一个部分视图(ActiveTranslationRegion),它呈现一些Html来表示区域模型。
我还希望能够通过ajax调用调用一个action方法,通过传递一个单独的区域模型来呈现一个单独的ActiveTranslationRegion局部视图。我已经相应地重构了代码。
部分视图(_ActiveTranslationRegion.spark)
<viewdata model="Region" />
<form action="/Translation/DeactivateRegion" class="ui-widget-content active-translation-region-widget">
<input type="hidden" name="Id" value="${Model.Id}" />
<label class="region-name">${ Model.RegionName }</label>
<input class="deactivate-region-button" type="image" src=${Url.Content("~/Content/Images/Deactivate.png")} alt="Deactivate" />
</form>
通过使用我可以参考视图中的模型,如RobertTheGrey建议的那样(见上文)。
我删除了所有代码以将视图作为字符串返回,并简单地创建了一个返回partialViewResult的操作方法方法:
[UnitOfWork]
public PartialViewResult ActivateRegion(int id)
{
var region = _repos.Get(id);
if (region != null)
{
region.Active = true;
_repos.SaveOrUpdate(region);
}
return PartialView("_ActiveTranslationRegion", region);
}
我必须要做的一件事就是修改我的父视图,看起来像这样:
<div id="inactive-translation-regions-panel">
<h3 class="ui-widget-header">Inactive Regions</h3>
<for each="var region in Model">
<span if="region.Active==false">
# Html.RenderPartial("_InActiveTranslationRegion", region);
</span>
</for>
</div>
之前我有以下内容:
<div id="inactive-translation-regions-panel">
<for each="var region in Model">
<ActiveTranslationRegion Region="region" if="region.Active==true"></ActiveTranslationRegion>
</for>
</div>
注意我必须调用Html.RenderPartial而不是使用该元素。如果我尝试使用该元素(我更喜欢这样做),我会收到以下错误:
Only one viewdata model can be declared. IEnumerable<Region> != Region
有没有解决这个问题的方法?
<强>更新强>
我尝试了你的建议,但没有运气。为了回顾这个问题,我想在两种不同的情况下使用partial。在第一个实例中,我有一个使用IEnumerable<Region>
模型的父视图,部分只使用Region
作为模型。所以在父母中我迭代IEnumerable
并将Region
传递给部分。在第二个实例中,我想从动作方法中调用PartialView("_ActiveTranslationRegion", region)
。如果我从部分删除<viewdata model="Region" />
,我会收到一个抱怨模型的错误。解决问题的最佳方法是在bindings.xml文件中添加绑定:
<element name="partial"># Html.RenderPartial("@name", @model);</element>
(注意:将此条目保留在绑定文件中的所有内容似乎非常重要)
这样我仍然可以从上面描述的action方法中调用partial,并将Region作为模型传递给我,但我也可以在父视图中使用更像'html'的标签替换我对Html.RenderPartial的调用:
<partial name="_ActiveTranslationRegion" model="region" />
所以我的父视图现在看起来更像是这样:
<div id="inactive-translation-regions-panel">
<h3 class="ui-widget-header">Inactive Regions</h3>
<for each="var region in Model">
<span if="region.Active==false">
<partial name="_ActiveTranslationRegion" model="region" />
</span>
</for>
</div>
当然,在引擎盖下它还在打电话
# Html.RenderPartial("_ActiveTranslationRegion", region);
但它是我们能提出的最佳解决方案。
此致 西蒙