[英]Dynamically determine existence of template only Ember glimmer component?
[英]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 組件中,有layout
和layoutName
屬性,所以我可以這樣做:
layoutName: 'components/component-name'
在基礎組件中,所有子組件都自動使用定義的模板。
現在我正在遷移到 Glimmer 組件,我似乎無法弄清楚如何做到這一點。 我努力了:
layout
屬性layoutName
屬性template
屬性唯一可行的方法是像這樣創建 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'));
但這感覺太老套了,以至於我決定先在這里問一下,是否有一種我錯過的正確方法來做到這一點?
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 是人們根本不喜歡類的主要原因——以及為什么函數式編程一直在上升——這是有道理的,絕對有點過度糾正,因為最好的代碼同時使用 FP 和 OP,在適當的時候。 並且對這些東西沒有教條。
屬於“Foo”但屬於“Foo”子類的東西實際上可能不像“Foo”那樣工作,因為在 JS 中,圍繞 inheritance 沒有嚴格的規則,因此您可以覆蓋 getter、方法等並擁有它們提供完全不同的行為。
這會讓想要調試您的代碼的人感到困惑。
此外,當有人嘗試進行調試時,他們需要打開更多文件以嘗試了解更大的圖景,這會增加認知負擔。
這使得單元測試變得更加困難——組件只作為“黑盒”/你看不到的東西進行測試——你測試輸入和輸出,中間什么都沒有。
如果您確實想測試中間情況,則需要提取常規函數或服務(或對特定事物進行更多渲染測試)。
我想說這是組合的經典案例,其中TypeAComponent
和TypeBComponent
使用BaseComponent
。
所以你有你的BaseComponent
和所有 HTML,基本上就是你的模板。 我認為在這里將組件更多地考慮為可能的模板很重要,而不僅僅是完整的組件。 所以讓我們稱之為TemplateComponent
。
所以你有你的TemplateComponent
也可以是一個模板組件。 然后你有作為TypeAComponent
和TypeBComponent
的模板:
<TemplateComponent
@type={{@title}}
@title={{@title}}
@onchange={{@onchange}}
@propertyThatIsChanged={{this.propertyThatIsChanged}}
...
/>
這允許您擁有一個 getter propertyThatIsChanged
來覆蓋片段。 公共行為也可以放在TemplateComponent
上,或者,如果它的公共代碼,也許放在只包含共享代碼的BaseCodeComponent
上,而我寧願不這樣做。
對於要替換的區域,這也打開了使用Blocks的可能性。 例如, TemplateComponent
可以使用has-block
來檢查:title
是否存在,然后使用此塊( {{yield to="default"}}
),如果不使用{{@title}}
。
因此,唯一明顯的缺點是:您必須代理所有參數。 起初這看起來很難看,但通常我認為組件不要有太多 arguments 會更好。 在某些時候, options
或data
參數可能會更好,因為它可以在必要時使用 js 構建。 還應該提到的是,有一個開放的 RFC可以解決這個問題。 對於即將推出的 SFC,我認為這是整體上更具前瞻性的解決方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.