簡體   English   中英

如何為 Glimmer 組件指定模板?

[英]How to specify template for Glimmer Component?

我有一個典型的 Glimmer “基礎”組件:

import Component from '@glimmer/component';
export default class BaseComponent extends Component { ... }

它有一個像往常一樣的模板,但該組件的實際實現是子組件,它覆蓋了一些模板 getter 和參數,以便它與各種不同的數據類型一起工作。

export default class TypeAComponent extends BaseComponent { ... }
export default class TypeBComponent extends BaseComponent { ... }

等等

我的問題是:如何指定所有子組件都應使用父 class 模板,這樣我就不必為所有子組件復制相同的相當復雜的 HTML? 從視覺上看,組件應該看起來相同,因此任何更改都必須在所有子組件類型中復制。 因此,多個重復的模板並不理想。

在 Ember Classic 組件中,有layoutlayoutName屬性,所以我可以這樣做:

layoutName: 'components/component-name'

在基礎組件中,所有子組件都自動使用定義的模板。

現在我正在遷移到 Glimmer 組件,我似乎無法弄清楚如何做到這一點。 我努力了:

  • layout屬性
  • layoutName屬性
  • template屬性
  • 使用沒有模板的子組件,希望它們會自動回退到父 class 模板。

唯一可行的方法是像這樣創建 Application Initializer:

app.register('template:components/child1-component', app.lookup('template:components/base-component'));
app.register('template:components/child2-component', app.lookup('template:components/base-component'));

但這感覺太老套了,以至於我決定先在這里問一下,是否有一種我錯過的正確方法來做到這一點?

如何為 Glimmer 組件指定模板?

tl;博士:你應該避免這種情況。

對於兩個更具體的問題,有兩個答案:

管理具有共享行為的復雜組件的推薦方法是什么?

通常,您需要重新編寫代碼以使用組合或服務。

作品

<BaseBehaviors as |myAPI|>
  <TypeAComponent @foo={{myAPI.foo}} @bar={{myAPI.bar}} />
<BaseBehaviors>

BaseBehaviors 的模板在哪里:

{{yield (hash
  foo=whateverThisDoes
  bar=whateverThisBarDoes
)}}

服務

export default class TypeAComponent extends Component { 
  @service base;
}

並且可以使用創建服務

ember g service base

然后,您無需訪問this上的所有內容,而是訪問this.base上的所有內容

忽略所有建議,我該如何在技術上做這件事

位於同一位置的組件(js + hbs 作為單獨的文件)在構建時合並到一個文件中,其工作方式如下:

// app/components/my-component.js
import Component from '@glimmer/component';

export default class MyComponent extends Cmoponent {
 // ..
}
{{! app/components/my-component.hbs }}
<div>{{yield}}</div>

上面的js和hbs文件變成下面的單個文件:

// app/components/my-component.js
import Component from '@glimmer/component';
import { hbs } from 'ember-cli-htmlbars';
import { setComponentTemplate } from '@ember/component';

export default class MyComponent extends Cmoponent {
 // ..
}

setComponentTemplate(hbs`{{! app/components/my-component.hbs }}
<div>{{yield}}</div>
`, MyComponent);

所以這意味着您可以在模塊級別的任何地方使用setComponentTemplate ,將模板分配給支持 class。

為什么不建議使用其他方法?

所有這些都是layout和相關屬性沒有進入 Octane 的主要原因。

正式支持的組件 inheritance 導致人們變得“聰明”

這本身並不是什么大問題,因為這是人們可以使用該工具做的事情。 糟糕的 inheritance 是人們根本不喜歡類的主要原因——以及為什么函數式編程一直在上升——這是有道理的,絕對有點過度糾正,因為最好的代碼同時使用 FP 和 OP,在適當的時候。 並且對這些東西沒有教條。

組件 Inheritance 更難調試

屬於“Foo”但屬於“Foo”子類的東西實際上可能不像“Foo”那樣工作,因為在 JS 中,圍繞 inheritance 沒有嚴格的規則,因此您可以覆蓋 getter、方法等並擁有它們提供完全不同的行為。

這會讓想要調試您的代碼的人感到困惑。

此外,當有人嘗試進行調試時,他們需要打開更多文件以嘗試了解更大的圖景,這會增加認知負擔。

組件 inheritance 允許人們忽略邊界

這使得單元測試變得更加困難——組件只作為“黑盒”/你看不到的東西進行測試——你測試輸入和輸出,中間什么都沒有。

如果您確實想測試中間情況,則需要提取常規函數或服務(或對特定事物進行更多渲染測試)。

我想說這是組合的經典案例,其中TypeAComponentTypeBComponent使用BaseComponent

所以你有你的BaseComponent和所有 HTML,基本上就是你的模板 我認為在這里將組件更多地考慮為可能的模板很重要,而不僅僅是完整的組件。 所以讓我們稱之為TemplateComponent

所以你有你的TemplateComponent也可以是一個模板組件。 然后你有作為TypeAComponentTypeBComponent的模板:

<TemplateComponent
  @type={{@title}}
  @title={{@title}}
  @onchange={{@onchange}}
  @propertyThatIsChanged={{this.propertyThatIsChanged}}
  ...
/>

這允許您擁有一個 getter propertyThatIsChanged來覆蓋片段。 公共行為也可以放在TemplateComponent上,或者,如果它的公共代碼,也許放在只包含共享代碼的BaseCodeComponent上,而我寧願不這樣做。

對於要替換的區域,這也打開了使用Blocks的可能性。 例如, TemplateComponent可以使用has-block來檢查:title是否存在,然后使用此塊( {{yield to="default"}} ),如果不使用{{@title}}

因此,唯一明顯的缺點是:您必須代理所有參數。 起初這看起來很難看,但通常我認為組件不要有太多 arguments 會更好。 在某些時候, optionsdata參數可能會更好,因為它可以在必要時使用 js 構建。 還應該提到的是,有一個開放的 RFC可以解決這個問題 對於即將推出的 SFC,我認為這是整體上更具前瞻性的解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM