Closed kevmeister68 closed 8 years ago
I'm not quite following. When you define a custom element that has template parts, you provide the "default" part implementation. It's just that it can be overridden later.
Hmmm. Doesn't help when my markup disappears :-( I've fixed the above now.
Sometimes it's better to illustrate the problem itself. If I have a custom element and I need to use it in 100 different places, and I need to override the default template part within that custom element, how do I do so in an elegant and maintainanble way? The answer is clearly to not repeat 100 copies of that same template override in 100 different places.
In a nutshell, what I am asking for is a convenient way to make a new "composition" of a custom element, with a specific set of template part overrides as you call them, without all the hassles of building a "full fat" custom element, and give that construct a name so I can use it all over the place in my views, without having to supply said overrides "all over the place".
In other words, I want to write this:
<image-menu-button blah blah blah></image-menu-button>
instead of:
<menu-button blah blah blah>
<template replace-part="button-content-part">
<img ... />
</template>
<menu-button>
across 100's of views, which would get real old quite quickly.
Using my menu-button and image-menu-button examples above, the closest (I think) that I can get right now is to do this:
image-menu-button.html:
<template>
<menu-button>
<template replace-part="button-content-part">
<img stuff />
</template>
</menu-button>
</template>
Effectively making a new custom element that includes the menu-button through composition. But it's not ideal because I want "image-menu-button" to be a derivation of "menu-button" and the above is not inheritance, it is composition. It would require me to re-articulate all the bindable properties of "menu-button" in "image-menu-button" (as well as any new ones) so they are exposed through <image-menu-button>
When in fact all I want to do is use "menu-button" with a pre-canned set of template parts (or you can think of it as a "different set of default template parts").
In other words, I want to reuse an existing custom element (implementation and template) through an aggregation/inheritance process, and my aggregate template just supplies an alternate set of template parts. These template parts are then the new "defaults" for those template parts.
Hoping this makes a little more sense. Otherwise I'll have to be more creative in my explanation.
I can't imagine the actual use case for this to be honest.
What you try to do seems like a dynamic composition of your custom element.
But I think rather than making one custom element to bind them all you could have one custom-element per actual use case and then let the surrounding elements viewmodel decide which custom element to pick by using the compose
-tag and dynamically binding its view-model
-property to a property on that viewmodel that holds the name of the custom element to pick.
If this is not what you meant maybe you can provide some use cases you want to choose between and explain why the custom element needs to be able to encapsulate all of them.
There is no dynamic composition going on here. You are misunderstanding what I am asking for, and hence you are not seeing the use-case as a consequence.
Let me offer a different description, albeit a little strange and "out there". Imagine your basic custom element represents the "base model" of an electric guitar, but some parts can be replaced. Now, there are various aspects of a guitar such as the paint color that are not functional changes (which I'll ignore, since "styling" takes place via a whole different avenue - CSS), but there are other aspects to a guitar that are functional changes, such as the type of bridge and the type of pickups.
If my guitar was a custom element, let's say my "default" part for the bridge part of the guitar was a fixed bridge, and let's say my "default" part for the front-pickup was an XYZ pickup and my "default" part" for the rear-pickup was a Humbucker.
Now let's say in my application I need a different configuration of guitar in lots of different places. In each situation I am going to have to specify my guitar and specify the template parts that I want replaced. For example:
<guitar>
<template replace-part="bridge">
<tremolo-bridge></tremolo-bridge>
</template>
<template replace-part="front-pickup">
<duncan-seymour-pickup></duncan-seymour-pickup>
</template>
</guitar>
and the rear pickup is left as-is.
Now, there is a company called Fender that makes a range of guitars known as a Stratocaster. There is a crap-load of different models. Now you can certainly order a custom Fender and specify all the individual parts that make up your guitar. But Fender gives simple names to a lot of their guitars, representing predefined combinations of components.
For example, you have the "American Stratocaster HH" - a guitar with two Humbucker pickups and a tremolo bridge.
So the whole point of my request, since the outset, has asked for a way to combine a set of template parts with its base custom-element to form a newly named component that can be referred to in a simple manner.
So I can write this effectively in my templates:
<american-stratocaster-hh></american-stratocaster-hh>
and not the thing I wrote above. I keep calling this "pre-canning" or "pre-packaging". There is nothing dynamic about it - it's just a name and potentially some extensions to an underlying custom element.
The key point, however, is that depending on what your replacement parts are, the interface of the guitar might change slightly. For example, a fixed bridge guitar has no tremolo bar hence the base interface of our guitar would logically have no means to control the tremolo bar. But if I replace the fixed bridge with a tremolo bridge, I might also need a corresponding extension to the interface of the guitar to now be able to control the tremolo bar.
So I'm not just talking about supplying a simple "alias" for a set of template parts, there is also the potential that the interface of the newly "packaged" component changes slightly to support whatever it is that is required by the replaced parts. Make no mistake here - the function of the guitar has not changed, but the interfaces of the "parts" have been surfaced upwards/outwards to the guitar interface so they can be used effectively
And also note that I am not saying this "surfacing" should somehow happen automagically within Aurelia, that's up to whoever is doing the packaging.
At it's most basic level, this request is about achieving easier re-use for custom-elements that are complex but have been designed to be composable through template parts.
One final thing: Microsoft Silverlight, whilst having its own problems, clearly recognised the distinction between the functional behaviour of a component and its visual representation. Like Aurelia, a Silverlight component has a template to describe the markup, and parts of the markup could be given names to describe them as template parts. Hence the underlying class code could still refer to the pieces of the template it needed to in order to function. Silverlight class writers were encouraged to put annotations on their class to describe what the expected template parts were. Silverlight had the concept of a "style" which is conceptually similar to CSS in HTML, except that a Silverlight style could actually replace the entire template of a component. Whilst the approach was different, the net result was that you could achieve what I am describing above in Silverlight by replacing the template, and perhaps - if needed - deriving a new component from the old one to surface additional behaviour.
Ok now I got a better understanding of what you want.
Let's start at the lowest level where each component (e.g. the Humbucker) needs an encapsulation of its view and viewmodel. And it may have special properties or behavior that other pickups lag. What you could do is to provide a standard implementation of what a pickup offers and use a mixin decorator to apply this standard implementation to your Humbucker.
By doing so you could achieve a loosely coupled quasi inheritance. You could then have a custom element for every part of your instrument: one for the standard implementation and one for every special component that can be put in its place.
On a sidenote the replacement mechanism that currently only supports the single placeholder <content></content>
is about to change to support multiple replacement positions via <slot></slot>
where every slot can be named and explicitly used. This could help you with the "composition" you try to achieve.
Still not sure if that nails it, but I guess we're getting closer^^
I'm not familiar with the concept of a mixin decorator - what is that?
Well in theory you can use a class decorator to just merge other imported classes into your decorated class. So as a result your decorated class should have all named attributes and methods from the mixed in classes.
I just tried to test it out, but got an exception:
Unhandled promise rejection Error: mixin is not a function
My test was the following:
smartTest.js
export class SmartTest {
soSmart(){
console.log("ask me anything...");
}
}
dumbTest.js
import {mixin} from 'aurelia-framework';
import {SmartTest} from './smartTest';
@mixin(SmartTest)
export class DumbTest {
attached(){
soDumb();
}
soDumb(){
console.log("don't even ask...");
soSmart();
}
}
Did I use the new decorator properly @EisenbergEffect? Tried it with the latest skeleton-navigation as is.
I found out several new things and now have working examples:
smartTest.js
export class SmartTest {
static soSmart = () => {
console.log("ask me anything...");
}
}
dumbTest.js
import {mixin} from 'aurelia-metadata';
import {SmartTest} from './smartTest';
@mixin(SmartTest)
export class DumbTest {
soDumb() {
console.log("don't even ask...");
}
}
anythingTest.js
import {inject} from 'aurelia-framework';
import {DumbTest} from './dumbTest';
@inject(DumbTest)
export class AnythingTest {
constructor(DumbTest) {
this.dumb = DumbTest;
}
attached() {
this.dumb.soDumb();
this.dumb.soSmart();
}
}
output in console:
don't even ask...
ask me anything...
Note:
aurelia-metadata
instead of aurelia-framework
aurelia-metadata
again to get it working (seems as if an npm mapping was missing)I think the new shadow dom v1 slot default content will handle the rendering side of this.
I think this issue has been wholly misunderstood. This issue was never about rendering.
It was about having a convenient means to compose/assign a fixed set of template parts against a custom element (which defined those template parts), and give the whole thing an easy-to-use name. My second post from the top illustrates the use-case.
The only workaround I can envisage is to define a new custom element as a HTML-only custom element, with an appropriate filename, and stuff the template parts into that. Using my example from above, I would create image-menu-button.html with this in it:
Is that the solution I have been requesting all along?
Yes, that should work fine. You are just creating a new element out of existing elements, pre-defining the template parts. It's simpler with an html-only element. You may need add bindables to the template to pass those through, but that's pretty easy.
Let's say I build a custom element called menu-button that is basically intended to be a component with button-like behaviour, but the button like click opens a menu.
The visual content displayed for the button is a typical candidate for a template part:
For example, I might want to put an image within the button, or text and an image, or a clock component -- whatever.
So template parts are great for doing "one-off" types of replacements, but what is missing is a way to "prepackage" a custom element with a set of template parts to make a "precanned" version of a component. Whether to call that a custom-element or not I don't know.
The key point here is that if your app uses 100 image-menu-button components you don't want to be specifying the same template replacement part in 100 places. You might as well write a new custom element to eliminate the potential maintenance burden. But then the newly written custom-component will copy-and-paste the original template, so that's a maintenance burden also (just a smaller one).
But from a base menu-button we could pre-can the most common menu buttons as perhaps a text-menu-button and image-menu-button, and then consumers needing these common components would not need to provide the template replace-parts themselves.
In practice the view-model side of things could theoretically be handled through simple inheritance (eg. class ImageMenuButton extends MenuButton) although I believe there are certain issues here regarding binding. The missing piece relates to defining the template for the new component, by providing a means to reference the underlying component. For example:
image-menu-button.html: