I’m working on a PR to the Poll plugin.
I’m attempting to render a Glimmer Component as a widget tree leaf as I’ve done several times before, but getting this strange error I’ve not seen before:
TypeError: this.parentMountWidgetComponent is undefined
firing on this line:
setComponentTemplate(template, component);
this._componentInfo = {
element,
component,
@tracked data: this.data,
setWrapperElementAttrs: (attrs) =>
this.updateElementAttrs(element, attrs),
};
this.parentMountWidgetComponent.mountChildComponent(this._componentInfo);
}
updateElementAttrs(element, attrs) {
for (let [key, value] of Object.entries(attrs)) {
if (key === "class") {
value = [element[INITIAL_CLASSES], value].filter(Boolean).join(" ");
}
if ([null, undefined].includes(value)) {
element.removeAttribute(key);
It seems that this.widget?._findView() || this._emberView
is undefined
.
(this.widget
exists)
if ([null, undefined].includes(value)) {
element.removeAttribute(key);
} else {
element.setAttribute(key, value);
}
}
}
get parentMountWidgetComponent() {
return this.widget?._findView() || this._emberView;
}
}
RenderGlimmer.prototype.type = "Widget";
/**
* Define a widget shim which renders a Glimmer template. Designed for incrementally migrating
* a widget-based UI to Glimmer. Widget attrs will be made available to your template at `@data`.
* For more details, see documentation for the RenderGlimmer class.
* @param name - the widget's name (which can then be used in `.attach` elsewhere)
My very early stage code is here:
}
},
});
createWidget("discourse-poll-option-dropdown", {
tagName: "div.irv-dropdown",
buildKey: (attrs) => `discourse-poll-option-dropdown-${attrs.option.id}`,
html(attrs) {
return [
new RenderGlimmer(
this,
"div.irv-dropdown-content",
hbsCli`<DropdownSelectBox @value={{@data.value}} @content={{data.content}} @onChange={{action (mut data.selectRank)}} @options={{hash showCaret=false filterable=false none="poll.options.irv.abstain" }} class="poll-option-dropdown"/>`,
{
...attrs,
value: 0 || "poll.options.irv.abstain", //option.value
content: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
selectRank: this.selectRank.bind(this),
}
),
Is it legal to use RenderGlimmer
in this context?
1 Like
This relies on an ancestor having this property?
this._findAncestorWithProperty("_emberView")
which comes back undefined.
Or itself having an _emberView
(which it doesn’t)
I’m guessing the ancestor should have had it set here:
const t0 = Date.now();
const args = this.args || this.buildArgs();
const opts = {
model: this.model,
dirtyKeys: this.dirtyKeys,
};
const newTree = new this._widgetClass(args, this.register, opts);
newTree._rerenderable = this;
newTree._emberView = this;
const patches = diff(this._tree || this._rootNode, newTree);
traverseCustomWidgets(this._tree, (w) => w.willRerenderWidget());
this.beforePatch();
this._rootNode = patch(this._rootNode, patches);
this.afterPatch();
this._tree = newTree;
OK a bit more info.
When the Poll is rendered (as a tree of widgets), this line is executed, but when you transitiion back to vote, it is not, thus for some reason there is no ancestor with an “_emberView” (Or it is not being found)
The source of this problem might be that the widget is never re-rendered:
rerenderWidget() {
Is not fired again despite adding a new tree of widgets … so this property is never set.
… thus the solution to this might be scheduling a re-render when the Vote button is hit …
toggleResults() {
**this.scheduleRerender();**
this.state.showResults = !this.state.showResults;
},
… no this doesn’t solve it.
More info:
_findAncestorWithProperty(property) {
let widget = this;
while (widget) {
const value = widget[property];
if (value) {
return widget;
}
widget = widget.parentWidget;
}
}
This is searching up the tree for the property.
On the initial view this appears at the post-stream
level.
But somehow the widgets appear to be losing their ancestry …
OK so I’ve dug a little further:
const refreshAction = dirtyOpts.onRefresh;
if (refreshAction) {
this.sendWidgetAction(refreshAction, dirtyOpts.refreshArg);
}
}
return this.draw(h, this.attrs, this.state);
}
_findAncestorWithProperty(property) {
let widget = this;
while (widget) {
const value = widget[property];
if (value) {
return widget;
}
widget = widget.parentWidget;
}
}
This is the hierarchy the widget traversal occurs up:
discourse-poll-option-dropdown-245da0f65a66dbd539bcd27e501d759a [widget.js:250:14](webpack://discourse/widgets/widget.js)
discourse-poll-option-245da0f65a66dbd539bcd27e501d759a [widget.js:250:14](webpack://discourse/widgets/widget.js)
poll-container-poll-1247 [widget.js:250:14](webpack://discourse/widgets/widget.js)
poll-poll-1247
Now the very last, existing widget … has no parent widget.
parentWidget
is undefined
.
And the kicker is that it has no “_emberView” property, so the result is undefined
So the property is never found and at that point the traversal stops.
3 Likes
david
(David Taylor)
April 17, 2024, 12:26pm
4
The poll plugin is a little unusual in that the widget is mounted inside a post’s cooked HTML, rather than directly inside an Ember template via the <MountWidget
component. So we probably need to add some extra logic here… will take a look
Thanks for reporting @merefield !
3 Likes
david
(David Taylor)
April 18, 2024, 2:29pm
6
I think if we merge this, it should make your code work @merefield
discourse:main
← discourse:widget-through-cooked
opened 02:12PM - 18 Apr 24 UTC
In this case, the top-level widget being glued must have a `_postCookedWidget` a… ttribute.
It’s not particularly pretty, but I think it’s ok since this is a pretty rare situation, and we hope to totally rip out all this widget/RenderGlimmer stuff in the not-too-distant future
4 Likes
Thanks David. Yes it makes sense that a compromise might suffice in the meantime
I’ll test it once/if it is merged (and I’ll have to update my fork! )
4 Likes
Really appreciate the fast turnaround on this one, David.
I can confirm this is now working for me.
I’ll post here if I find any further issues with this arrangement.
(Screenshot Caveat: Very early days for this project!)
2 Likes
system
(system)
Closed
May 19, 2024, 11:15am
10
This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.