候补(Alternates)是形状(Shape)可选变形,你可以在主题中对一些形状的特殊情况定制特殊的呈现模板。通过使用候补,你可以根据呈现内容、呈现类型或其他特征来进行重写。例如:你可以为形状在首页的候补创建一个模板文件,而其他的页面中还是使用默认模板文件。或者你可以使用候补让博客文章的标签呈现不同与其他的地方的标签呈现。候补非常有用,当你有不同类型的内容时,你可以自定义形状在不同类型内容中的外观。Orchard框架使用一个命名规则来确认在呈现某一内容时使用那一个候补。通过命名规则来添加不同的候补的模板时,你无需更改任何代码和文件。
候补的命名规则(Naming Convention for Alternates)
候补形状的命名使用双下划线(__)加形状的特殊名称来命名的。例如:有一个Parts_Tags_ShowTags形状,它的候补名称就可以有Parts_Tags_ShowTags__BlogPost和Parts_Tags_ShowTags__Page。双下划线后面的部分反映了形状用于的类型。(更多关于形状的内容可以查看《Orchard是如何呈现内容的》)
映射一个模板文件到一个候补(Mapping a Template File to an Alternate)
要创建一个模板文件映射到相应的形状,你必须按照以下的约定来命名模板:
- 在模板名称中,将下划线(_)转换为点(.)或反斜杠(\)。一个反斜杠表示模板存放在一个子文件夹。
- 将形状名称中的将双下划线(__)转换为短横线(-)。
- 对于形状名称中的不同显示类型,把显示类型名称前加点(.)放在模板名称后面。如:Content-BlogPost.Summary。
所有候补的模板需要放在Views目录下。Views目录可以在主题或者模块中。下表显示了Views目录下不同子目录存放的不同类型的模板文件。
Shape type | Template Folder |
---|---|
Content item | Views\Items |
Parts | Views\Parts |
Fields | Views\Fields |
EditorTemplate | Views\EditorTemplates\[template type folder] (For example, an EditorTemplate for a part is located at Views\EditorTemplates\Parts.) |
All other | Views |
例如:创建一个Tags部件候补的模板,你可以添加一个模板文件到MyTheme\Views\Parts目录下。然而,因为下划线可以被转换成一个点(.)或反斜杠(\),你同样也可以创建一个模板文件加上Parts.前缀直接放在Views目录下。一个模板无论是Views\Parts\Tags.ShowTags-BlogPost.cshtml还是Views\Parts.Tags.ShowTags-BlogPost.cshtml都可以映射到一个名为Parts_Tags_ShowTags__BlogPost的形状。
如果Orchard框架不能加载候补的模板,那么它将根据形状名称加载默认的形状模板。例如:你没有创建Tags部件候补的模板,它将默认使用Views\Parts\Tags.ShowTags.cshtml模板。
Orchard框架会自动创建很多候补,可以在你的应用程序中使用。然而,你也可以创建这些形状候补的模板。Orchard会根据以下模式来创建形状候补。
内容形状:
- Content__[DisplayType]. (Example template: Content.Summary)
- Content__[ContentType]. (Example template: Content-BlogPost)
- Content__[Id]. (Example template: Content-42)
- Content_[DisplayType]__[ContentType]. (Example template: Content-BlogPost.Summary)
- Content_[DisplayType]__[Id]. (Example template: Content-42.Summary)
区域形状:
- Zone__[ZoneName]. (Example template: Zone-SideBar)
菜单或菜单项形状:
- Menu__[MenuName]. (Example template: Menu-main)
- MenuItem__[MenuName]. (Example template: MenuItem-main)
本地化菜单和菜单项形状:
- LocalMenu__[MenuName]. (Example template: LocalMenu-main)
- LocalMenuItem__[MenuName]. (Example template: LocalMenuItem-main)
样式和资源:
- Style__[FileName]
- Resource__[FileName]
小部件(widget)形状:
- Widget__[ZoneName]. (Example template: Widget-SideBar)
- Widget__[ContentType]. (Example template: Widget-BlogArchive)
字段:
- [ShapeType__FieldName]. (Example template: Fields\Common.Text-Teaser)
- [ShapeType__PartName]. (Example template: Fields\Common.Text-TeaserPart)
- [ShapeType]__[ContentType]__[PartName]. (Example template: Fields\Common.Text-Blog-TeaserPart)
- [ShapeType]__[PartName]__[FieldName]. (Example template: Fields\Common.Text-TeaserPart-Teaser)
- [ShapeType]__[ContentType]__[FieldName]. (Example template: Fields\Common.Text-Blog-Teaser)
- [ShapeType]__[ContentType]__[PartName]__[FieldName]. (Example template: Fields\Common.Text-Blog-TeaserPart-Teaser)
部件:
- [ShapeType]__[Id]. (Example template: Parts\Common.Metadata-42)
- [ShapeType]__[ContentType]. (Example template: Parts\Common.Metadata-BlogPost)
你可以使用Shape Tracing模块来创建这些候补的模板。关于Shape Tracing工具,可查看《Customizing Orchard using Designer Helper Tools》。
Url候补(URL Alternates)
通过上面介绍的规则可知形状可以通过不同类型、区域,内容等来创建相应的候补。在Orchard中还提供了URL Alternates模块,来进一步的扩展该功能。它可以让你根据不同的Url地址来创建形状候补,这样你就可以为不同Url中的形状来创建相应的模板了。要使用这个功能你需要首先在管理后台中启用URL Alternates,启用这个功能后,例如:MenuItem在的Url为/my-blog/post-1中就可以创建以下不同的候补了:
MenuItem-main
MenuItem-main-url-my-blog
MenuItem-main-url-my-blog-post-1
在homepage中可以有:
MenuItem-main-url-homepage
使用这个模块,还可以为不同Url提供不同的Layout形状候补,如:
Layout-url-homepage
这样如果你需要为About页面创建不同的布局文件,你就可以在主题中添加一个Layout-url-About.cshtml文件就可以了。
注意:如果都只是小的变化,或许使用区域的Url候补会更合适。
下载和安装Shape Tracing模块可访问Orchard Designer Tools。
明确的来指定一个候补(Explicitly Designating an Alternate Template)
除了自动创建的候补以外,我们还可以手动的指定一个形状的候补。在placement.info文件中,你可以为某一内容类型添加一个指定的候补。如:使用一个特殊的模板来显示博客文章中的标签,你可以在Orchard.Tags 模块中修改placement.info文件,当匹配BlogPost类型时使用指定的候补,代码如下:
<Placement> <Place Parts_Tags_Edit="Content:7"/> <Match ContentType="BlogPost"> <Place Parts_Tags_ShowTags="Header:after.7;Alternate=Parts_Tags_ShowTags_BlogPost"/> </Match> <Match DisplayType="Detail"> <Place Parts_Tags_ShowTags="Header:after.7"/> </Match> <Match DisplayType="Summary"> <Place Parts_Tags_ShowTags="Header:after.7"/> </Match> </Placement>
Match标签的顺序很重要。只有第一个成功匹配的Match标签中的内容才会被使用到。在这个例子中,如果将匹配BlogPost的标签放在,匹配Detail和Summary的标签后面,那么Parts_Tags_ShowTags_BlogPost候补将不会起作用。更多关于placement.info文件知识请查看《理解Orchard中的placement.info文件》。
通过代码来添加候补(Adding Alternates Through Code)
另外我还可以通过代码来添加一个候补。你可以创建一个类实现IShapeTableProvider接口。然后,你为每一个你需要创建候补的形状添加一段处理 OnDisplaying 的代码。在这段代码里,你可以添加自己需要创建候补的逻辑了。下面的例子实现了对Content形状添加新的候补,当在HomePage时使用候补为Content__HomePage,同时也实现了对Parts_Tags_ShowTags形状添加新的候补,当为摘要显示的时候使用Tags_ShowTags_Summary候补。
ExampleShapeProvider
using Orchard;
using Orchard.ContentManagement;
using Orchard.DisplayManagement.Descriptors;
namespace MyTheme.ShapeProviders
{
public class ExampleShapeProvider : IShapeTableProvider
{
private readonly IWorkContextAccessor _workContextAccessor;
public ExampleShapeProvider(IWorkContextAccessor workContextAccessor)
{
_workContextAccessor = workContextAccessor;
}
public void Discover(ShapeTableBuilder builder)
{
builder.Describe("Content")
.OnDisplaying(displaying =>
{
if (displaying.ShapeMetadata.DisplayType == "Detail")
{
ContentItem contentItem = displaying.Shape.ContentItem;
if (_workContextAccessor.GetContext().CurrentSite.HomePage.EndsWith(';' + contentItem.Id.ToString()))
{
displaying.ShapeMetadata.Alternates.Add("Content__HomePage");
}
}
});
builder.Describe("Parts_Tags_ShowTags")
.OnDisplaying(displaying =>
{
if (displaying.ShapeMetadata.DisplayType == "Summary")
{
displaying.ShapeMetadata.Alternates.Add("Tags_ShowTags_Summary");
}
});
}
}
using Orchard.ContentManagement;
using Orchard.DisplayManagement.Descriptors;
namespace MyTheme.ShapeProviders
{
public class ExampleShapeProvider : IShapeTableProvider
{
private readonly IWorkContextAccessor _workContextAccessor;
public ExampleShapeProvider(IWorkContextAccessor workContextAccessor)
{
_workContextAccessor = workContextAccessor;
}
public void Discover(ShapeTableBuilder builder)
{
builder.Describe("Content")
.OnDisplaying(displaying =>
{
if (displaying.ShapeMetadata.DisplayType == "Detail")
{
ContentItem contentItem = displaying.Shape.ContentItem;
if (_workContextAccessor.GetContext().CurrentSite.HomePage.EndsWith(';' + contentItem.Id.ToString()))
{
displaying.ShapeMetadata.Alternates.Add("Content__HomePage");
}
}
});
builder.Describe("Parts_Tags_ShowTags")
.OnDisplaying(displaying =>
{
if (displaying.ShapeMetadata.DisplayType == "Summary")
{
displaying.ShapeMetadata.Alternates.Add("Tags_ShowTags_Summary");
}
});
}
}
看了上面的例子你可能会说,Orchard不是默认已经提供了这些候补吗?是的,这只不过是一个例子说明了代码实现添加候补的方法,你当然可以定制自己特有的逻辑来实现自己所需的候补。
参考文档
==========================================
作者:二十四画生
转载请注明来源于博客园——二十四画生的Blog,并保留有原文链接。