Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not wait for "displayed" before applying the layout #3288

Conversation

martinRenou
Copy link
Member

@martinRenou martinRenou commented Sep 27, 2021

See #2762

Also fix #2605

And trigger a "layout-applied" event

@github-actions
Copy link

Binder 👈 Launch a binder notebook on branch martinRenou/ipywidgets/apply_layout_before_displayed

And trigger a "layout-applied" event
Widget.ResizeMessage.UnknownSize
);
});
this.trigger('layout-applied');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a "layout-applied" event to the widget, we need to document this

@@ -920,23 +918,25 @@ export class DOMWidgetView extends WidgetView {
oldLayoutView.remove();
}

return this.create_child_view(layout)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to revert this

@@ -956,7 +956,7 @@ export class DOMWidgetView extends WidgetView {
oldStyleView.remove();
}

return this.create_child_view(style)
this.create_child_view(style)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are reverting the other one, we should also revert this one.

Copy link
Member

@vidartf vidartf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From reading the code in the core ipywidgets repo, I made these notes:

  1. The layoutPromise and stylePromise are currently typed as Promise<any>, but at least the stylePromise is used under the assumption that it is a Promise<undefined | StyleView>:
    this.stylePromise.then(function (style) {
    if (style) {
    style.style();
    }
    });
  2. By not waiting for the displayed promise before calling setLayout, there is an opportunity for the StyleView to be initialized before the render method of the owning widget is called. If this happens, the child elements of the widget might not be created yet, which means that any styles applied via query strings will fail to be applied (e.g. DescriptionWidget, IntSlider, etc.). This will not currently happen for the example widgets, since they create their child elements before any await calls in the render method, but there is no guarantee that this is true for all third-party widgets.

So in conclusion: If we decide to go ahead with this pattern, point 1. needs to be addressed. On the other hand, I think we should ensure that setStyle (and probably also setLayout for consistency) does not get called until the initial render method of the view has resolved. The displayed promise seems to be the best bet for this, so I would much prefer a solution that keeps this pattern.

@vidartf
Copy link
Member

vidartf commented Oct 15, 2021

To expand on the race condition mentioned above:

class MyWidgetView extends DOMWidgetView {
  async render() {
    await someAsyncAction();
    // Create DOM elements here
  }
}

AFAICT, the DOM element creation is likely to happen after the styles are applied. Ref:

const elements = selector
? parent.el.querySelectorAll<HTMLElement>(selector)
: [parent.el];

Example of a style that has a selector for a child element:

export class SliderStyleModel extends DescriptionStyleModel {
defaults(): Backbone.ObjectHash {
return { ...super.defaults(), _model_name: 'SliderStyleModel' };
}
public static styleProperties = {
...DescriptionStyleModel.styleProperties,
handle_color: {
selector: '.noUi-handle',
attribute: 'background-color',
default: null as any,
},
};
}

I haven't been able to find any examples of a real-life widget that has both an async render method AND a style selector that targets a child element, but that doesn't mean it doesn't exist, and I believe we should support it if it does (as we currently do).

@martinRenou
Copy link
Member Author

Right. Let's close this PR then, and not touch this logic as it might break.

I will open a separate PR that only adds a "layout-applied" and a "style-applied" event, this should be enough.

@martinRenou martinRenou deleted the apply_layout_before_displayed branch October 18, 2021 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

setLayout before attaching to DOM
2 participants