Alan Hou的个人博客

电商系统Shopify二次开发03:深入 Liquid 内核之标签

在前面的文章中,我们在实操中见过一些Liquid标签,比如流程控制标签。本文中我们将学习更多可用于动态修改页面内容的标签。我们会学习创建变量标签及主题标签,以及最佳使用方式。

通过学习各种类型的迭代标签和参数,我们可以反复执行一些代码块,这有助于编写更高质量的代码。最后我们会讲解一下已淘汰的标签,在一些较老的主题依然存在,因此了解它们的作用及使用方式很有必要。

本文中讲解如下内容:

本文通过探索Liquid编程逻辑会拓展有关比较运算符和不同数据类型的知识。

技术要求

首先因Shopify是一个托管服务,因此需要能够上网。本文中使用的数据集为*.csv格式,可通过https://github.com/PacktPublishing/Shopify-Theme-Customization-with-Liquid/blob/main/Product-data.csv获取。相关的代码参见https://github.com/PacktPublishing/Shopify-Theme-Customization-with-Liquid/tree/main/Chapter03。视频请见

准备工作

在开始之前,需要先创建一些产品和集合页面,用于整个练习之中。

创建产品页面

通常创建产品页面或其它页面都是自然和利于理解的过程:

  1. 我们可以导航到左侧边栏的Products区,点击Add products按钮,会自动重定向至定义产品名、描述、图片、价格及其它参数的页面。我们不会细讲如何管理产品。但想要进一步了解该主题的内容,可参见https://help.shopify.com/en/manual/products/add-update-products
  2. 为避免手动创建稍后会使用到的大量产品,我们使用GitHub上找到已创建好的产品。这里可通过.csv文件创建用于开发的多个产品。只需要通过https://github.com/PacktPublishing/Shopify-Theme-Customization-with-Liquid/blob/main/Product-data.csv下载该文件并在店铺中进行导入。
  3. 下载好文件后,点击侧边栏的Products,在打开的页面中点击Import按钮唤起弹窗开始导入:
    图3.1 – 开始进行产品导入的示例
  4. 图3.1 – 开始进行产品导入的示例
  5. 在选择好所下载的Product-data.csv文件后,就可以通过点击Upload and continue按钮开始产品的导入了:
    图3.2 – 选取.csv文件上传至店铺
  6. 图3.2 – 选取.csv文件上传至店铺
  7. 数秒后,会弹出另一个窗口用于预览我们所要导入文件中的一个产品。在确定了各字段包含正确的信息后,我们就可以通过点击Import products按钮最终确认产品的导入:
    图3.3 – 通过.csv文件上传产品至店铺的最终确认
  8. 图3.3 – 通过.csv文件上传产品至店铺的最终确认

创建集合页面

店铺中已经填充好了产品,可以创建一些集合页面放置这些新产品了。创建集合(分类)页面的流程和产品页一样简单:

  1. 先点击侧边栏的Products,然后点击展开后的子菜单Collection
  2. 进入页面后,点击Create collection按钮,会自动进入填写集合名称、描述并设置产品和其它参数。
  3. 因为我们有两种类型的产品,所以会创建两个集合并把同一类型的产品放入单个集合中。在点击Create collection按钮后,输入集合名称Outdoor,设置集合类型为Automated,这样我们就无需手动添加每个产品了。最后一步是设置CONDITIONS,这样我们就可以只添加类型为Outdoor的产品了:
    图3.4 – 创建集合及自动根据产品类型添加产品
  4. 图3.4 – 创建集合及自动根据产品类型添加产品

点击Save按钮保存所做的修改,此时会自动创建集合页面并根据所设置条件加入产品。注意在继续接下来的学习之前,我们应重复以上步骤创建一个Indoor集合,在后续的章节会使用到。如果希望了解更多有关管理集合页面的知识,请参见https://help.shopify.com/en/manual/products/collections

更新导航菜单

为快速导航,我们可以将这两个集合的链接添加到主菜单导航中:

  1. 可通过点击侧边栏的Online store,然后点击展开子菜单中的Navigation
  2. 进入该页面后,点击Main menu进入新页面,通过Add menu item可添加所需数量的链接(注意要点击 Save menu 保存)。如果希望学习更多有关管理导航菜单的知识,请参见https://help.shopify.com/en/manual/online-store/menus-and-links

