-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Migrate view layer from Backbone.View to CustomElement/WebComponent #3573
Comments
My first thoughts are that this would probably need to be done over multiple major releases. First I think removing Backbone as a dependency is a good idea. I also think it'd be nice if the pieces could be created so that we could continue supporting the current API / Backbone.View while allow with a plugin or a I'm also assuming we could essentially provide the class mixins for direct use but also provide the class that mixes in the defaults (at least for a while) to keep close similarities to the current API? essentially: const View = withView();
export { View } |
Yes. Babel to support ES6 class and polyfill for Web Components
Yes |
I'm 100% on the feeling Marionette should move toward this direction. I'm actually tweaking a View to use JSX and VDom with snabbdom. I made it work but it just feels wrong because there is so much unneeded things for this case, inherited from Backbone concepts... I reached the limit of the |
BTW: i'm following the same route (after a short time using idom)
Also inherits from string renderer concepts like dom:refresh and dom:remove events |
I'm all for moving the lib towards more modern trends, but what we can't do is require large rewrites or removal of the string templating in order to do so. What are the smaller steps to improving |
Maybe @blikblum and I could work on a View concept as the final goal to get to and then split it up step by step to get a better overview. What do you think ? |
Yes. Instead of a simple function the renderer should be an object with a few functions like the domApi From top of my mind it should control if reinitialize regions at each render, if trigger dom:remove, dom:refresh |
I don't entirely understand the const MyView = View.extend({
getTemplate() {
// Similar logic could easily be done in the template itself, but this is a dramatic example
if(this.model.get('foo')) return FooTemplate;
return BarTemplate;
},
ui: {
datepicker: '.datepicker'
},
onDomRefresh() {
this.ui.datepicker.datepicker();
},
onDomRemove() {
this.ui.datepicker.cleanUpPlugin();
}
}); |
With VDom, there's no way to know when the dom will change at Take a real example (template is jsx, state is the serialized model): export default Marionette.View.extend({
events: {
'input input': 'updateModel'
},
modelEvents: {
change: 'render'
},
template(state) {
return (
<div>
<h3>{state.name}</h3>
<input type="text" name="name" className="form-control" value={state.name}/>
<input type="checkbox" name="showDatePicker" className="form-control" value={state.showDatePicker}/>
{state.showDatePicker && <div id="my-date-picker"/>}
</div>
)
},
updateModel(e) {
let data = Backbone.Syphon.serialize(this)
this.model.set(data)
}
}) Every time an input event occurs, the view is re-rendered. If When the name input changes, there's no change to datepicker element but There are two ways of initializing/destroying a jQuery plugin with VDom: manually check at each render like here or use the VDom specific features. In case of Snabbdom it provides hooks for dom mutation |
What do you think of a 1st step being a rewrite of render() {
if (this._isDestroyed) { return this; }
this.triggerMethod('before:render', this);
if (this.shouldSetElement()) {
this._setElement();
}
if (this.shouldReinitRegion()) {
this._reIniRegion();
}
let el;
if (this.shouldRenderTemplate()) {
el = this.attachElContent(this._renderTemplate());
}
this.bindUIElements();
this._isRendered = true;
this.triggerMethod('render', this);
return el;
} A lot of changes here, let me explain step by step: shouldSetElement1st big breaking change that would involve ditch Backbone.View to put all inside Marionette.View. The issue of Backbone.View is that I propose that the root element, when having one, should only be set at (first) render. A default string template implementation matching the actual behavior could be something like shouldSetElement() {
return !this._isRendered;
},
_setElement() {
// same logic than actual, checking el, tagName, etc.
} The advantage of this API is that someone can keep string templates like handlebars, but with the view.el matching the template root node by writing a renderer like this shouldSetElement() {
return false;
},
attachElContent(el) {
this.el = el; // naive setElement bindings should be reset
return el;
} Or having a renderer dealing with VDOM shouldSetElement() {
return false;
},
attachElContent(vnode) {
if (!this.el) {
this.el = document.createElement(vnode.sel);
}
this._vel = patch(this._vel || this.el, vnode);
return this._vel;
} You could even return souldSetElement() {
// let me handle this
return false;
} shouldReInitRegionSolve the should we or not re-init the regions at re-render case. Default renderer could just return shouldRenderTemplateThis one might seems useless, but I like being consistent and it might open some doors to a thing like The biggest change is more the return valueShould return the node or vnode to be able to use View as a component. A lot of library allow view as function or Class if they have a template() {
// I dont like the CapitalCase but needed for JSX custom elements
const SomeChild = new ChildView();
return <div><h1>title</h1><SomeChild></SomeChild></div>;
} rendererA default renderer matching the actual shouldSetElement() {
return !this._isRendered;
},
shouldReinitRegion() {
return true;
},
shouldRenderTemplate() {
return true;
},
attachElContent(html) {
this.Dom.setContents(this.el, html, this.$el);
return this.el;
}, Then community and / or Marionette team could provide more renderer for people to use and even mix with others renderer so you could do import {jsxVdom} from 'marionette/renderers';
export const VDomView = Marionette.View.setRenderer(jsxVdom); There is a lot to think and solve above this, but I think this is a start. |
I am rather against adding public API to the view. Ideally this is done from the renderer entirely. However I don't really see calling |
In any case, the changes should be made post v4 |
I agree, though.. should the renderer API be left experimental through v4 allowing for some breaking changes? It would only be breaking of 3rd party renderers. |
I think so |
I think it's doable to put all the logic inside a renderer
It is a non real option because anyone returning always false instead of
We all are on the same page on this one :) |
For the record. Link of a nice article about we components: http://blog.ionicframework.com/the-end-of-framework-churn/ |
Custom Elements are gaining traction as the best way to create interoperable components for web. Ionic, the popular mobile framework, is being rewritten with custom elements. Angular is making Custom Element a first class citizen.
By migrating to Custom Element, Marionette would align with current web development technology and opens the door to easier interoperability. The code would be simplified a lot since Custom Elements already implement features like attach/detach(connect/disconnect) lifecycle events.
Implementation could be done with class mixins like is skatejs and elix are doing.
SkateJS allows to customize the rendering with a similar concept as Marionette setRenderer, using mixins instead of a class method.
Marionette would provide withEvents, withUI, withBehaviors etc mixins with each functionality implemented and a withView mixin with all features. This would allow to customize the View class as needed.
The default one should be:
if only events is used the user could do
Is has some drawbacks like being a big breaking
Pros
Cons
The text was updated successfully, but these errors were encountered: