AMP

记住用户操作

专家模式关闭

专家模式

使用专家模式来隐藏面向初学者的 Web 开发指导。

有时,我们希望单个用户交互同时影响多个组件。在一个简单的情况下,我们可能有几个关闭的折叠面板,其中有一个按钮显示“展开所有部分”。如果用户按下该按钮,我们希望同时打开所有折叠菜单。

同时,在更复杂的场景中,我们可能正在为购物网站开发一个结帐表单。在这种情况下,我们希望用户能够将他们的送货地址用作账单地址,或者显示一个地址输入表单。我们只希望向用户显示完成表单所需的字段,因此单击单个复选框可能会导致多个地址输入字段出现或消失。

回想一下,在构建我们的奶酪自行车网站时,我们构建了两个通过事件和操作链接在一起的图像轮播图。每当我们在任一轮播图中更改幻灯片时,我们都会更新另一个轮播图以指向同一幻灯片。这对于操作和事件效果很好,因为每个事件的效果数量很少。如果我们想添加第三个组件,该组件也需要在选择新幻灯片时更新,该怎么办?突然,事情可能会变得复杂。在上面的“展开所有部分”示例中,每次我们添加一个新部分时,我们都必须更新该按钮的事件处理程序以打开新的折叠面板。

在编程中,当我们需要在多个位置使用相同的值时,我们使用变量。每当在代码中引用变量时,它都会返回代码执行时的值。如果您更新变量的值,则任何引用该变量的代码都会看到更新后的值。

我们可以对我们的网站做类似的事情。我们可以将信息存储在变量中,并在整个页面中引用它们。当我们更新这些变量时,页面将使用新值进行更新。

因此,对于我们的轮播图示例,我们可以创建一个包含当前选定幻灯片的变量。每当用户更改其中一个轮播图上的幻灯片时,我们都会更新该变量的值。最后,我们重新调整每个轮播图,以便在更新我们创建的变量时更新它们选定的幻灯片。在我们的“展开所有部分”示例中,我们可以使按钮按下更新一个变量,并且我们可以更新每个折叠面板,使其在变量更新时处于打开状态。

这些类型的变量称为“状态变量”,而当状态变量更改时更新组件的过程称为“绑定”。恰如其分地,开发人员使用 <amp-state> 组件在 AMP 中管理状态变量,并使用 <amp-bind> 组件在状态变量更改时更新组件。

将信息存储在应用程序状态中

状态变量会根据用户操作进行更新。我们可以使用它们来跟踪用户单击按钮的次数、用户在表单字段中输入的文本、有关用户登录的帐户的信息或图像轮播图设置的幻灯片。

但是,我们如何设置状态变量的值呢?正如我们在中级课程中学到的,我们通过事件和事件处理程序来响应用户操作。为了同步我们的两个图像轮播图,我们使用了轮播图组件上的一个操作 (goToSlide) 来更改选定的幻灯片。类似地,AMP 运行时提供了一个名为 setState 的操作,允许我们设置我们的状态变量。 AMP.setState 操作采用一个 JSON 对象,该对象表示要存储的状态变量。因此,例如,以下代码将 wasPressed 状态变量设置为值 true

<button on="tap:AMP.setState({wasPressed: true})">
    Press Me
</button>

注意:如果您的状态包含多个状态变量,您只需要将 setState 方法传递给您想要更新的变量即可。AMP 会智能地将给定的 JSON 对象合并到现有状态中,因此无需尝试自己合并值或每次都传递每个状态变量。如果您有兴趣,可以在附录中阅读有关 AMP 如何合并这些值的更多信息。

可以为您的状态变量设置初始值。为此,请创建一个具有特定 ID 的 <amp-state> 组件。我们在该 <amp-state> 组件中创建的状态存储为对象,并将该 ID 作为键。接下来,在该 <amp-state> 组件内部,添加一个类型为 application/json 的 script 标记。然后,您可以向该 script 标记添加一个 JSON 对象,该对象表示状态的初始值。

下面的示例显示了如何设置初始状态。请注意,我们在设置状态变量时如何使用 <amp-state> 组件的 ID

<amp-state id="accordionState"/>
    <script type="application/json">
        {
            "isOpen": false
        }
    </script>
<amp-state>
<button on="tap:AMP.setState({ accordionState: { isOpen: !accordionState.isOpen }})">
    Open/Close All Sections
</button>

上面的代码与之前的“展开所有部分”示例匹配。我们为折叠面板(上面未显示)创建了一个状态变量 (isOpen)。 <amp-state> 组件的 ID 为 accordionState。当我们设置 isOpen 的值时,我们将其设置在名称为 accordionState 的对象内部。当我们引用 isOpen 的值时,我们必须将其引用为 accordionState.isOpen。清晰的命名约定对于帮助开发人员跟踪状态变量的含义非常重要。这种清晰性将使您的代码更易于理解,并且更不容易出错。

注意<amp-state> 具有超出本培训范围的其他功能。甚至可以使用从远程服务器下载的数据来设置状态的初始值!或者,您可以在浏览器中将状态的当前值打印到开发人员控制台。有关更多信息,请查看有关 <amp-state>文档

使用绑定将状态连接到元素

绑定是我们的状态变量更改导致我们的网站出现可见更新的方式。换句话说,绑定是组件上的属性和状态变量之间的连接。

在 AMP 中,我们通过用括号将绑定属性括起来来定义它。我们将该属性设置为等于表达式。每当该表达式中状态变量的值更改时,都会根据这些新值评估该表达式,并且属性会更新。

例如,如果我们想控制元素的文本,我们可以绑定到元素的 text 属性。在下面的代码中,按下按钮将使段落标记包含文本“Hello AMP!”

<button on="tap:AMP.setState({message: ‘Hello AMP!’})">
    Say Hello!
</button>
<p [text]="message"></p>

[text] 是绑定属性,它将在状态变量 message 更改时更改。在这种情况下,表达式只是将段落元素的文本设置为 message 变量的值。

text 属性可以在任何支持文本内容的元素或组件上绑定。其他此类属性包括 widthheighthiddenclass 。大多数 AMP 组件还提供可以绑定到状态变量的自定义属性。例如,我们可以绑定到 <amp-carousel> 组件的 slides 属性来控制活动幻灯片、绑定到 <amp-selector> 组件的 selected 属性来控制选定的项目,以及绑定到 <amp-img> 组件的 src 属性来更改显示的图像。可以在 <amp-bind> 文档中找到可绑定属性的完整列表。

请务必注意,绑定在状态变量的值发生变化之前不会更改。首次加载页面时不会评估绑定。在前面的“Hello AMP”示例中,即使我们使用 <amp-state> 组件为 message 状态变量加载了初始值,段落在初始页面加载时也是空的。因此,为了确保您的组件在页面加载时看起来合理,请始终为绑定属性包含默认值。

要为绑定属性添加默认值,请同时包含带有和不带括号的属性。当页面加载时,它将使用默认属性。每当通过更改相关的状态变量触发绑定时,将覆盖默认属性。例如,在以下代码中,文本的颜色将在初始页面加载时为蓝色,但在按下按钮后变为红色。

<button on="tap:AMP.setState({messageClass: ‘text-color-red’})">
    Change to Red!
</button>
<p [class]="messageClass" class="text-color-blue">Hello AMP!</p>

到目前为止,我们已经使用了直接引用单个状态变量的基本表达式,但是我们可以变得更复杂。表达式是 JavaScript 的一个子集。它们可以包括静态值(如数字或字符串)、状态变量和一组允许的函数。 文档提供了可以添加到表达式中的值的概要。

绑定属性的值设置为评估表达式时返回的值。例如,此代码显示了如何在帐户创建期间创建错误消息。以下情况检查用户输入的两个密码是否匹配

<input type="text" placeholder="Password"
    on="change:AMP.setState({ firstPassword: event.value })" />
<input type="text" placeholder="Re-Enter Password"
    on="change:AMP.setState({ secondPassword: event.value })" />
<p hidden [hidden]="firstPassword == secondPassword">
    The passwords don't match!
</p>

在上面的代码中,用户输入的密码文本存储在两个状态变量中:firstPasswordsecondPassword。 错误消息默认是隐藏的,并且如果两个密码匹配,则保持隐藏状态。换句话说,只有在密码文本不匹配时才会显示错误!

绑定是构建动态、交互式网站的强大工具。它们帮助我们将处理用户交互的方式分解为两个不同的问题:用户交互如何影响状态变量,以及状态变量的变化如何影响我们网站的内容和/或外观。 我们可以最接近问题相关的地方来解决每个问题。 我们使用 setState 操作在事件处理程序中管理用户交互对状态的影响,并通过将组件的属性绑定到状态变量来更新我们网站的内容和外观。

这种在应用程序中管理用户交互的方法可以减少错误,使您将来更容易更改组件,并使其他人更容易理解您的代码。

绑定是构建丰富、动态、交互式网站的强大工具。它们允许我们将少量状态变量绑定到多个不同的属性。此外,它们允许我们将事件中状态变量的更新与页面上绑定属性中可见效果的管理分离开来。

当仅使用事件和操作时,我们必须知道单个用户操作如何转化为整个页面的效果。有了状态和绑定,我们现在可以将与用户操作相关的信息存储到状态变量中。然后,我们让绑定决定这些状态变量如何影响我们网站中属性的值。

练习 1:使用状态来保持我们的轮播同步

为了练习我们目前所学的知识,让我们将我们的轮播从使用事件和操作转换为使用状态和绑定来保持彼此同步。提示:我们不应该再使用 <amp-carousel> 操作 goToSlide<amp-selector> 操作 toggle,就像我们在之前的课程中所做的那样。此外,让我们在缩略图下方添加一些文本,以跟踪我们选择了哪个幻灯片。

使用 <amp-state><amp-bind>文档,尝试完成以下每个要求

  • 当用户在较大的轮播或缩略图轮播中更改幻灯片时,另一个轮播的幻灯片也应更改为相同的幻灯片。

  • 将当前幻灯片索引(0、1 或 2)存储在状态变量 selectedSlide 中。

  • <amp-selector> 组件之后添加一个 <p> 标签。

  • 新的 <p> 标签应包含格式为“幻灯片 X/3”的文本,其中 X 是当前选定的幻灯片。注意:幻灯片索引从 0 开始,但幻灯片计数器文本应从 1 开始。提示:不要忘记为段落标签文本添加初始值!

  • [可选] 将状态变量存储在 ID 为 carousel<amp-state> 组件中。

完成后,结果应如下所示

样式化的缩略图轮播的图像

解决方案

包含轮播的页面部分应如下所示


<amp-state id="carousel">
    <script type="application/json">
        {
            "selectedSlide": 0
        }
    </script>
</amp-state>
<amp-carousel on="slideChange:AMP.setState({carousel: {selectedSlide:event.index}})"
    [slide]="carousel.selectedSlide" lightbox id="imageSlides" layout="responsive"
    width="412" height="309" type="slides" loop>
    <amp-img src="https://cdn.glitch.com/d7f46a57-0ca4-4cca-ab0f-69068dec6631%2Fcheddar-chaser.jpg?1540228205366"
        width="412" height="309" layout="responsive"></amp-img>
    <amp-img src="https://cdn.glitch.com/d7f46a57-0ca4-4cca-ab0f-69068dec6631%2Fcheese.jpg?1540228223785"
        width="412" height="309" layout="responsive"></amp-img>
    <amp-img src="https://cdn.glitch.com/d7f46a57-0ca4-4cca-ab0f-69068dec6631%2Fmouse.jpg?1540228223963"
        width="412" height="309" layout="responsive"></amp-img>
</amp-carousel>
<amp-selector id="ampSelector" [selected]="carousel.selectedSlide"
    on="select:AMP.setState({carousel: {selectedSlide:event.targetOption}})">
    <amp-carousel layout="fixed-height" height="78" class="thumbnail-carousel">
        <amp-img src="https://cdn.glitch.com/d7f46a57-0ca4-4cca-ab0f-69068dec6631%2Fcheddar-chaser-thumb.jpg?1540228250623"
            option="0" selected role="button" tabindex="1" width="96" height="72" layout="fixed"></amp-img>
        <amp-img src="https://cdn.glitch.com/d7f46a57-0ca4-4cca-ab0f-69068dec6631%2Fcheese-thumb.jpg?1540228249992"
            option="1" role="button" tabindex="1"  width="96" height="72" layout="fixed"></amp-img>
        <amp-img src="https://cdn.glitch.com/d7f46a57-0ca4-4cca-ab0f-69068dec6631%2Fmouse-thumb.jpg?1540228249062"
            option="2" role="button" tabindex="1" width="96" height="72" layout="fixed"></amp-img>
    </amp-carousel>
</amp-selector>
<p [text]="’Slide ‘ + (carousel.selectedSlide + 1) + ‘ of 3’">
    Slide 1 of 3
</p>

请记住在 <head> 中包含 <amp-bind>

<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>

案例研究:分析在线购物页面

绑定和状态管理对于电子商务页面至关重要。想象一下,你正在浏览一个购物网站,想买一件新的 T 恤。你在选项列表中搜索,然后决定你真正喜欢的款式和品牌。 导航到产品页面,你会看到其他选项列表。你可以在不同的合身度、尺寸或颜色之间选择。

在线购物产品页面示例

通常,当我们选择这些选项时,页面会更改以匹配我们的选择。图片可能会更新为绿色的女士衬衫或红色的男士衬衫。当我们选择不同的 T 恤合身度或尺寸时,价格可能会发生变化。其他选项可能会全部售罄,导致出现错误消息!

页面的其他部分也会显示动态内容。通常,当我们登录电子商务网站的帐户时,我们可能会在右上角看到我们的姓名。通常会有一个购物车图标,上面有一个小数字徽章,指示我们的购物车中有多少商品。当我们单击按钮购买更多商品时,该数字会更新为购物车中的新商品总数。

要开始了解电子商务网站中状态变量中存储了多少信息,请考虑一下其他人使用同一网站的情况。他们有不同的帐户信息和不同的购物车。他们可能正在查看不同的产品或启用了不同的选项。两个用户如何看到同一个网站的每种差异都将由一个或多个状态变量描述。

通过我们所学的关于状态变量和绑定的知识,我们现在开始了解如何创建这样的页面。我们需要将每个产品选项的当前值存储在自己的状态变量中。然后,我们需要与各种产品选项对应的定价和可用性信息(例如,绿色男士 XL 衬衫与红色女士 S 衬衫的价格)。最后,我们绑定动态字段(如价格、缺货错误可见性和产品图像),以考虑每个选定的选项。

练习 2:重建在线产品页面

我们的下一个目标是重建一个基本的在线产品页面,例如我们在案例研究中讨论的页面。我们的产品将是一件 T 恤,它有各种选项:男士或女士;小号、中号或大号;以及红色、蓝色或绿色。产品的基本价格将根据用户的选择而变化。最后,当用户选择不同的颜色时,T 恤产品的图像将更新。

我们正在构建的示例产品页面

我们不会在我们的 Chico’s Cheese Bikes 商店项目中构建此产品页面。相反,你可以使用此 Glitch 作为此练习的起点。注意:不要忘记重新混合它以便你可以编辑!Glitch 包含

  • 一些基本的 CSS 和 HTML 来布局静态产品页面。

  • 描述产品成本、各种选项的加价以及各种颜色产品图像 URL 的状态变量。

  • 一个 <amp-state> 组件,用于保存具有初始值的状态变量。

让我们讨论一下在给定 Glitch 起始应用程序中加载到 <amp-state> 组件中的产品信息的结构。在真正的电子商务网站中,我们可能会直接从服务器加载此数据,但在我们的示例中,我们将直接在网站中包含数据。