至此我们已经配置好了产品页面和集合页面,接下来一起来学习Liquid的编程逻辑。

Liquid中的控制流

在前面的章节中,我们学习到了控制流标签的具体使用,如ifandor;下面我们进行更深入的了解,学习控制流标签的类型及如何使用它们。控制流标签是Liquid编程逻辑的一种类型,用于通过授权在某些条件下选取某个代码块来告诉Liquid代码执行什么任务。可以将控制流标签分成如下4个小组:

if/else/elsif标签

在之前的例子中,我们见过一些if条件语句的使用示例,在为true时执行其内的语句。下面我们就来进行实操。在此前章节中创建过Learning about the page handle页面。

但这里我们新建一个页面用于练习、巩固所学知识,这里尽量保持简化:

  1. 新建一个页面Controlling the flow of Liquid,在后续会不停使用到。如果忘记了如何新建页面,可以查看电商系统Shopify二次开发02:Liquid基础学习数据类型内的EmptyDrop一节,在其中讲解了新建页面的流程。
  2. 新建页面之后,就该编辑所新建页面的模板文件了。我们需要访问Online store下的Themes,点击我们所创建主题副本下的Actions按钮,然后选择Edit code打开代码编辑器。
  3. 进入代码编辑器之后,找到该页面对应的模板。本例中对应的模板名为page.liquid,点击该文件。
  4. 当前该页面包含两个Liquid元素: {{ page.title }}用于生成页面标题,{{ page.content }}用于生成页面内容,以及一些HTML元素,如果{{ page.content }}为空会怎样呢?我们得到的是一个空的div元素:
    图3.5 – 页面中空元素的示例
  5. 图3.5 – 页面中空元素的示例

若要解决这一问题,可以在包含{{ page.content }}div之外包裹一个条件语句用于检查页面内容是否为空:

这样通过加入条件语句就可以保证在页面中不会有多余的空字符串和元素了。

接下来我们可以添加一些占位文本,让访客知道在稍后我们会添加页面内容。此时就需要用到elseelsif语句了。借助elseelsif,我们可以创建多个条件来保证代码会正确地执行:

  1. 我们在{% endif %}之前添加一个else条件,在{{ page.content }}为空时输出不同的结果:

    借助else条件语句,在有人访问到该页面时,就不会认为这个页面出现了故障,因为他们会看到如下的消息:
    图3.6 – 执行了else条件的示例
  2. 图3.6 – 执行了else条件的示例
  3. 我们还可以添加语来检查是否开始对页面内容进行了处理并相对应地显示消息。可以通过添加elsif语句来检测页面内容是否超过了个100字符。但如果要进行这一处理,我们还需要修改已有的if语句,在页面内容大于等于100个字符时显示页面内容:

    上例中的第一个条件语句会检测页面内容是否多于100个字符。如果是,则返回true,显示页面内容。第二个if语句检测页面内容是否少于100个字符,仅在第一条语句返回false时才会进行这一检测。

通过这两个条件语句,在用户访问页面时应该有足够的信息了。但如果仔细查看语句的话,会发现{% else %}语句永远不会执行,因为页面内容要么多于100个字符,要么少于100个字符。而一个代码块中又只能有一个条件语句。解决这一问题,我们需要使用and运算符来确保代码块中所有语句都正确执行。

and/or标签

在上一章中,我们学习过andor运算符可以让我们在一个控制流标签中添加多个条件,用于创建复杂的条件语句。

使用and运算符,我们可以将另一个条件与已有条件绑定到一起,仅在左右两个语句都为真是才返回true。下面在elsif语句中添加另一个检测是否有页面内容的条件:

通过添加第二个条件,我们就保证了控制流标签可以正确地执行。在页面内容多于100个字符时第一条语句会返回true,在页面内容少于100个字符且不为空时第二个条件语句返回true。最后如果前面的语句返回了false,则会执行else语句中的代码。

类似于and运算符,也可以使用or参数来在标签中拼接其它条件。但区别于and运算符要求左右两边均返回true时才返回trueor只需要其中的一个语句为真时就返回true,这时其后代码块就会执行。

case/when标签

if/elsif/else条件语句那样,case/when是一种控制流标签,可用于创建switch语句,控制仅在返回内容匹配某个值时执行指定代码块中的代码。我们可以使用case来初始化switch语句,使用when来按顺序设置要执行代码的条件。

回到我们之前编辑的page.liquid模板文件,创建一个检查页面内容具体字符数的switch语句,根据返回true的语句渲染相应的消息。可以把这一功能放到第一个if语句之前:

上例中我们使用page.content.size初始化了case标签,让我们可以使用when语句来检查页面内容的字符数是否严格等于所设置的值。注意when语句中未使用变量。这是因为when语句仅接受值作为参数,在值精准匹配时返回true

图3.7 – 执行case/when标签的示例

借助这里的控制流标签,如果在页面内容中输入150个字符,第一个when语句返回true,结果就会显示其中的消息。对比if/elsif/else标签,它看起来好像不够强大,因为只能用于精准的匹配值。但case/when标签实际上是编程逻辑中很重要的一块,在后面的章节中我们会用它来实现复杂的功能。

我们提过添加到模板中的代码对使用该模板的所有页面都会执行。因为我们上面的代码添加到了page.liquid模板中,应当确保仅对Controlling the flow of Liquid页面执行上面所添加代码。对此可以使用到unless标签。

unless标签

类似if标签可以检测具体条件的状态,unless标签可用于检测是否不满足某个条件:

可以将unless标签的起始语句放在case起始标签之上,将其结束语句放到if/elsif/else标签之后:

图3.8 – 与Liquid控制流相关的完整代码示例

通过这里的unless标签,我们保障了所有代码仅在这一个页面中执行,不会影响到其它同样使用了page.liquid模板文件的页面。

学习了各种类型的控制流标签之后,我们为精通Liquid编程逻辑又迈出了坚实的一步,它们是实现更为复杂功能的基石。

上一章中讲解各种数据类型以及使用方式,如字符串、数值和布尔类型。但如果希望保存这些数据并在不同地方复用,且无需手动更新各行该怎么办呢?这时就会用到变量。

变量标签

可以把变量看成数据容器,保存稍后在代码中使用或按需要重写的各种类型的信息。除了保存信息供稍后使用外,变量还让我们可以使用描述性的文件作为标签,有助于理解具体变量所包含的信息类型。可以将变量标签分成如下四组:

assign标签

assign标签可以声明一个变量,使用字符串、数值或布尔值赋值。声明时可以在assign关键字后接变量名,然后接等号及赋值给该变量的数据,以及带百分号的花括号进行包裹:

变量在声明后,可以通过双花括号定界符来无限次调用它。

调用以上变量会生成三个分别赋值给各自变量的不同类型的数据:

注意单独使用双花括号定界符调用变量会返回其值。如果希望在for标签或if语句中使用变量,使用的是变量名,无需添加双花括号:

以上在多个if语句中使用了之前声明的变量在确定变量的值。因为在第一个语句中变量值等于所比较的值,所以第一条语句返回true

至此,我们学习如何给变量赋单一类型的值,但如果在变量值为一个字符串和变量的组合时该怎么办呢?这时就要使用另一个名为capture的变量类型。

capture标签

assign标签只允许赋单个值不同,capture允许我们通过其起始和结束标签获取多个值。我们可以使用一组带有百分号的花括号定界符并在capture后接所需要声明的变量名:

上例中我们一开始使用assign标签定义了两个变量,一个为数值,另一个存储字符串。在声明了这两个变量后,我们再次使用capture标签声明了一个新变量。在其它中有一个包含了之前所声明变量的消息,最后,通过在promoMessage变量外添加两个双花括号定界符来查看结果:

可以看到,使用capture标签,我们成功地创建了一个复杂的字符串消息,在不断地学习新知识后会发现这非常方便。

使用assign标签,我们学习了如何创建可存储单一类型数据的变量。使用capture标签,我们学习了如何利用多个变量创建复杂的字符串,但如何创建一个用作计数器的数值变量呢?

increment标签

不同于assigncapture标签,increment标签无需先进行声明。而在初次调用时就自动创建了该变量。increment标签让我们可以自动创建一个变量并在每次使用increment标签调用该变量时自增。我们可以在一对带百分号的花括号定界符中使用increment接所要创建变量名:

任何使用increment标签创建的变量初始值都为0。

考虑到increment标签在调用时会自动输出值,其使用场景相当有限。最常用在为HTML元素自动生成唯一标识:

上例让我们可以为每个div元素创建一个唯一ID,第一次出现increment标签时值为0,之后每次递增1:

另一个有关increment标签的重要知识是它与使用assigncapture标签创建的变量之间是独立的。我们使用assign标签创建一个变量并试下对其进行自增的效果:

刚开始,我们创建了一个变量并赋值7,之后使用increment标签连续两次调用该变量。最后,我们调用一开始使用assign标签创建的变量:

通过结果可以看出,虽然我们已经使用assign标签声明了变量numberVar并为其赋值7increment标签还是从0开始进行自增。变量名虽然相同,但却不是同一个变量。increment变量不会影响到已创建的变量,反之亦然。

我们学习了如何创建独立变量标签以为任意数量的元素创建唯一元素标识符。但假定出于某种原因,对于大量元素我们需要一个变量输出负数。实现这一功能,我们需要使用另一种类型的变量标签,即decrement

decrement标签

除使用decrement关键字来初始化变量外,decrement标签与increment标签存在两个主要不同点:

下例中,我们可以看到使用decrement标签3次调用同一变量:

例子中我们3次调用了decrement标签。因decrement变量值从-1开始,调用3次得到如下结果:

变量是一个强大的工具,通过与迭代标签相结合,让我们离编写简洁、可复用代码又理进了一步。

迭代标签

迭代标签是一种新的Liquid编程逻辑类型,让我们可以重复运行同一代码块。使用迭代标签可以节约我们的时间,否则我们就要在每次执行代码时都手动添加;此外,它也会让代码更为简洁、可读性更强。为保持简洁性,本节只讲述那些最常用的迭代标签及参数,这比全部列举更为重要,因为它们的创建概念相近。我们可以将迭代标签分成四组:

for/else标签

上一章中,我们在讲解数据类型中的数组时,在示例中使用了for循环。但我们还没能讲解for循环带给我们的所有可能性。for循环是一种可遍历代码块或数组并输出循环结果的Liquid编程逻辑。

此前,我们使用page.liquid模板进行了讲解,但现在我们要使用collection.liquid模板。

我们来编写一个for循环死出所有的产品名,放在{% section 'collection-template' %}一行之下:

上例中,我们在for标签后接product变量对collection.products对象进行遍历,返回当前在集合内的产品的名称,在Indoor集合页看到的效果如下:

图3.9 – 使用for循环列举产品名称的示例

图3.9 – 使用for循环列举产品名称的示例

虽然for循环确实生效了,我们也看到了产品名称,但并没有什么用处,因为我们已经可以看到同一拨产品了。我们来使用前一章内数据类型EmptyDrop小节讲解的通过不同方式访问页面句柄的知识。

当前代码中,我们读取的是所访问的集合中的产品。我们可以访问位于另一个集合outdoor中的产品:

虽然当前我们在查看Indoor集合,我们却获取到了属于Outdoor集合的产品列表。

跳出语句

通过名称可知,跳出语句可设置条件从循环中排除某些遍历项或在满足具体条件时停止循环。我们可以将跳出语句分成如下两组:

例如,通过{% continue %},我们的循环返回6个不满足条件的产品。而将 {% continue %} 替换为 {% break %}时,则有可能一个产品也不返回。如果所使用的句柄,Outdoor集合的第一个产品价格为$200.00,那么第一次迭代就满足了条件,这会导致{% break %}停止掉迭代,剩余的迭代不再执行。

for的参数

除跳出语句外,我们还有一些可用于定义循环限制和工作流的参数。可将配合for循环使用的参数分成如下4个组:

limit

通过名字可知,limit参数用于限制循环中所执行的迭代次数。可以在for循环起始标签的最后添加limit参数,后接一个冒号及数量。

通常在不实现条件语句又要指定迭代数量时使用limit参数。这是因为limit参数仅计算迭代的次数,而不管迭代次数与语句是否匹配。

首先将前面示例中的{% break %}替换为{% continue %} ,再添加limit参数的数量为4,这是我们希望在结果中获取的最大产品数:

上例中,在添加limit参数前,for循环返回价格小于$100的6个产品的迭代。而添加了limit参数之后,却只返回了3个产品,但我们设置的是4,这是为什么呢?

类似前面所说的{% break %}语句,limit参数用于将迭代次数限制在所赋值的范围内。打开Outdoor集合,循环的正是它的句柄。我们会注意到集合中的第一个产品价格为$200.00,这会触发if语句,进而调用 {% continue %}语句,直接将第一个产品排除在外。

结果就是我们的for循环没有打印出该产品的名称。但是,limit参数仍会将其记为一次迭代,也即会再执行3次迭代。因为接下来的3个产品价格都小于$100.00,这3个产品在for循环结束前都进行了返回。因此,limit参数通常直接用在没有其它条件的循环中。否则就存在没有达到所设定迭代数量的风险,甚至是没有迭代,因为在所设定条件未满足时可能为0次迭代。

offset参数

offset参数可用于将for循环延后几个从指定的索引处开始进行循环。我们可以在for循环起始标签的最后添加offset参数,后接冒号及数量。

我们修改前例,将limit替换为offset参数,值为4:

我们的for循环会自动跳过前4次产品迭代,从索引5处开始迭代产品,返回3个产品迭代,但如果我们在offset参数后再添加一个limit参数并将其值设置为1会怎样呢?

上例看起来可能无法运行,但它却是一个完全合法的循环。前面已经说到,limit参数让我们将for循环限制为指定次数的迭代。通过添加两个参数,for循环会从索引5处开始迭代。会按钮limit参数的设定进行一次迭代,然后会结束for循环,不管这次迭代是否通过了if语句的判断。

范围参数

范围参数提供了类似offset参数的功能。主要的区别在于通过使用范围参数,我们可以设定起始及结束索引的位置。可以在for循环标签的最后添加范围参数,在花括号定界符中写入起始值,后接两个点,然后接结束值:

读者应该还记得offset参数不包含循环内的起始位置索引。而是从下一个位置开始循环,范围参数起始及结束位置的值都包含。我们这个在for循环中将范围参数设置为(3..5) 的示例结果如下:

除接收数字作为其值外,范围参数还接受变量以及对象作为其起始和结束值:

这个for循环返回从索引3开始的所有迭代,一直到该集合的最后一个产品。既然可以使用变量和对象作为起始和结束值,我们就可以创建可复用的for循环让代码更简洁、质量更高。

reversed参数

分组里的最后一个参数是reversed,通过名字可以猜出,它可以逆转迭代的顺序。reversed参数没有值,可以将其放到for循环起始标签的最后:

上例中我们在范围参数后添加了reversed参数,这会让迭代按反向执行:

通过学习我们可以看出,参数是对for标签的一个很好的补充,可借助于它们来实现具体类型的迭代并获取所需的结果。

cycle标签

cycle是另一种强大的标签,它只能用于结合for标签来对一组字符串进行遍历,结果按最初定义的排序输出各项内容。可通过带百分号的花括号前后定界符来定义cycle标签,内接关键字cycle,再接以逗号分隔的任意数量的字符串:

上例中,我们在for循环中添加了带4个字符串的cycle标签,循环也限定为了4次。通过实现cycle标签,我们确保了第一个div元素会接收到first 作为 class,而第4个元素则接收到最后一个last,中间两个元素无class,因为字符串为空:

通过结果可以看出,cycle标签是非常有价值的工具,可将按数据按所定义的顺序传入特定的迭代中。但如果去除了循环中的limit参数或将其设置为9之后为怎样呢?

前一个例子中,我们将迭代的次数与cycle标签的字符串数量设置为一致的。在新的示例中,迭代的次数要大于字符串的数量,这表示cycle会进行重置并按顺序再次应用字符串,直到完成迭代次数为止:

至此,我学习了cycle标签的基础用法,并且知道了该标签会按照迭代的次数输出字符串,在有单个cycle标签时会很有帮助,但如果有两个或多个标签时会怎么样呢?

