-
Notifications
You must be signed in to change notification settings - Fork 38
Description
After playing around with the subviews property of ampersand-view I feel like it’s making it harder to work with a given subview than it has to be.
If I want to listen to an event from a subview for instance, it is not really clear where I would have to put my listenTo code, because the subview is initialized after some change event from the parent view.
My ideal scenario would look like this:
var AmpersandView = require("ampersand-view");
var CollectionView = require("ampersand-collection-view");
var ViewSwitcher = require("ampersand-view-switcher");
module.exports = AmpersandView.extend({
template: "<div><div></div><ul data-hook=\"collection-container\"></ul></div>",
subviews: {
stuffView: {
container: "[data-hook=collection-container]",
constructor: CollectionView
},
tabSwitcher: {
container: "[data-hook=switcher]",
constructor: ViewSwitcher
}
},
initialize: function () {
AmpersandView.prototype.initialize.apply(this, arguments);
// Because subviews are initialized with the view it’s save to
// rely on them everywhere in the containing view.
this.listenTo(this.stuffView, "some:event", this._handleSomeEvent);
this.listenTo(this.tabSwitcher, "show", this._handleTabShow);
// Alter subview properties or call subview methods without checking
// if the subview is already initialized and having to handle it async.
this.stuffView.collection = this.model.stuffCollection;
},
rerender: function () {
// Maybe establish a `rerender` which only renders all the subviews but
// leaves this views dom setup untouched ...
this._renderSubviews();
return this;
},
_handleSomeEvent: function (target, value) {
// handle `some:event` in here
},
_handleTabShow: function (target, value) {
// handle the `show` event in here
}
});So basically I’m proposing to align the subview’s life-cycle with that of the parent view:
- initialize calls
_initializeSubviews()which initializes the configured subviews,registerSubview()them and assignes them to a property on this view. - render calls
_renderSubviews()which callsrenderSubview()for all initialized subviews. This could also be used for rerendering subviews. - remove calls
remove()on all registered subviews.
Now if you want to do some work on a view before or after subviews are rendered you can override render and call View.prototype.render from there (In some cases this could address the need for events demanded in #70).
I think all of this could be implemented in a backwards compatible manner. So if this doesn’t suit your need you could call registerSubview() or renderSubview() like before.
One possible use case would be mitigating problems with binding server rendered views. Currently, if you want to attach a view instance to an existing element, subviews are a nightmare because they immediately render after a view is initialized.
It would be cool if we could establish a bind() method or something:
// bindable-view.js
var AmpersandView = require("ampersand-view");
module.exports = AmpersandView.extend({
bind: function (el) {
// Attach view to given el and bind configured subviews
this.el = el;
this._bindSubviews();
return this;
},
bindSubview: function (view, el) {
// Query this view for el selector
el = (typeof el === "string") ? this.query(el) : el;
// Bind subview to found el
view.bind(el);
return view;
},
_bindSubviews: function () {
if (!this.subviews) {
return;
}
each(this.subviews, function (config, name) {
var subview = this[name];
this.bindSubview(subview, config.selector);
}, this);
}
});And then we could use it to take over an existing DOM element:
<html>
<body>
<div id="my-view">
<h1 data-hook="title"></h1>
<p>This is some static text</p>
</div>
</body>
</html>var BindableView = require("./bindable-view");
var PageView = BindableView.extend({
bindings: {
"model.title": {
type: "text",
hook: "title"
}
}
});
var myModel = new Model({title: "Hello"});
var myView = new PageView({model: myModel});
// Attach myView to `#my-view` DOM element
myView.bind(document.querySelector("#my-view"));
// Altering the model now triggers DOM change as if this view was rendered
// through javascript.
myModel.title = "Hi";
// Rendering the whole thing would also work as expected
myView.render();So to sum things up: I think aligning the life-cycle phase of subviews with their parent views will give us a lot of flexibility.
So what’s everybody’s thought on this?