<amp-state id="productData">
    <script type="application/json">
        {
            "basePrice": 14
            "upcharges": {
                "fit": {
                    "Men": 0,
                    "Women": 3
                },
                "size": {
                    "Small": 0,
                    "Medium": 2,
                    "Large": 5
                },
                "color": {
                    "Red": 0,
                    "Blue": 1,
                    "Green": 0
                }
            },
            "images": {
                "Red": "https://...redtshirt.jpg",
                "Blue": "https://...Blue_Tshirt.jpg",
                "Green": "https://...greentshirt.png"
            }
        }
    </script>
</amp-state>

首先,请注意 <amp-state> 组件的 ID 为 productData。这意味着可以在绑定表达式中将商品的底价(14 美元)引用为 productData.basePrice。接下来,在 upcharges 部分中,我们列出了 T 恤的各种选项以及它们如何提高商品的价格。

例如,在表达式中引用 productData.upcharges.size.Medium 将返回中号 T 恤的加价(2 美元)。 (加价是为产品定制支付的额外价格。) 我们只需将加价添加到 T 恤的底价(productData.basePrice + productData.upcharges.size.Medium = 14 美元 + 2 美元 = 16 美元),即可获得衬衫的最终价格。 最后,images 部分包含指向我们衬衫每种颜色图像的不同 URL。

使用<amp-bind><amp-state>的文档以及上述说明,更新给定的基本产品页面以满足以下要求

  • 当合身度、尺寸和颜色选择框更新时,它们应将其新的选定值存储到 ID 为 optionsData<amp-state> 组件中的相应状态变量中。

  • T 恤的图像应与当前选定的颜色选项相对应。

  • 包含产品描述(合身度:男士 - 尺寸:小号 - 颜色:红色)的 <p> 标签应更新以反映当前选择的产品选项。

  • 包含产品价格美元部分的 <span> 标签应更新以反映商品的底价以及与当前选择的产品选项相关的任何加价。

提示:完成此练习的一项重要技能是使用一组状态变量来从另一个状态变量检索数据。 在这种情况下,我们将经常使用 optionsData 状态变量中的变量来从 productData 状态变量中检索信息。 例如,为了找出当前选定的 T 恤尺寸的加价是多少,我们将在绑定表达式中引用 productData.upcharges.size[optionsData.size]。 如果 optionsData.size 当前设置为 Small,则之前的表达式等效于 productData.upcharges.size.Small。 你可以在此处阅读有关这种在 JSON 中引用信息的方法的更多信息。

解决方案

可以在此 Glitch 示例中找到解决方案。包含更改的页面部分应如下所示


<main>
    <h2>Tina's T-Shirts</h2>
    <div class="filter-sort-selectors">
        <p>Product Fit:</p>
        <select class="product-selector" on="change:AMP.setState({optionsData: { fit: event.value }})">
            <option value="Men">Men's</option>
            <option value="Women">Women's</option>
        </select>
        <p>Product Size:</p>
        <select class="product-selector" on="change:AMP.setState({optionsData: { size: event.value }})">
            <option value="Small">Small</option>
            <option value="Medium">Medium</option>
            <option value="Large">Large</option>
        </select>
        <p>Product Color:</p>
        <select class="product-selector" on="change:AMP.setState({optionsData: { color: event.value }})">
            <option value="Red">Red</option>
            <option value="Green">Green</option>
            <option value="Blue">Blue</option>
        </select>
    </div>
    <div class="product">
      <amp-img src="https://cdn.glitch.com/f87df7df-18be-4e6e-8b0e-1145de279989%2Fredtshirt.jpg?1542275948053"
          [src]="productData.images[optionsData.color]" layout="responsive" width="350" height="350"></amp-img>
      <h2>T-Shirt</h2>
      <p [text]="'Fit: ' + optionsData.fit + ' - Size: ' + optionsData.size + ' - Color: ' + optionsData.color">
          Fit: Men - Size: Small - Color: Red
      </p>
      <p class="price">
          $<span [text]="productData.basePrice +
                         productData.upcharges.fit[optionsData.fit] +
                         productData.upcharges.size[optionsData.size] +
                         productData.upcharges.color[optionsData.color]"> 14</span>.00
      </p>
    </div>
</main>