将 Spark 视图渲染为字符串
我正在尝试将部分视图呈现为字符串,以便它可以作为 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>
它似乎无法识别该模型。
PS 当我像这样从父视图调用视图时,
<for each="var region in Model">
<ActiveTranslationRegion Region="region" if="region.Active==true"></ActiveTranslationRegion>
</for>
它会完美呈现。我做错了什么?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
仅从外观上看,我认为以下行是问题所在:
相反,它应该显示为:
注意小写的“模型”。这是因为
model
是一种特殊情况,因为它在幕后执行到强类型视图模型的转换。最上面的一个将在生成的视图类中定义一个名为Model
的变量,并将值Region
分配给它。使用下面的小写选项实际上会创建一个Model
变量,但也会将其转换为来自ViewData
字典的Region
强类型实例。注意 当在代码中使用
Model
时,就像您在foreach
循环中所做的那样,它需要是大写,这在您的代码中是正确的案件。再次强调,这是唯一的特殊情况,因为它从ViewData
字典中提取强类型模型。另一件事 -
必须在父视图中声明,并且每个页面只能定义一次,因此您不能在分部视图中重新定义它。如果它是部分视图,您应该通过传递模型的一部分来使用它,就像上面第二个示例中所做的那样。上述异常的原因是因为它试图将
Id
属性作为Region
Type 的静态项获取,而不是查询 <作为视图模型一部分的Region
实例 上的 code>Id 属性。附带说明一下,到达您想要的位置的代码有点混乱。您可以通过查看一些 直接使用示例,但我知道这可能只是看到它工作的一个峰值...:)
更新以响应您的跟进问题/答案
我相当确定问题出在传递
Region 上
进入以下调用:... 再次归结为命名。是的,正如我之前所说,每个视图只能有一个模型,因此您需要做的是从部分顶部删除以下内容:
这就是导致问题的原因。然后,我将像这样重命名进入您的部分的项目:
然后您的部分将如下所示:
注意我使用
ActiveRegion
因为在Spark解析器中,ActiveRegion
被声明为变量,并在循环for 循环
时分配当前作用域中region
的值。无需严格遵守模型
- 因为您已经将模型
的一部分传递给了您已声明为ActiveRegion
。哦,如果您确实愿意,您可以坚持使用名称Region
,我只是更改它来表明观点,因为您有一个名为的
在你的代码中,我不太喜欢使用与类型相同的变量名称可能带来的奇怪问题。另外,它使它更清晰一些。Type
Region调用 Html.RenderPartial 方法的缺点并不是立即显而易见的。您失去的一件事是 Spark 提供的 3 遍渲染。如果您使用标签语法(这是更好的),您将能够将部分中的部分堆叠到多个级别,向下传递变量,为每个部分提供它们需要的堆栈。它变得真正强大 - 开始思考数据网格类型结构,其中行和单元格是单独的部分,它们从模型中获取所需的变量,所有这些都在单独的可管理视图文件中保持良好和干净。但不要就此止步,开始考虑基于变量或三列布局来定位页眉和页脚内容,这些布局创建一个仪表板,在多个深度的单独堆叠的部分上呈现各种类型。
当您使用沼泽标准 ASP.NET MVC Helper 方法
Html.RenderPartial()
时,您会失去所有灵活性 - 小心这样做,很可能有像上面这样的解决方案。让我知道这是否有效...
一切顺利
罗布·G
Just from looking at it, I think the following line is the problem:
Instead it should read:
Note the lower case "model". This is because
model
is a special case since behind the scenes it performs a cast to a strongly typed viewmodel. The top one will define a variable calledModel
in the generated view class and assign the valueRegion
to it. Using the lowercase option below will actually create aModel
variable, but also cast it to strongly typed instance ofRegion
that comes from theViewData
dictionary.Note When using
Model
in the code though, like you did in thefor each
loop, it needs to be upper case which is correct in your case. Once again, this is the only special case because it's pulling a strongly typed model from theViewData
dictionary.One other thing -
<viewata model="Region" />
must be declared in the parent view, and it can only be defined once per page, so you cannot redefine it in a partial view. If it's a partial view, you should rather use it by passing in a part of the model like you have done in your second example above.The reason for your exception above is because it's trying to get the
Id
property as a static item off theRegion
Type, rather than querying theId
property on your instance ofRegion
as part of your viewmodel.As a side note, the code to get where you want is a little mangled. You can find neater ways of doing what you want by checking out some of the Direct Usage Samples, but I understand this was probably just a spike to see it working... :)
Update in response to your follow up question/answer
I'm fairly sure that the problem is with passing the
Region
into the following call:... is again down to naming. Yes, you can only have one
model
per view as I said before, so what you need to do is remove the following from the top of your partial:That's what's causing an issue. I would then rename the item going into your partial like so:
and then your partial would look like this:
Note I'm using
ActiveRegion
because in the Spark parser,ActiveRegion
gets declared as a variable and assigned the value ofregion
in the current scope as you loop through thefor loop
. No need to stick religiously to themodel
- because you've gone and passed in a piece of themodel
now that you've declared asActiveRegion
. Oh, and you could stick with the nameRegion
if you really want, I just changed it to make a point, and because you've got aType
calledRegion
in your code and I'm not a big fan of the quirky issues using the same name for a variable as a type can bring about. Plus it makes it a little clearer.The disadvantage of calling the Html.RenderPartial method is not immediately obvious. One thing you lose is the 3-pass rendering that Spark provides. If you use the tag syntax (which is preferable) you'll be able to stack partials within partials to multiple levels down passing variables that feed each partial what they need down the stack. It gets really powerful - start thinking data grid type structures where rows and cells are individual partials that are fed the variables they need from the model, all kept nice and clean in separate manageable view files. Don't stop there though, start thinking about targeting header and footer content base on variables or three column layouts that create a dashboard that renders all sorts on individually stacked partials many levels deep.
You lose all of that flexibility when you use the bog standard ASP.NET MVC Helper method
Html.RenderPartial()
- careful of doing that, there's more than likely a solution like the one above.Let me know if that works...
All the best
Rob G
我对代码和视图进行了相当多的重构。最后,我真正想要实现的是有一个父视图(未显示)迭代 IEnumerable 并为每次迭代渲染一个部分视图(ActiveTranslationRegion),它渲染一些 Html 来表示区域模型。
我还希望能够通过 ajax 调用来调用操作方法,以通过向其传递单独的区域模型来呈现单独的 ActiveTranslationRegion 部分视图。我相应地重构了代码。
部分视图 (_ActiveTranslationRegion.spark)
请注意,通过使用我可以按照 RobertTheGrey 建议的方式引用视图中的模型(见上文)。
我删除了所有代码以将视图作为字符串返回,并简单地创建了一个返回partialViewResult的操作方法方法:
我必须做的一件事是修改我的父视图,使其看起来像这样:
以前我有以下内容:
注意我有调用 Html.RenderPartial 而不是使用该元素。如果我尝试使用该元素(我更愿意这样做),我会收到以下错误:
有解决该问题的方法吗?
更新:
我尝试了你的建议,但没有成功。回顾一下这个问题,我想在两种不同的情况下使用部分。在第一个实例中,我有一个使用
IEnumerable
模型的父视图,部分仅使用Region
作为其模型。因此,在父级中,我迭代IEnumerable
并将Region
传递给部分。在第二个实例中,我想从操作方法调用PartialView("_ActiveTranslationRegion", region)
。如果我从部分中删除
,我会收到有关模型的错误消息。解决我发现的问题的最佳方法是将绑定添加到 bindings.xml 文件:(注意:将绑定文件中的此条目全部保留在同一行似乎非常重要)
我仍然可以如上所述从操作方法中调用部分,并将其传递为区域作为模型,但我也可以用更像“html”的标签替换父视图中对 Html.RenderPartial 的调用:
所以我的父级现在的视图看起来更像是这样的:
当然,在幕后它仍在调用
但它是我们能想到的最佳解决方案。
问候,
西蒙
I refactored the code and views quite a bit. In the end all I'm really trying to acheive is have a parent view (not shown) iterate over an IEnumerable and for each iteration render a partial view (ActiveTranslationRegion) which renders some Html to represent a region model.
I also want to be a able to call an action method via an ajax call to render an indivual ActiveTranslationRegion partial view by passing it an individual region model. I've refactored the code accordingly.
Partial view (_ActiveTranslationRegion.spark)
Notice by using I can refer to Model within the view as RobertTheGrey suggested (see above) .
I removed all the code to return the view as a string and simply created an action method method that returned a partialViewResult:
One thing I had to do was amend my parent view to look like so:
Where previously I had the following:
Notice I have to call the Html.RenderPartial rather than use the element. If I try and use the element (which I would prefer to do) I get the following error:
Is there a way round that problem?
Update:
I tried your recommendation but with no luck. To recap the problem, I want to use the partial in 2 different situations. In the first instance I have a parent view that uses a model of
IEnumerable<Region>
, the partial simply usesRegion
as its model. So in the parent I iterate over theIEnumerable
and passRegion
to the partial. In the second instance I want to callPartialView("_ActiveTranslationRegion", region)
from an action method. If I remove the<viewdata model="Region" />
from the partial I get an error complaining about the model. The best way round the problem I have found is to add a binding to the bindings.xml file:(Note: It seems very important to keep this entry in the bindings file all on te same line)
This way I can still call the partial from the action method as described above and pass it a Region as the model, but I can also replace my call to Html.RenderPartial in the parent view with a more 'html' like tag:
So my parent view now looks more like this:
Of course under the hood its still making a call to
But its the best solution we could come up with.
Regards,
Simon