上例中,我们创建了两具独立的for循环,第一个限制为6次迭代,第二个限制为4次迭代。前面已经学习到,不论在cycle标签中定义了几个字符串,标签都会迭代的次数进行字符串输出:

通过结果可知,第一个for循环按对应顺序创建了6次迭代,和预期一致。但第二个for循环并没有产生相同的结果。而是从上一个cycle标签结束处开始了字符串的输出。

这种行为是符合逻辑的,因为Liquid当前无法分辨出不同类型的cycle标签。但通过引入cycle组的参数可以轻松解决这一问题。

cycle组可以令我们分隔出cycle标签,以确保每个循环从第一个位置开始输出字符串,不论在同一页面中是否使用过相同的cycle标签。在cycle关键字之后,可以添加一个带引号的字符串,后接冒号:

通过引用cycle组参数可以确保每个cycle标签单独运行:

通过结果可以看出所有的迭代都按预设的方式接收字符串,虽然在其中不止一个cycle标签。

除了forcycle标签之外,还有一种名为tablerow的迭代标签,与tablerow标签类似。唯一的不同之处在于tablerow以HTML表格的形式返回结果。受篇幅所限,这里不再讲解tablerow标签及其参数了。但如果读者感兴趣,可以阅读https://shopify.dev/docs/themes/liquid/reference/tags/iteration-tags

主题标签

主题标题是一种特殊类型的标签,让我们可以同时控制未渲染和已渲染代码。通过各种主题标签,我们可以为模板创建不同类型的HTML标记,这些模板是那些用于为购买商品的顾客创建表单的基础。此外,还可以通过它们为不同页面选择不同的主题布局,自定义区块和脚本文件创建之后可复用的代码块以及其它的功能。可以将主题标签分成如下几个组:

layout标签

在第一章中,我们讲到了Layout目录,以及theme.liquid文件的重要性,因为是在该文件中渲染的所有文件和模板。在该文件中排布了页面的总体布局。

页面主要由三个主要元素组成:头部主内容底部。假定我们希望在产品页中删去头部和底部,或是想要替换为另一组头部和底部。该怎么做呢?

  1. 第一步是需要创建另一个布局文件供当前主题的产品页面使用。可以在代码编辑器中展开Layout目录,点击Add a new layout 按钮,这时弹出一个容器,选择所要创建的布局页面类型并为新页面命名。
  2. 在下拉框中,我们选择theme作为布局类型,在随后的输入框中,将文件名设置为alternate。完成布局文件类型和名称的设置后,点击Create layout按键完成创建:
    图3.10 – 新建布局文件的示例
  3. 图3.10 – 新建布局文件的示例

这样我就成功创建了新布局文件theme.alternate.liquid。如果你拿这个文件和theme.liquid做对比的话,会发现内容一模一样。因为在新建页面、布局或模板时,Shopify会全新创建一个页面,但它会拷贝主题当前使用的默认页面。例如,如果展开Templates目录新建一个模板文件的话,新的模板中会包含此前我们对page.liquid文件所做的所有修改。

新建好了布局文件,我们就可以将其配置给产品页面了。可通过展开Templates目录,点击product.liquid文件,可在其中第一行设置布局。通过一组带百分号的花括号加关键字layout来定义layout标签,关键字后接布局文件的名称:

这时如果进入theme.alternate模板删除掉{% section ‘header’ %}{% section ‘footer’ %},产品页面的头部和底部就会消失。但在那些仍使用theme.liquid布局的页面中仍存在头部和底部。

若模板文件不包含layout标签,则会使用theme.liquid布局以保障案例。

liquid和echo标签

liquid标签是Liquid中最新添加的,非常强大,因为可以通过它在一组定界符中写入多个标签,让代码更易读、更易维护。可以通过一对带百分号的花括号内接liquid关键字,再接任意行Liquid代码。只需要定义起始liquid标签,其结束标签会自动进行关闭。

echo标签是对liquid标签的补充,让我们可以输出一个原来需要在又花括号中输出的表达式。通过echo关键字后接表达式即可定义echo标签。

下面我们使用liquidecho标签来对学习迭代类型时集合模板进行重构。

我们先看下原来在集合页面中创建的代码:

在使用liquidecho标签重构后的代码如下:

重构代码后,可以看到去除了所有带百分号的花括号定界符,并将双花括号定界符替换成了echo标签。仅剩的带百分号花括号定界符是用于包裹liquid标签的。

form标签

form标签让我们根据所调用的form标签类型可以输出各种类型的HTML <form>以及所需的<input>元素。可以使用一对带百分号花括号定界符定义form标签,初始标签内接form关键字,后接带引号的表单名称,最后使用endform标签加花括号收尾:

上例中,我们使用了带引号的product来生成产品表单标签。但注意后面还有一个逗号,后面有一个不带引号的关键字product。有些表单,比如我们正在使用的产品表单,要求我们传递一个参数来生成相应的内容。本例中的参数为product对象。

这种最常用于产品页。但我们也可以在集合页或其它可以访问product对象的页面中使用这一form标签。我们在之前集合页面的循环内加入form标签进行测试。

因为我们要处理的数据混着字符串和非字符串,先撤销之前所做的liquidecho的修改。可通过点击文件名和Delete file按钮之间的Older versions按钮:

图3.11 – 回退文件修改的示例

点击Older versions按钮会出现一个下拉菜单,其中可以看到对该文件所有进行过修改的日期及时间。每个日期都代表着我们点击过Save按钮进行保存。选择任意选择会自动将代码回滚到相应的时间点。

在回滚修改之后,我们就可以在for循环中添加form标签了:

通过在for循环中添加form标签,我们生成的代码在检查Indoor集合页面元素中可以看到HTML form标签。虽然form标签没什么问题,但我们还缺少一个重要部分来让表单具有用途,在下一章我们会进行学习。

通过对集合页面审查元素可以看到form标签已经拥有一些属性了。但如果想自己添加或修改已有的属性呢?可以直接添加属性名、冒号及属性值:

上例中,我们使用字符串在表单中添加了一个data属性。但我们也使用定义的变量修改了已有的ID值。

注意虽然我们可以直接在属性值里使用变量,但不能在form标签自身中混用字符串和变量。因此,我们使用了capture标签先拼接了字符串和increment标签,然后使用变量名在属性名处调用了它。

除了可对form传递对象作为参数,form标签还提供了一个名为return_to的特殊参数。默认在提交产品表单时,通常会重定向至购物车页面。但借助于return_to参数,我们可以选择在提交表单后跳转的页面:

上例中,我们为return_to参数赋值了一个字符串back,它会自动返回表单提交前所在的页面。除了back字符串外,我们还可以使用一个相对路径或routes属性来生成一个提交表单后返回的动态URL。

有很多种表单标签可供使用。但因为它们创建的格式一致,我们只讲解一个用于举例。更多有关其它类型的form标签及参数,参见https://shopify.dev/docs/themes/liquid/reference/tags/theme-tags#form

paginate标签

前面我们学习过所有的迭代标签,可用于重复执行同一代码块。但迭代标签有一个限制,只能在每页中输出最多50条结果。要得到更多数据的迭代,就需要使用paginate将结果分到多页中。

paginate标签必须用在对数组进行遍历的for标签中,才能将内容分成多页。在for循环中使用paginate标签的示例如下:

在示例中可以看到,paginate标签包含paginate关键字,后接返回集合中产品数组的对象,再接by参数和一个数值。根据在by参数后添加的1至50之间的数值,paginate标签即可知道每页应当呈现的最大产品数。在paginate标签内,我们可以访问paginate对象的属性。出于篇幅考虑,我们不详细讲解paginate对象。更多详情,请参见https://shopify.dev/docs/themes/liquid/reference/objects/paginate

render标签

render标签可渲染代码段文件的内容至指定位置。在电商系统Shopify二次开发01:初识Shopify中学到,代码段文件可以通过调取名称来在模板、区块中复用一段代码:

从示例中可以看出,只需在render关键字后添加代码片断文件的名称,就会渲染其中的内容。通过使用代码片断文件,不仅代码可读性更强,通过复用同一代码块也让维护更为简单。

我们新建一个名为collection-form的代码片断,将集合页面中创建的for循环内else语句的内容放入其内:

以上代码虽然看上去没问题,但在执行时会出现报错(Liquid error: product form must be given a product )。为什么呢?

