编写帮助程序以包装HTML元素

时间:2012-05-24 23:29:37

标签: html seaside

我目前正在编写自己的Component基类,其中包含一大堆辅助方法,用于使用Twitter Bootstrap CSS框架(并避免使用它周围的所有样板代码)。它们可以像现有的表单助手一样使用(例如html div ...)。

例如,水平文本字段(水平格式)的内容如下所示:

horizontalTextField: aLabel
| field |

field := WATextInputTag new
    class: 'input-xlarge';
    yourself.

self html div
    class: 'control-group';
    with: [
        self html label
            class: 'control-label';
            with: aLabel.
        self html div
            class: 'controls';
            with: [self html brush: field].
    ].

^ field.

在渲染组件时,我的目标是这样使用:

(self horizontalTextField: 'Titel')
    on: #title of: self article;
    id: 'title'.

因此,目标是将实际文本字段包装在几个div中,但仍然能够在辅助函数之外对这个包装元素进行更改(使用普通标记帮助程序访问器),如上所示。

然而,这个不起作用,因为with:方法导致包装div在我返回元素之前被序列化(也称为渲染),然后我再也无法编辑了。

可能的解决方案

  1. 实际上扩展用于渲染字段的WACanvasTag子类,并在我的组件类中的自定义助手中创建这些新子类的实例。我只是为了我的乐趣而覆盖他们的渲染代码。这可能是最面向对象的做事方式,但是非常艰巨,特别是因为我必须重复相当多的代码来为这些子类中的每一个子元素之前和之后插入我自己的HTML(如他们已经必须从我试图包装的Tag类继承。
  2. 调用助手时,请执行self horizontalTextField: 'Titel' with: [:field | id: 'title']之类的操作。然后在渲染辅助方法中的实际文本字段之前应用该块。这将非常灵活,但不是很漂亮(语法方面)。
  3. 在我的帮助函数中对包装HTML(与this question相关)进行硬编码。像这样:self html html: '<label class="control-label">等。在某种程度上相当黑客,而不是非常面向对象。
  4. 评论?想法?更好的建议?

2 个答案:

答案 0 :(得分:2)

正如您所注意到的,上面的代码不起作用,因为HTML Canvas立即发出HTML。刷子不应存放在任何地方,它们只能在很短的时间内存放。 HTML画布也是如此,它非常罕见,并且可能导致错误将其存储在某处。

在Seaside中执行此操作的典型方法是创建辅助方法:

renderHorizontalLabel: aLabelRenderer andField: aFieldRenderer on: html
   html div
       class: 'control-group';
       with: [
           html label
               class: 'control-label';
               with: aLabelRenderable.
           html div
               class: 'controls';
               with: aFieldRenderer ]

上述代码的优点是aLabelRendereraFieldRenderer都可以是实现#renderOn:的任何对象(String,Number,Block,WAPresenter,WAComponent,...)。

renderContentOn: html
   self
       renderHorizontalLabel: 'Comment'
       andField: [
           html textField
               value: comment;
               callback: [ :value | comment := value ] ]
       on: html.
   self
       renderHorizontalLabel: 'Acknowledge'
       andField: [ self renderCheckbox: false on: html ]
       on: html.
   ...

要生成实际字段,您可以创建其他方法,然后从您传入的块中调用aFieldRenderer。这使您可以灵活地任意组合不同的部件。看看Seaside中的例子,有很多这种模式的用户。

答案 1 :(得分:0)

看起来你想像Tag Brush一样使用它,所以我想你肯定想要使用扩展的WACanvasTag方法 - 但只有当你真的想要在horizo​​ntalTextField情况下创建一个复合“tag”时

对于其他Bootstrap概念 - 如行,容器等,最好的办法是简单地向WAHtmlCanvas添加扩展方法。

我自己也做过 - 这是我对Bootstrap NavBar标签的实现:

WABrush subclass: #BSNavBar
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'Bootstrap'

BSNavBar>>with: aBlock
    super with: [
        canvas div class: 'navbar'; with: [
            canvas div class: 'navbar-inner'; with: [
                canvas container: aBlock]]].
    closed := true

以下是我实施“行”的方法 -

WAHtmlCanvas>>row
    ^self
        div class: 'row'

WAHtmlCanvas>>row: aBlock
    self row with: aBlock

此外,我还为WATagBrush添加了扩展方法,以支持span,offset和pull-right以及流体容器:

WATagBrush>>offset: anInteger
    self class: 'offset', anInteger asString

WATagBrush>>beFluid
    self attributeAt: #class 
        put: (((self attributeAt: #class ifAbsent: [^self]) 
            copyReplaceTokens: 'container' with: 'container-fluid')
            copyReplaceTokens: 'row' with: 'row-fluid')

最后,这是一个使用上述内容的示例渲染方法:

renderContentOn: html
    html navBar: [
        html div pullRight; with: [ 
            self session isLoggedIn 
                ifTrue: [self renderUserMenuOn: html] 
                ifFalse: [self renderLoginBoxOn: html]]]