渲染Halogen HTML
Halogen HTML
元素是Halogen
应用程序的最小构建块。这些元素描述了您希望在屏幕上看到的内容。 Halogen HTML
元素不是组件(我们将在下一章中介绍组件),如果没有组件,元素就无法呈现。但是,编写生成Halogen HTML
的辅助函数然后在组件中使用这些函数是很常见的。
在本章中,我们将探索在没有组件或事件的情况下编写HTML
。
Halogen HTML
您可以使用Halogen.HTML
或Halogen.HTML.Keyed
模块中的函数编写Halogen HTML
,如下例所示:
import Halogen.HTML as HH
element = HH.h1 [ ] [ HH.text "Hello, world" ]
Halogen HTML
元素可以被认为是浏览器DOM
元素, 但它们由Halogen
库控制,而不是DOM
中的实际元素。在幕后,Halogen
负责更新实际DOM
以匹配您编写的代码。
Halogen
中的元素接受两个参数:
- 应用于元素的
attributes
、properties
、event handlers
和/或references
的数组.这些对应于普通的HTML
属性(如placeholder
)和事件处理程序(如onClick
)。我们将在下一章学习如何处理事件,本章只关注属性。 - 子元素数组,如果该元素支持子元素。
举个简单的例子,让我们把这个普通的HTML
翻译成Halogen HTML
:
1
2
3
4
5
6
<div id="root">
<input placeholder="Name" />
<button class="btn-primary" type="submit">
Submit
</button>
</div>
让我们分解我们的Halogen HTML
:
- 我们的
Halogen
代码具有与普通HTML
相同的形状:一个div
包含一个input
和一个button
,button
本身包含纯文本。 - 属性从标签内的键值对移动到元素的属性数组中.
- 如果元素支持子元素,则子元素从标签内移动到子元素数组。
import Halogen.HTML as HH
import Halogen.HTML.Properties as HP
html =
HH.div
[ HP.id "root" ]
[ HH.input
[ HP.placeholder "Name" ]
, HH.button
[ HP.classes [ HH.ClassName "btn-primary" ]
, HP.type_ HP.ButtonSubmit
]
[ HH.text "Submit" ]
]
您可以在此处看到Halogen
对类型安全的重视:
text input
不能有子元素,因此Halogen
不允许该元素将更多元素作为参数。button
的type
属性只能使用某些值,因此Halogen
使用sum
类型来限制它们。CSS classes
使用ClassName newtype
,以便在需要时可以对其进行特殊处理;例如,classes
函数确保您的classes
在组合时以空格分隔。
一些HTML
元素和属性与PureScript
中的保留关键字或Prelude
中的常见函数发生冲突,因此Halogen
为它们添加了下划线。这就是为什么您在上面的示例中看到type_
而不是type
的原因。
当您不需要在Halogen HTML
元素上设置任何属性时,您可以改用其带下划线的版本。例如,下面的div
和button
元素没有属性:
html = HH.div [ ] [ HH.button [ ] [ HH.text "Click me!"] ]
这意味着我们可以使用它们的下划线版本重写它们。这有助于保持HTML
整洁:
html = HH.div_ [ HH.button_ [ HH.text "Click me!" ] ]
在 Halogen HTML 中编写函数
为Halogen HTML
编写辅助函数是很常见的。由于Halogen HTML
是由普通的PureScript
函数构建的,因此您可以在代码中自由穿插其他函数。
在这个例子中,我们的函数接受一个整数并将其呈现为文本:
header :: forall w i. Int -> HH.HTML w i
header visits =
HH.h1_
[ HH.text $ "You've had " <> show visits <> " visitors" ]
我们还可以渲染事物列表:
lakes = [ "Lake Norman", "Lake Wylie" ]
html :: forall w i. HH.HTML w i
html = HH.div_ (map HH.text lakes)
-- same as: HH.div_ [ HH.text "Lake Norman", HH.text "Lake Wylie" ]
这些函数引入了一种新类型HH.HTML
,这是您以前从未见过的。别担心!这是Halogen HTML
的类型,我们将在下一节中了解它。现在,让我们继续学习在HTML
中使用函数。
一个常见的要求是有条件地呈现一些HTML
. 你可以用普通的if
和case
语句来做到这一点,但为常见模式编写辅助函数很有用. 让我们来看看您可能在自己的应用程序中编写的两个辅助函数,这将帮助我们获得更多使用Halogen HTML
编写函数的练习。
首先,您有时可能需要处理可能存在或不存在的元素。像下面这样的函数可以让你渲染一个存在的值,否则渲染一个空节点。
maybeElem :: forall w i a. Maybe a -> (a -> HH.HTML w i) -> HH.HTML w i
maybeElem val f =
case val of
Just x -> f x
_ -> HH.text ""
-- 如果name有的话, 则渲染它
renderName :: forall w i. Maybe String -> HH.HTML w i
renderName mbName = maybeElem mbName \name -> HH.text name
其次,您可能只想在条件为真时呈现一些HTML
,如果条件不符合,则不计算HTML
。您可以通过将其评估隐藏在函数后面来实现此目的,这样HTML
仅在条件为真时才计算。
whenElem :: forall w i. Boolean -> (Unit -> HH.HTML w i) -> HH.HTML w i
whenElem cond f = if cond then f unit else HH.text ""
-- 渲染旧的number,但前提是它与新的number不同
renderOld :: forall w i. { old :: Number, new :: Number } -> HH.HTML w i
renderOld { old, new } =
whenElem (old /= new) \_ ->
HH.div_ [ HH.text $ show old ]
现在我们已经探索了几种使用HTML
的方法,让我们了解更多关于描述它的类型。
HTML 类型
到目前为止,我们已经编写了没有类型签名的HTML
。但是当您在应用程序中编写Halogen HTML
时,您将包含类型签名。
HTML w i
HTML
是Halogen
中HTML
的核心类型,它用于不绑定到特定类型组件的HTML
元素. 例如,它被用作我们目前看到的h1
、text
和button
元素的类型。您也可以在定义自己的自定义HTML
元素时使用此类型。
HTML
类型有两个类型参数:w
,代表widget
,描述可以在HTML
中使用哪些组件;i
,代表input
,代表用于处理 DOM
事件的类型。
当您为Halogen HTML
编写不需要响应DOM
事件的辅助函数时, 那么您通常会使用HTML
类型而不指定w
和i
是什么。例如,这个辅助函数可以让你创建一个按钮,给定一个标签:
primaryButton :: forall w i. String -> HH.HTML w i
primaryButton label =
HH.button
[ HP.classes [ HH.ClassName "primary" ] ]
[ HH.text label ]
你也可以接受HTML
作为标签,而不是只接受一个字符串:
primaryButton :: forall w i. HH.HTML w i -> HH.HTML w i
primaryButton label =
HH.button
[ HP.classes [ HH.ClassName "primary" ] ]
[ label ]
当然,作为一个按钮,您可能希望在单击它时执行某些操作。别担心——我们将在下一章介绍处理DOM
事件!
ComponentHTML 和 PlainHTML
您通常会在Halogen
应用程序中看到另外两种HTML
类型.
当您编写旨在处理特定类型组件的HTML
时,将使用ComponentHTML
。它也可以在组件外部使用,但最常用于组件内部。我们将在下一章中了解有关此类型的更多信息。
PlainHTML
是限制性更强的HTML
版本,用于不包含组件且不响应DOM
中的事件的HTML
.该类型允许您隐藏HTML
的两个类型参数,这在您将HTML
作为值传递时非常方便。但是,如果要将这种类型的值与其他响应DOM
事件或包含组件的HTML
组合时,则需要使用fromPlainHTML
进行转换.
IProp
当您从Halogen.HTML.Properties
和Halogen.HTML.Events
模块中查找函数时,您会看到IProp
类型突出显示。例如,这是一个placeholder
函数,它可以让您在文本字段上设置字符串placeholder
属性:
placeholder :: forall r i. String -> IProp (placeholder :: String | r) i
placeholder = prop (PropName "placeholder")
IProp
类型用于事件和属性,它使用row
类型来唯一标识特定事件和属性;当您将这些属性之一与Halogen HTML
元素结合使用时, Halogen
能够验证您应用该属性的元素是否实际支持它。
这是可能的,因为Halogen HTML
元素还带有一个row
类型,其中列出了它可以支持的所有属性和事件. 当您将属性或事件应用于元素时,Halogen
会在HTML
元素的row
类型中查找它是否支持该属性或事件。
这有助于确保您的HTML
格式良好。例如, 根据DOM
规范,<div>
元素不支持placeholder
属性, 因此,如果您尝试在 Halogen
中为div
提供placeholder
属性,则会出现编译时错误:
-- ERROR: Could not match type ( placeholder :: String | r )
-- with type ( accessKey :: String, class :: String, ... )
html = HH.div [ HP.placeholder "blah" ] [ ]
此错误告诉您,您尝试将属性与不支持它的元素一起使用。它首先列出您尝试使用的属性,然后列出元素支持的属性。Halogen
类型安全的另一个例子!
添加缺失的属性
HTML
是一种不断被修订的living standard, Halogen
试图跟上这些变化,但有时会落后.(如果您对我们如何自动化检测这些变化的过程有任何想法,请告诉我们)
您可能会发现Halogen
中缺少某些属性。例如,您可以尝试编写:
html = HH.iframe [ HP.sandbox "allow-scripts" ]
只收到此错误:
Unknown value HP.sandbox
即使看起来应该支持此属性:
type HTMLiframe = Noninteractive (
height :: CSSPixel, name :: String,
onLoad :: Event, sandbox :: String,
src :: String, srcDoc :: String,
width :: CSSPixel
)
解决方案是编写您自己的此缺失属性的实现:
sandbox :: forall r i. String -> HH.IProp ( sandbox :: String | r ) i
sandbox = HH.prop (HH.PropName "sandbox")
然后你可以在你的HTML
元素中使用它:
html = HH.iframe [ sandbox "allow-scripts" ]
请打开一个问题或PR
以添加此缺少的属性。这是一种为Halogen
做出贡献的简单方法。