部件(Widgets) - TemplatedWidget 类

优质
小牛编辑
137浏览
2023-12-01

部件在其构造函数或其它方法生成复杂的 HTML 标签,因此可能导致类具有很多难以维护的意大利面条式的代码。此外,由于标签就定在程序代码中,可能难以自定义输出。

  1. public class MyComplexWidget : Widget
  2. {
  3. public MyComplexWidget(jQueryObject div)
  4. : base(div)
  5. {
  6. var toolbar = J("<div>")
  7. .Attribute("id", this.UniqueName + "_MyToolbar")
  8. .AppendTo(div);
  9. var table = J("<table>")
  10. .AddClass("myTable")
  11. .Attribute("id", this.UniqueName + "_MyTable")
  12. .AppendTo(div);
  13. var header = J("<thead/>").AppendTo(table);
  14. var body = J("<tbody/>").AppendTo(table);
  15. ...
  16. ...
  17. ...
  18. }
  19. }

通过使用 HTML 模板,可以避免这种问题。例如,我们可以向 HTML 页面添加下列模板:

  1. <script id="Template_MyComplexWidget" type="text/html">
  2. <div id="~_MyToolbar">
  3. </div>
  4. <table id="~_MyTable">
  5. <thead><tr><th>Name</th><th>Surname</th>...</tr></thead>
  6. <tbody>...</tbody>
  7. </table>
  8. </script>

在这里使用了 SCRIPT 标签,但通过指定其类型为 "text/html",浏览器不会把它当成真正的脚本来执行。

让我们通过使用 TemplatedWidget 重写之前的意大利面条式的代码块:

  1. public class MyComplexWidget : TemplatedWidget
  2. {
  3. public MyComplexWidget(jQueryObject div)
  4. : base(div)
  5. {
  6. }
  7. }

当在一个 HTML 元素上创建该小部件,其内容如下:

  1. <div id="SampleElement">
  2. </div>

你最终将得到这样的 HTML 标签:

  1. <div id="SampleElement">
  2. <div id="MySamples_MyComplexWidget1_MyToolbar">
  3. </div>
  4. <table id="MySamples_MyComplexWidget1_MyTable">
  5. <thead><tr><th>Name</th><th>Surname</th>...</tr></thead>
  6. <tbody>...</tbody>
  7. </table>
  8. </div>

TemplatedWidget 自动查找类的模板,并将它应用于 HTML 元素。

生成 TemplatedWidget 的 ID

如果你仔细观察,在模板中我们为子元素指定 ID : ~_MyToolbar~_MyTable

但是当该模板应用到 HTML 元素时,结果标签使用的 ID 却是 MySamples_MyComplexWidget1_MyToolbarMySamples_MyComplexWidget1_MyTable

TemplatedWidget 使用部件的 唯一名称 和下划线 (““) (this.idPrefix 包含合并的前缀) 替换 `~` 前缀。

使用该策略,即使同一部件模板在页面中用于多个 HTML 元素,其 ID 也不会互相冲突,因为它们都有唯一的 ID。

TemplatedWidget.ByID 方法

TemplateWidget 向生成的标签追加一个唯一的名称,部件模板中的 ID 属性不能用于访问创建部件后的元素。

部件的唯一名称和下划线应该放在查找元素的模板中的原始 ID 属性前面:

  1. public class MyComplexWidget : TemplatedWidget
  2. {
  3. public MyComplexWidget(jQueryObject div)
  4. : base(div)
  5. {
  6. J(this.uniqueName + "_" + "Toolbar").AddClass("some-class");
  7. }
  8. }

也可以使用 TemplatedWidget 的 ByID 方法替代:

  1. public class MyComplexWidget
  2. {
  3. public MyComplexWidget(jQueryObject div)
  4. : base(div)
  5. {
  6. ByID("Toolbar").AddClass("some-class");
  7. }
  8. }

TemplatedWidget.GetTemplateName 方法

在最近的示例中,MyComplexWidget 自动查找其模板。

TemplatedWidget 根据约定找到其模板(根据编码约定)。它在类名称之前插入 Template_ 前缀,搜索含此 ID 属性 (Template_MyComplexWidget) 的 SCRIPT 元素,并使用其 HTML 内容作为模板。

如果我们想使用另一个像下面的 ID :

  1. <script id="TheMyComplexWidgetTemplate" type="text/html">
  2. ...
  3. </script>

在浏览器控制台中将看到下面的错误:

  1. Can't locate template for widget 'MyComplexWidget' with name 'Template_MyComplexWidget'!

我们可以修改模板 ID 或要求部件使用自定义 ID :

  1. public class MyComplexWidget
  2. {
  3. protected override string GetTemplateName()
  4. {
  5. return "TheMyComplexWidgetTemplate";
  6. }
  7. }

TemplatedWidget.GetTemplate 方法

GetTemplate 方法可以被重写,以从另一个资源提供或手动指定模板:

  1. public class MyCompleWidget
  2. {
  3. protected override string GetTemplate()
  4. {
  5. return $('#TheMyComplexWidgetTemplate').GetHtml();
  6. }
  7. }

Q.GetTemplate 方法和服务端模板

默认调用 GetTemplateName 方法并搜索具有该 ID 的 SCRIPT 元素来实现 TemplatedWidget.GetTemplate 方法。

如果没有找到这样的 SCRIPT 元素,可使用同一 ID 调用 Q.GetTemplate 方法获取模板。

如果也不返回结果,则会抛出错误。

Q.GetTemplate 可访问定义在服务端的模板。这些模板从 ~/Views/Template~/Modules 或它们的子文件夹中含 .template.cshtml 扩展的文件编译而来。

例如,我们在服务端为 MyComplexWidget 创建一个模板文件 ~/Views/Template/SomeFolder/MyComplexWidget.template.cshtml,其内容如下:

  1. <div id="~_MyToolbar">
  2. </div>
  3. <table id="~_MyTable">
  4. <thead><tr><th>Name</th><th>Surname</th>...</tr></thead>
  5. <tbody>...</tbody>
  6. </table>

模板文件名和扩展名是很重要的,而其文件夹会被完全忽略。

使用该策略就不需要将部件模板插入到页面标签中。

此外,由于这种服务端的模板在首次使用时加载(延迟加载),并在浏览器和服务器中缓存,页面标签没有被可能永远不会在特定网页使用的部件模板污染。因此,服务端模板青睐内嵌 SCRIPT 模板。