开发元件
创建一个元件
元件是一个可重用的逻辑单元,包含逻辑或者UI,他可以附加到内容类型上面。比如,Orchard原生提供了Route元件,使得我们可以将内容项关联到一个特定的URL,还提供了Tag元件,使得内容项具有标签功能。
在这篇文档中,我们会创建一个自定义地图的元件,通过配置经度和纬度,生成一副地图。
首先,我们先创建一个"Maps"模块(请先熟悉上一篇文章,学习如何创建模块):
我们在控制台中,输入下面的命令"codegen module Maps /IncludeInSolution:true" ,创建Map模块,"IncludeInSolution"开关,表示创建的模块,是否自动加入当前解决方案中。
orchard> codegen module Maps /IncludeInSolution:true
Creating module Maps
Module Maps created successfully
执行指令以后,Visual Studio会提示重新加载解决方案:
我们会看到,Maps模块已经加载到解决方案中了。
注意:使用命令,向解决方案中添加了内容后,最好重新编译解决方案,否则可能会出现未知问题,比如,如果没有重新编译,后面使用数据迁移命令后,得到的是一个空的数据迁移文件
编辑Maps模块根目录下面的Module.txt文件(这个文件,记录的该模块的说明信息,比如名称、作者、版本、依赖的特性等等):
Name: Maps
AntiForgery: enabled
Author: The Orchard Team
Website: http://orchardproject.net
Version: 1.0.0
OrchardVersion: 1.0.0
Description: Adds a map image to content items, based on longitude and latitude.
Features:
Maps:
Description: Adds a map image to content items, based on longitude and latitude.
Category: Geolocation
好了,让我们开始创建元件。首先,我们要创建一个类,来持久化元件的数据:
在Models文件夹中,我们创建MapRecord.cs和MapPart.cs两个文件,MapRecord是用于元件的数据持久化,MapPart使用MapRecord,实现了数据存储:
using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;
namespace Maps.Models
{
public class MapRecord : ContentPartRecord
{
public virtual double Latitude { get; set; }
public virtual double Longitude { get; set; }
}
public class MapPart : ContentPart<MapRecord>
{
[Required]
public double Latitude
{
get { return Retrieve(r => r.Latitude); }
set { Store(r => r.Latitude, value); }
}
[Required]
public double Longitude
{
get { return Retrieve(r => r.Longitude); }
set { Store(r => r.Longitude, value); }
}
}
}
接下来,我们创建Maps模块的数据迁移文件。这个文件用于MapRecord和数据库的映射(ORM中的概率,请自学)。同时,这个文件还包含了模块的版本更新内容和初始化内容。
我们通过命令"codegen datamigration Maps"来创建数据迁移文件:
orchard> codegen datamigration Maps
Creating Data Migration for Maps
Data migration created successfully in Module Maps
Visual Studio会提示你重新加载解决方案:
打开migrations文件,我们发现里面有一个Create方法,他创建了与MapRecord类相对应的表。
注意,Create会在特性启用的时候执行。
using System;
using System.Collections.Generic;
using System.Data;
using Maps.Models;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
namespace Maps.DataMigrations {
public class Migrations : DataMigrationImpl {
public int Create() {
// Creating table MapRecord
SchemaBuilder.CreateTable("MapRecord", table => table
.ContentPartRecord()
.Column("Latitude", DbType.Double)
.Column("Longitude", DbType.Double)
);
ContentDefinitionManager.AlterPartDefinition(
typeof(MapPart).Name, cfg => cfg.Attachable());
return 1;
}
}
}
我们在Create方法中,执行了AlterPartDefinition函数,他的作用,就是表明,该元件,可以添加到任意的内容类型中。
我们添加该元件的Handler。Handler是指的该元件的行为,他具有事件句柄,还可以在元件渲染前操纵数据模型。在我们的handler中,我们告诉了元件,使用IRepository<MapRecord>来操纵数据。我们将该文件放在Handlers\MapHandler.cs:
using Maps.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
namespace Maps.Handlers {
public class MapHandler : ContentHandler {
public MapHandler(IRepository<MapRecord> repository) {
Filters.Add(StorageFilter.For(repository));
}
}
}
我们为元件添加Driver。Driver定义了元件如何编辑、渲染、导入导出,他也是一种handler,我们可以将其看成简易的控制器。
内容项渲染的时候,会调用元件的Display方法,返回需要渲染的Shape,编辑的时候,会调用Edit方法,返回编辑Shape。Shape是具有数据和视图的,可以独立渲染的单元。我们将Driver文件放在Drivers\MapDriver.as :
using Maps.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
namespace Maps.Drivers {
public class MapDriver : ContentPartDriver<MapPart> {
protected override DriverResult Display(
MapPart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_Map", () => shapeHelper.Parts_Map(
Longitude: part.Longitude,
Latitude: part.Latitude));
}
//GET
protected override DriverResult Editor(
MapPart part, dynamic shapeHelper) {
return ContentShape("Parts_Map_Edit",
() => shapeHelper.EditorTemplate(
TemplateName: "Parts/Map",
Model: part,
Prefix: Prefix));
}
//POST
protected override DriverResult Editor(
MapPart part, IUpdateModel updater, dynamic shapeHelper) {
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
}
接下来我们添加视图。首先在Views文件夹下面,添加Parts和EditorTemplates/Parts 文件夹,在两个文件夹中,都添加Map.cshtml文件。
注意:Parts和EditorTemplates/Parts文件夹名称不能更改,是固定规范。Map.cshtml文件名称,需要和Driver中返回的Shape名称一致。
Views/EditorTemplates/Parts/Map.cshtml :
@model Maps.Models.MapPart
<fieldset>
<legend>Map Fields</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Latitude)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => model.Latitude)
@Html.ValidationMessageFor(model => model.Latitude)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Longitude)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => model.Longitude)
@Html.ValidationMessageFor(model => model.Longitude)
</div>
</fieldset>
Views/Parts/Map.cshtml :
<img alt="Location" border="1" src="http://maps.google.com/maps/api/staticmap?
&zoom=14
&size=256x256
&maptype=roadmap
&markers=color:blue|@Model.Latitude,@Model.Longitude
&sensor=false" />
在模块根目录,创建placement.info文件,里面记录了,元件在内容类型中的顺序和位置:
<Placement>
<Place Parts_Map="Content:10"/>
<Place Parts_Map_Edit="Content:7.5"/>
</Placement>
这个文件表明,Parts_Map Shape在内容类型中的顺序是10,Parts_Map_Edit在内容类型中的顺序是7.5
在后台管理页面,我们开启该特性
让我们在后台管理页面,创建一个Event内容类型,将地图元件附加到内容类型上面
单机Add按钮,选择要添加的内容类型
选择Map,点击保存
创建Event内容项,我们会看到,需要我们填写经度和纬度
在前端,我们就可以看到地图了