Resources:
Before submitting an issue, please consult our docs.
Stencil version: (run npm list @stencil/core from a terminal/cmd prompt and paste output below):
@stencil/[email protected]
I'm submitting a ... (check one with "x")
[x ] bug report
[ ] feature request
[ ] support request => Please do not submit support requests here, use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/
Current behavior:
If a component extends another component then the props, state, etc. are not inheritted.
Expected behavior:
It should be possible to extend a component and have the derived component inherit the base class features (Prop, State, etc.).
This is the behavior with Ionic v3 using Angular so existing apps would break when moving to Ionic v4.
One use case for inheritance is a base component with all of the logic (properties, event handling, etc.) and derived implementations with alternate rendering methods. For example, a component that can render with Bootstrap markup on the web and Ionic markup in an app.
Related code:
@Component({
tag: 'test-hello',
})
export class Hello {
@Prop() name: string;
render() {
return (
<div>
Hello {this.name}.
</div>
);
}
}
@Component({
tag: 'test-goodbye',
})
export class Goodbye extends Hello {
render() {
return (
<div>
Goodbye {this.name}.
</div>
);
}
}
<test-hello name="John" />
<test-goodbye name="John" />
Produces the following output:
Hello John.
Goodbye .
Good catch, thanks.
I should also add, Ionic Angular v4 would continue to work the same Angular way for user components.
For stencil components we are avoiding inheritance. We would prefer to use composition. Your code could work like this. For more advanced composition you would embed on component within the other's render. Does this make sense?
The example probably is too simple to show how this would work. But probably in your example just add the name Prop to the test-goodbye component.
I understand the preference for composition in many use cases because of the simplification in understanding the code but there are still some use cases where composition does not work very well.
One example we have used is complex components with many Props and lots of internal logic that differ only in their rendering (e.g. use Ionic for mobile and Bootstrap for web). In this case you don't want all the rendering code in a single component because it would increase the size of the code for both platforms so we inherit from a base implementation.
We are creating a 100% meta data driven framework to support user interaction with any complex dataset. This includes automatically generated layouts for lists, detail screens, forms, graphs, etc. for any schema. Stencil seems like a huge improvement over Angular rendering and change detection but our use case may be pretty different from the typical app.
When you say you are not supporting inheritance does that mean you are not supporting inheritance of meta data driven behavior (Prop, State, Method, etc.) or that you are not allowing components to inherit from a base class at all?
Remember to mark this as a breaking change in Ionic 4.0 since this is different than the current behavior.
Hey! Thanks for the great explanation. Would you mind sharing some code as an example of your use case?
Hello all! I am going to close this issue for now as it has been a while since there was any activity. Thanks for using Stencil!
@jthoms1, @jgw96, @adamdbradley: are there any examples on how we can extend existing Ionic Stencil components?
My use case: I need to change Range component to have 2 range-bars in different colors on the left of the knob and 2 range-bars on the right. (_basically to split space between side and knob in half_).
Am I forced to copy-paste the ion-range and have a forked version with own props, logic & render?
Ideally I would want to just:
barLhalf, barRhalf)updateBar() methodrender() methodHi any news on this topic ? I am trying to create a component which acts as a wrapper for other components within it that all share an exhaustive list of Props, is there any way to use inheritance to pass them down and if not what exactly does composition mean. I essentially have a map component which will generate a WebGL based map if webGL is supported or a JS-based map if not - they share the same properties (11+). I'm happy to provide more details if needed
If we can't inherit from components, can we at least have abstract ones?
I've got about 20 lines of componentWillLoad code that use a @Prop({ context: foo }). I'm currently copy-pasting this into all my components. If I could have an abstract base class so they could share that instead it would be _sweet_.
Here's another use-case I just tried and failed.
I've got a basic timer component which just counts down towards a deadline and upon reaching the deadline it can start counting upwards again. Here's a _simplified_ version of the timer.
export class Timer {
@Prop() deadline: Date;
@State() seconds: number;
private _interval;
componentWillLoad() {
_interval = setInterval(() => {
this.seconds = Math.floor((this._deadline - Date.now()) / 1000);
}, 1000);
}
getTime(): string {
// format the time according to the time left to/passed from zero.
}
render() {
return (<span>{ this.getTime() }</span>);
}
}
Now, I want to develop a timer that will follow a football game. The timer will count to 45, then there's a 15 minute break in which it will count downwards from 15 to 0, at which time it will start counting up again towards 90. There will be additional logic for if the match goes to overtime. My matchtimer wouldn't need to be more complicated than this.
export class Matchtimer extends Timer {
getTime(): string {
if (super.seconds > 0) {
// the match haven't started yet, so use default countdown logic.
return super.getTime();
} else {
// do custom logic according to rules explained above
}
}
render() {
return (<span>{ this.getTime() }</span>);
// or even: return super.render.call(this);
}
}
The problem here is of course as mentioned above, the @State on seconds isn't transferred to the Matchtimer and so there will be no updates on that component. I did try to get around this using @Watch("seconds") in Matchtimer, but it says it can only watch @Prop or @State.
Additionally it would appear that the Timer.deadline is never getting set, probably due @Prop being ignored as well same as @State above.
This has been on my radar with some of my recent work. I think the question about inheritance, composition or something else. I think we need to provide a best practice to the community on how best to solve these problems.
Hopefully soon we will have an answer for this. Thank you for the examples!
One other example would be to be able to extend _Ionic 4_ components.
For example, I'd like to extend the Action Sheet component to add a thumbnail additionally to the header and subheader. I'd add a new @Prop() image and overwrite the render function. Currently, not sure how I'd do, apart from copying the original code with adjusted paths.
Thanks for the issue! This issue is being closed due to inactivity. If this is still an issue with the latest version of Stencil, please create a new issue and ensure the template is fully filled out.
Thank you for using Stencil!
Most helpful comment
This has been on my radar with some of my recent work. I think the question about inheritance, composition or something else. I think we need to provide a best practice to the community on how best to solve these problems.
Hopefully soon we will have an answer for this. Thank you for the examples!