-
Notifications
You must be signed in to change notification settings - Fork 75
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
Instances of a child get ignored on construction #31
Comments
It's worth mentioning that in the example above the event bubbling between the parent and child is broken once you assign the model instance after constructing. Children are set directly in for (child in this._children) {
this[child] = new this._children[child]({}, {parent: this});
this.listenTo(this[child], 'all', this._getEventBubblingHandler(child));
} We should probably set a getter / setter so that when a new model gets assigned we stop listening to the old model and start listening to the new one. |
I created a quick and dirty mixin that uses the a prop of the type While being quite familiar with Backbone source I'm pretty new to Ampersand and although it's similar I'd like some feedback on whether this would be the right approach before spending time porting it back to ampersand-state and making a pull request :) |
Hey, @JaapRood. Sorry for the slow response here. So, maybe you already discovered this, but we already have a property type of So, if you change your var Person = AmpersandState.extend({
props: {
foo: 'string',
smile: 'state'
}
}); I believe the problem goes away. So, therein lies the subtle difference between The Also, "empty" children always get instantiated when the parent model does so they can be expected to always exist. This sounds like another case where we need to improve the documentation. |
Here are the relevant tests for And for |
I filed a bug to write a guide addressing this difference: AmpersandJS/ampersandjs.com#78 Please re-open this if there's still an issue. Thanks for your very detailed bug filing, seriously. Much appreciated. |
That's exactly what I figured out, but what I don't quite understand is why make the distinction between the two at all? In a project I'm using the following mixin (also adds support for polymorphism, but the main gist is still there): var State = require('ampersand-state'),
_ = require('lodash');
module.exports = function(childModels) {
if (!childModels) childModels = {};
var props = {};
_.each(childModels, function(def, name) {
props[name] = 'state';
});
return {
props: props,
set: function(key, value, options) {
// Replicating a bit of the source so we can support all set
var attrs;
// Handle both `"key", value` and `{key: value}` -style arguments.
if (_.isObject(key) || key === null) {
attrs = key;
options = value;
} else {
attrs = {};
attrs[key] = value;
}
options = options || {};
var model = this;
// if a child isn't set yet and only an object is given, create an instance
_.each(childModels, function(def, name) {
// normalise the child definition
var childConstructor;
if (_.isFunction(def)) {
childConstructor = def;
} else {
childConstructor = def.getConstructor.call(model);
}
if (!this[name] && attrs[name] && !(attrs[name] instanceof childConstructor)) {
attrs[name] = new childConstructor(attrs[name], options);
}
});
// defer rest to the original implementation
return State.prototype.set.call(this, attrs, options);
}
};
}; I use that like so: var childModels = require('child-models-mixin');
var Smile = AmpersandState.extend({
props: {
width: 'number'
}
});
var Person = AmpersandState.extend(childModels({
smile: Smile
}),{
props: {
foo: 'string'
}
}); It really is a hack and I don't suggest copying that straight into source of course, but it seems to merge the two concepts together: it's children as props with type 'state' that also get inflated if a hash is given instead of an instance of the child. Also: having a child as a prop with type 'state' allows you to change the model for another one, while defining it using a 'children' definition, as soon as you swap out the instance (which you should be able to do, it being just a prop) it breaks. It doesn't cover that there is always an instance available, the prop could be null, but if there is some way we can get that to work I think merging the two is by far preferable. Personally I get around by setting a default value for the child prop (by using 'state' type) if I need guarantee of existence and it seems to work out just fine (although let me know if you have a reason for concern there). Add to that the confusion having two of these different but closely related concepts around, I think this is worth considering. |
@HenrikJoreteg Github won't let me re-open this issue, so unless you want me to repost the issue you'll have to do it 😄 |
So, @latentflip and I struggled back and forth on this issue for a while. I'm trying to recall exactly why we went this route. I wonder if @latentflip can recall. |
I agree that the distinction is confusing. Perhaps we can eliminate the declarative children. I think part of the reason we did it this way was because we didn't used to support having |
I think the main difference was whether the child model would be initialised with data parsed out of the json/serialised in outgoing json or not. It seems like we already have semantics for that with props & session though. So +1 on revisiting removing children & collections. Philip Roberts
|
I get that having 'children' and 'collections' props do clean up the definition of the props. You can keep them around simply as a small abstraction on top of the props and save having to define everything manually, sort of like the mixin I posted above. Don't have too great insights on how feasible that actually is though |
Yeah, I do like the semantics of having them listed separately, but like you said, we could still support that syntax. |
This seems to be related to my issue: #43 Also, is this 'state' also supposed to be used with collections? |
I think most of this issue can be considered addressed with the documentation changes in PR above. It's possible we need to make further changes to how these cases are handled. But I think the explanation is at least a good start. Re-open if this fails to address these concerns. |
Consider the following (edit / run on requirebin here)
Maybe this has done by design, but I can't help but think it's confusing and not the behaviour we're looking for. Ideally there should only be one instance of every resource representation across the app, otherwise keeping everything in sync would become a pain.
The text was updated successfully, but these errors were encountered: