> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 隐藏和重命名购物车行

> 一个使用行转换和比较器的 Cart SDK 用例：隐藏由应用注入的行、重命名它们，并控制其渲染顺序。

隐藏顾客不该看到的行，重命名显示效果不佳的行，并控制所有内容的渲染顺序——完全不改动购物车中的实际内容。

这取代了以往用 CSS 或 `style.display = 'none'` 隐藏购物车元素的旧模式。行转换会在购物车每次渲染时应用，因此它能在更新、重新渲染和抽屉重新打开后依然生效。

<div id="hide-a-line">
  ## 隐藏一行
</div>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Hide free lines from the drawer. The cart total is unchanged.
window.aftersell.cart.hooks.registerLineTransform((line) => {
  if (line.finalLinePrice === 0) {
    line.setHidden(true);
  }
});
```

<Warning>
  \*\*隐藏不等于移除。\*\*被隐藏的行仍留在顾客的购物车中，仍计入总额，并会一路进入结账流程；它只是不会在抽屉中被绘制出来。它确实会从 `getCart().items` 和 `itemCount` 中消失，因此你自己的代码也不再能看到它。如果想真正移除它，请使用 [`removeItem`](/zh/aftersell/cart/sdk-actions#removeitemkey)。
</Warning>

值得隐藏的常见情况：

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerLineTransform((line) => {
  // An app-injected line, tagged with a private property.
  if (line.properties?._bundle_child) {
    line.setHidden(true);
  }

  // A specific SKU that shouldn't be shopper-managed.
  if (line.variantId === HIDDEN_VARIANT_ID) {
    line.setHidden(true);
  }

  // Gift cards issued by a loyalty app.
  if (line.isGiftCard && line.finalLinePrice === 0) {
    line.setHidden(true);
  }
});
```

<div id="relabel-a-line">
  ## 重命名一行
</div>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerLineTransform((line) => {
  // Flag discounted lines.
  if (line.finalLinePrice < line.originalLinePrice) {
    line.setTitle(`${line.title} (On sale)`);
  }

  // Make the subscription cadence read naturally.
  if (line.sellingPlan) {
    line.setVariantTitle(`Delivered ${line.sellingPlan.name.toLowerCase()}`);
  }

  // Drop a meaningless variant label.
  if (line.variantTitle === 'Default Title') {
    line.setVariantTitle(null);
  }
});
```

行上可用的四个 setter：

| Setter                            | 效果                                                                               |
| --------------------------------- | -------------------------------------------------------------------------------- |
| `setHidden(bool)`                 | 在抽屉中隐藏该行。                                                                        |
| `setTitle(string)`                | 更改显示的标题。                                                                         |
| `setVariantTitle(string \| null)` | 更改显示的变体标签。`null` 表示移除。                                                           |
| `setInternalProperties(obj)`      | 合并仅用于渲染的属性，供[自定义代码区块](/zh/aftersell/cart/custom-code-blocks)读取。永远不会持久化到 Shopify。 |

<div id="control-the-order">
  ## 控制顺序
</div>

比较器采用与 `Array.prototype.sort` 期望的相同形式，并在隐藏和重命名之后运行：

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Subscriptions first, then everything else.
window.aftersell.cart.hooks.registerLineComparator((lineA, lineB) => {
  return (lineB.sellingPlan ? 1 : 0) - (lineA.sellingPlan ? 1 : 0);
});
```

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Push free gifts and add-ons to the bottom.
window.aftersell.cart.hooks.registerLineComparator((lineA, lineB) => {
  return (lineA.finalLinePrice === 0 ? 1 : 0) - (lineB.finalLinePrice === 0 ? 1 : 0);
});
```

对你不关心的行对返回 `0`。比较器以决胜（tie-breaker）方式组合：第一个返回非零值的比较器决定该行对的顺序，返回 `0` 则把决定权交给下一个比较器，而不是强行规定顺序。

<div id="hide-a-whole-block-not-a-line">
  ## 隐藏整个区块，而不是某一行
</div>

行转换作用于购物车行。若要按国家/地区、市场或货币显示不同的**区块**设置（例如不同的奖励层级），请使用[购物车编辑器中的条件](/zh/aftersell/cart/blocks-overview#show-or-hide-by-market-country-or-currency)。无需代码，且能在购物车重新设计后继续生效。购物车总额和购物车内容**不是**编辑器条件类型。

当你的规则是条件无法表达的内容（购物车中的商品 ID、自定义总额等）时，再使用 SDK。这种情况下，请在 [shadow root](/zh/aftersell/cart/sdk-overview#shadowroot) 中查询公开的 `cart-external-*` 类：

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_updated', (state) => {
  const root = window.aftersell.cart.shadowRoot;
  if (!root) return;

  const rewards = root.querySelector('.cart-external-rewards');
  if (!rewards) return;

  const hasExcluded = state.items.some((line) => EXCLUDED_PRODUCT_IDS.includes(line.productId));
  rewards.style.display = hasExcluded ? 'none' : '';
});
```

<Warning>
  这段代码在每次渲染之后运行，因此在你的代码隐藏区块之前，它可能会闪现出来。条件渲染没有这个问题，所以只要适用就优先使用条件渲染。
</Warning>

<div id="things-to-get-right">
  ## 需要注意的要点
</div>

* \*\*转换是初始化调用。\*\*在初始化脚本的顶部注册它们；无需 `ready()`。
* \*\*所有人的转换都会运行。\*\*你的转换会与其他应用注册的转换组合。你无法替换它们的转换，它们也无法丢弃你的。
* \*\*转换无法更改价格、数量或身份。\*\*它只改变渲染的内容。要进行真正的更改，请使用[操作](/zh/aftersell/cart/sdk-actions)。
* \*\*抛出异常的转换会被静默跳过。\*\*其余的仍会运行。开发时请检查 [`aftersellCartDebugEvents`](/zh/aftersell/cart/sdk-overview#debugging)。
* **`registerLineTransform` 会返回一个注销函数**，以便之后需要时撤销注册。

<div id="where-to-go-next">
  ## 下一步
</div>

* **[钩子](/zh/aftersell/cart/sdk-hooks)**：完整的钩子参考。
* **[购物车对象](/zh/aftersell/cart/sdk-cart-object)**：可用于分支判断的每个字段。
* **[自定义 CSS](/zh/aftersell/cart/custom-css)**：`cart-external-*` 类命名约定。
