javascript - StimulusJS 如何根据不同子 Controller 中的值计算父 Controller 中的值(购物车 -> 购物车项目)

标签 javascript design-patterns controller stimulusjs

我正在用刺激 build 一辆购物车。购物车回顾页面显示多个购物车商品。购物车商品具有单价和数量。 每个购物车商品组件都有一个数量选择器,当数量发生变化时,会重新计算 cartitem 元素的总价 totalCartItem = unitPrice x Quantity ,如果还必须重新计算购物车的话,则总价 totalCartPrice = sum(totalCartItem)

这是 html 结构的简化

<div class="cart" data-controller='cart'>
  <div class="cart-item" data-controller='cart-item'>
    <p class='unit-price'>20</p>
    <input type="number" name="quantity" value="1">
    <p class='total-price' data-target='cart-item.totalPrice' data-action="change->cart-item#update" data-target='cart-item.quantity'>20</p>
  </div>
  <div class="cart-item" data-controller='cart-item'>
    <p class='unit-price'>10</p>
    <input type="number" name="quantity" value="2" data-action="change->cart-item#update" data-target='cart-item.quantity'>
    <p class='total-price' data-target='cart-item.totalPrice'>20</p>
  </div>
  <div class="cart-total">
    40
  </div>
</div>

我的购物车项目 Controller 工作得很好,并正确更新 UI 中购物车项目的总价格。

export default class extends Controller {
  static targets = ["totalPrice", "quantity"]

  static values = {
    unitPrice: String,
  }

  connect() {
    console.log("hello from StimulusJS")
  }

  update(){
    this.totalPriceTarget.textContent = parseFloat(this.unitPriceValue) * parseInt(this.quantityTarget.value)
  }
}

但是,我现在不知道如何更新 totalCartPrice。我觉得这应该是包装 cartItems 元素的 cartController 的责任,但我不知道实现此目的的正确方法是什么 id 。 我觉得我应该在每个数字输入选择器上添加 change->cart#update 以获取每个购物车项目的数量,但是我应该在 cart#update 方法中添加什么来重新计算每个项目的总数单个购物车项目?

export default class extends Controller {
  static targets = ["total"]
  connect() {
    console.log("hello from StimulusJS")
  }

  update(){
    // let details = event.detail;
    // this.cartItemChildren.forEach((item) => console.log(item.totalValue))
  }
}

最佳答案

根据 Stimulus 文档,最好始终 start with the HTML 。通过扩展,最好以易于访问的方式构建 HTML,并查看浏览器为您提供的内容,而无需重新发明东西。

当您构建表单时,最好使用 form 元素,即使该表单没有被提交。 https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement

还有一个非常有用的 output 元素,可以帮助您根据某些输入值分配输出值。 https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output#attr-for

最后,表单元素有一个 DOM API,可让您通过其 name 属性访问表单的各个部分,即 form.elementshttps://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements

将这些整合在一起,我们可以从以下 HTML 开始...

HTML

  • 我们只是将 Controller 操作作为一个整体附加到表单上,这样我们就可以监听表单内输入的任何更改。
  • 本例使用一个 Controller ,虽然文档推荐了更细粒度的 Controller ,但这里的最小单元内容似乎是整个表单。
  • 每个input都有一个唯一的ID,每个output元素使用for属性通过这个id引用输入 - 这对于可访问性,但当我们想要解析值并更新输出时会派上用场。
  • 我们还有一个使用相同 ID 的单价附加数据属性,请注意,该数据属性没什么特别的,只是为我们提供了一种“查找”相关价格的方法。无需为这个简单的情况创建 Controller 目标。
  • 注意:这假设您不必支持 IE11(因为输出元素在该浏览器中不起作用)。
<form class="cart" data-controller='cart' data-action="change->cart#updateTotals">
  <div class="cart-item">
    <span class='unit-price' data-price-for="cart-item-1">20</span>
    x
    <input type="number" name="quantity" value="1" id="cart-item-1">
    =
    <output class="total-price" for="cart-item-1" name="item-total">20</output>
  </div>
  <div class="cart-item">
    <span class='unit-price' data-price-for="cart-item-2">10</span>
    x
    <input type="number" name="quantity" value="1" id="cart-item-2">
    =
    <output class="total-price" for="cart-item-2" name="item-total">10</output>
  </div>
  <div class="cart-total">
    Total: <span data-cart-target="total">30</span>
  </div>
</form>

JavaScript( Controller )

  • 除了总目标之外,我们的 Controller 实际上不需要任何其他内容,因为我们可以在基本表单的附加 Controller 上使用 this.element
  • 我们避免使用通用的 update 方法,并尝试使用 updateTotals 实现更具体的方法。
  • 我们在表单上使用 elements API 来获取名为 'item-total' 的元素(请注意,不会提交输出名称)。 https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements
  • 一旦我们有了每个输出元素,我们就可以通过 for 和输入的 id 以及数据属性找到相关的输入元素来查找价格。
  • 然后我们计算总计并最终更新 DOM 中的所有值。
  • 注意:数字解析非常基础,最好在这里添加一些安全性,而且我们假设您不需要支持 IE11。
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
  static targets = ['total'];

  connect() {
    this.updateTotals();
  }

  updateTotals() {
    // parse the form elements to get all data
    // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements

    const cartItems = [...this.element.elements['item-total']].map(
      (outputElement) => {
        const forId = outputElement.getAttribute('for');
        const priceElement = document.querySelector(`[data-price-for='${forId}']`);
        const price = Number(priceElement.textContent);
        const quantity = Number(document.getElementById(forId).value);
        return { outputElement, total: quantity * price };
      }
    );

    // calculate the grand total
    const grandTotal = cartItems.reduce(
      (runningTotal, { total }) => runningTotal + total,
      0
    );

    // update the grand total
    this.totalTarget.textContent = grandTotal;

    // update totals for each cart item
    cartItems.forEach(({ outputElement, total }) => {
      outputElement.textContent = total;
    });

    console.log('form updated', { grandTotal, cartItems });
  }
}

关于javascript - StimulusJS 如何根据不同子 Controller 中的值计算父 Controller 中的值(购物车 -> 购物车项目),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72476371/

相关文章:

php - 在 CodeIgniter 中找不到错误类 Controller

asp.net - 如何在asp.net mvc中使用显示 "island"数据的用户控件?

javascript - 印度卢比货币的正则表达式 '₹'

javascript - 如何比较 Vuejs 中的日期?

条件迁移算法

java - Java 的数据库迁移模式?

grails - Grails-如何从 Controller 中删除多个参数

javascript - 如何通过 CSS 定位特定设备的特定浏览器?

javascript - 除非唯一,否则默认表单字段值将被清除

java - 为什么构建器模式使用 new 关键字,尽管它的构造函数是私有(private)的?