将spark视图渲染为字符串

时间:2010-11-29 17:52:17

标签: asp.net asp.net-mvc partial-views spark-view-engine

我正在尝试将部分视图呈现为字符串,因此可以将其作为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>

它呈现完美。我做错了什么?

2 个答案:

答案 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);

但它是我们能提出的最佳解决方案。

此致 西蒙