代码片断文件无法访问父级元素中定义的变量。本例中的变量为在for循环中定义的product,在form中以参数对其进行了调用。解决这一问题,我们需要用参数将变量传递到代码片断中:

我们创建了一个名为product的参数,将此前创建的变量赋值给它。我们也可以为参数选择另一个名称,如collection_product。但这样的话,就需要将位于代码片断文件中form标签中的product参数修改为collection_product,并将product.title修改为collection_product.title,以匹配新的对象关键字。

在通过参数将变量赋值给代码片断后,我们可以对代码片断内的变量值进行修改,修改独立于父级模板。即使是在代码片断中重写变量值,父模板中的值还是会保持不变。注意我们可以通过参数传递任意多数量的变量至代码片断中。但如果希望向其中传递对象呢?

类似变量,我们可以使用withas参数将对象传至代码片断文件中。但仅能传递一个对象参数:

上例中,我们使用render参数传递了collection对象至代码片断文件中,在文件中可使用featured_collection关键字进行访问。

最后一个可在render标签中使用的参数为for参数。使用foras参数,我们可以对每次出现时渲染代码片断。我们对集合页面进行重构,将collection-form代码片断放到其中并使用for参数渲染产品。但注意我们无法拷贝原来for标签中的参数,因为render不接受其它参数。此外,我们需要删除表单标签中的captureproductId参数,因为increment标签不能用于render for参数。

首先我们需要将ifelse语句放到代码片断文件中,并相应地进行放置:

剩下的只是要将其它参数加到render标签中。下例中我们对render标签添加了foras参数,这让我们可以迭代collections[“outdoor”].products数组并可对集合中的每个产品渲染代码片断。此外,我们传递了迭代对象作为代码片断的参数,可在其内部使用:

因已在代码片断文件中使用了product关键字,我们成功地将对偈传递给了产品表单参数和产品标题。此时如果运行代码,会看到结果和此前一样,但是代码更为简洁了。

raw标签

raw标签可以在页面上输出未解析的Liquid代码。可以使用rawendraw标签包裹住不希望解析的内容:

上例中,我们创建一条消息用于动态更新集合名称。便因为使用raw标签包住整条消息。Liquid不会处理它,而以按照原样返回:

这种功能非常有用,尤其是在处理Handlebars这样的冲突语法时。

comment标签

通过名称可知,comment标签可用于在Liquid模板文件中留下注释。位于开闭注释标签之间的文本在Liquid模板文件中不会进行渲染。

因为消息的第二部分使用了comment标签进行包裹。Liquid仅会渲染第一部分:

注释标签非常有用,可用于在文件中留下必要信息,而又不会因可见的注释而污染DOM。

废弃的标签

废弃的标签是那些已过时的Liquid标签,不开发中应当不再使用。但可能会在一些老的主题中碰到它们,因此认知并了解它们的功能非常重要。

Shopify中废弃的唯一一个标签是include标签,其作用类似于前面讲解过的render标签:

两者的主要区别是在使用include标签渲染代码片断时,其中的代码可自动访问并修改父级模板中的变量。这不仅带来了很多性能问题,还让代码的维护变难,这也是Shopify使用render标签替换它的原因。

小结

在本章中,我们学习了在具体条件选择执行哪段代码的Liquid编程逻辑。我们掌握了变量和迭代标签,以及如何结合其它编程逻辑来反复执行代码块,返回特定结果。

最后我们学习了各类主题标签以及使用它们来输出具体模板的HTML标记。通过学习如何使用代码片断让我们的代码可读性和维护性都增强了,我们在编写高质量代码上又进了一步。在后续章节中这尤为重要,我们会进一步深入到Liquid的核心并学习Liquid对象及它们的属性。

知识巩固

  1. 如果希望展示最多7次迭代并跳过前3次迭代在for循环中该使用哪个参数?
  2. 使用capture标签创建的变量可以赋值哪种数据类型?
  3. 以下代码存在哪两个问题?
  4. 通过替换已有class属性为字符串和变量组合修改HTML生成的产品表单应采取哪种方法?
  5. 如果希望从父元素传递对象该使用什么参数?

 

退出移动版