I ran into a interesting situation where I wanted to make an Ember component flexible but the internal use of the copmponent had more complexity that it wasn't easy to make flexible in the usual ways.
Specifically I had a component
(I’ll call it FooBar
)
that was attempting to work like this:
<div ...attributes> Dolor commodi quidem earum quidem tenetur ullam. Eveniet nihil pariatur sed quis iure <ButtonLink @linkTo="…" @analytics="banner-click"> libero ab porro eum molestiae accusamus. </ButtonLink> Dolor accusantium ex consectetur rerum suscipit Tempore rerum adipisci tempora velit labore molestias et similique. </div>
What I needed was to be able to control the text from the consumer. The nieve approach would be to paramertarize each part.
<FooBar @href="/foobar" @analyticsKey="foo-bar" @text1="Sit ipsam reiciendis nam eos quo sint hic Vel sapiente ex" @text2="numquam quos odit qui" @text3=". Sapiente laborum aperiam quas natus sunt adipisicing Voluptatibus rerum velit perferendis nesciunt odit animi Nobis" />
FooBar
component using tightly coupled parameters.
I this approach
we’ve tightly coupled the parameters with the internal implementation.
This will cause difficulties if that format of the implementatgion changes.
Perhaps a new requirements wants to add a @text4
or we need to use this component
but want to not have a @text2
.
Changing things like this is difficult.
Instead if we could just offer the FooBar
component one parameter
that is flexible enough to express the needs any way we wish.
{{! Notice <link>numquam quos odit qui</link> in @text }} <FooBar @href="/foobar" @analyticsKey="foo-bar" @text="Sit ipsam reiciendis nam eos quo sint hic Vel sapiente ex <link>numquam quos odit qui</link>. Sapiente laborum aperiam quas natus sunt adipisicing Voluptatibus rerum velit perferendis nesciunt odit animi Nobis" />
FooBar
that is loosly coupled to the implementation.
In this example
we can change the @text
in anyway we need.
Opt-in to a link
or opt-out.
Mix and match as needed.
How can we implement this instead?
<div ...attributes> {{#each this.parts as |part|}} {{#if part.isLink}} <ButtonLink @linkTo={{@href}} @analytics={{@analyticsKey}} > {{part.text}} </ButtonLink> {{else}} {{part.text}} {{/if}} {{/each}} </div>
FooBar
component template
import Component from '@glimmer/component'; interface Signature { Args: { href: string; analyticsKey: string; text: string; }; } export default class FooBar extends Component<Signature> { get parts() { return splitTextIntoParts(this.args.text); } } function * splitTextIntoParts(text: string) { let parser = new DOMParser(); let dom = parser.parseFromString( `<root>${text}</root>`, 'application/xml' ); for (let node of dom.documentElement.childNodes) { if (node instanceof Text) { yield { isLink: false, text: node.data, }; } else if (node instanceof Element) { yield { isLink: node.tagName === 'link', text: node.textContent, }; } } }
FooBar
component logic