-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
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
Add a preinitialize method to allow for true instance properties and ES6 classes #3827
Conversation
I'm not sure I like this any better than the property methods approach... |
@jridgewell hey Justin. I get it because I don't think its perfect/beautiful either. This way has a bit less boilerplate than methods for everything, and works for Backbone libs/extensions that rely on properties without using I'd love it if somebody came along and tossed out a better solution for a clean syntax with pure ES6 classes. If this PR is a start for something better then that's a win in my mind. But if there isn't something better, I think this is an improvement. |
Agreed. And this has the added disadvantage of not sharing objects across prototypes. An app with a lot of objects is going to balloon in memory very quickly. I've been declaring ES2016 classes broken until they fix prototypes and |
I think this is overstated How much additional memory overhead is this really going to add in a normal case, if this is for configuration props?. Again though, is there a better way?
I understand this mindset. But, I'm in favor of allowing devs to use the spec'd classes here for the sake of standardization. A common class standard allows better tooling (allow things like http://ternjs.net/ to better interpret Backbone constructs for instance) as well as lowering the learning curve when moving between Backbone/Angular 2/ React / etc. So while I'm very willing to accept that this isn't the best way of doing this (I hope somebody has something better), I don't like the idea of giving up on allowing ES6 classes as a usable option. |
It's not much boilerplate. And for things like
I'm wary about this argument. Yes they'll be bigger, but I don't think it'll cause huge problems.
I'm actually coming around to this. If we update the code to pass in I know there are issues requesting this, and that can be solved with this, but my issue searching skills are failing me. Something along the lines of changing the Also, it'd help since ~75% of my own code does So, add |
@@ -394,6 +394,7 @@ | |||
var Model = Backbone.Model = function(attributes, options) { | |||
var attrs = attributes || {}; | |||
options || (options = {}); | |||
_.extend(this, _.result(this, 'localProps')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't need result
for this, no? There's never a case localProps
would be an object over just sticking them on the prototype, yeah?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think forcing it to be a method is the right choice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thought was around the secondary use case (in my mind) of adding properties to the instance. I was thinking a user might want to do that with a few static properties (using the traditional Backbone class style). But I'm realizing now that the reference would be shared anyway and that would undermine the purpose. So I'm on board with that.
How about change the name from localProps to instanceProps ? |
@jridgewell just making sure I'm understanding you correctly, you want the options passed in to the constructor passed to the localProps/instanceProps function? @blikblum I'd be ok with instanceProps. It would also be an opportunity to clarify the current docs. Right now the model
It's referring to the fact that the first argument to extend is added as instance properties on the prototype, but thats super confusing I think to most people. Naming this instanceProps and clarifying that extends is mixing data onto the prototype and not an instance would likely be helpful. |
Yup.
Separate PR?
I think this is going in the wrong direction. Defining a more generic var Model = function(data, options) {
var attrs = attributes || {};
options || (options = {});
this.preinitialize.apply(this, arguments);
// ...
this.initialize.apply(this, arguments);
}; |
@jridgewell I'm on board with that. Generalizing it makes sense to me. Thanks for the wider context. Ok I'm going to edit the PR to rename this to preinitialize across the board, make it method only, and change it to applying with the constructor options as opposed to extending the prototype. So my example above will look like:
Which is more explicit for readers anyway. I like it. |
So, I don't quite understand how this is different than the following. class MyView extends Backbone.View {
constructor (attributes, options) {
_.extend(this, …)
super(attributes, options)
this.listenTo(…)
}
} I find |
You can't use class MyView extends Backbone.View {
constructor (attributes, options) {
_.extend(this, …) // throws error
super(attributes, options)
this.listenTo(…)
}
}
class MyView extends Backbone.View {
constructor (attributes, options) {
super(attributes, options)
_.extend(this, …) // too late, dom element has already been created and events bound.
this.listenTo(…)
}
}
I actually agree for the current case: |
Well that's…surprising. I don't understand why since one can already do In that case we're left with getters and setters I suppose? I haven't seen them mentioned yet in this thread or #3560. class MyView extends Backbone.View {
get tagName () { return '…' }
…
} I understand it's preferable to declare these things directly on the prototype (I would rather that as well) but I don't know that |
But that won't work for runtime options: class View extends Backbone.View {
constructor(options) {
super(options);
_.extend(this, _.pick(options, 'type'));
}
tagName() {
return this.type === 'emote' ? 'em' : 'span';
}
} Yes, you could pass in |
Thanks @jridgewell! (That's a long thread.) I guess I'll leave this alone then, as I don't have any better ideas. ¯_(ツ)_/¯ |
I was being cheeky, sorry.
I don't mean to force my way here, I just think it's a pretty simple solution and it'll work well with code I usually write. |
@jridgewell well I proposed an idea that I wasn't completely happy with in the hopes that it would produce a better idea, and I feel like that happened, so its all good from my POV |
@@ -1192,7 +1203,9 @@ | |||
// Creating a Backbone.View creates its initial element outside of the DOM, | |||
// if an existing element is not provided... | |||
var View = Backbone.View = function(options) { | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Random newline?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jridgewell oops. Fixed.
(replying to @jridgewell) My objection is that with this pull classes now have three places to put create-time logic: the |
@akre54 theres default constructor behavior, and hooks to run code before and after the default behavior. Or you can override the default behavior. Thats a reasonable and coherent set of options, and its a common pattern to provide a component that has life cycle functions/events, and hooks to add behavior before or after those events. Marionette Views allow you to override React has similar before and after hooks for the life cycle events of their components (componentWillUpdate/componentDidUpdate). Ember does similar stuff, components have I think people will be able to grasp the difference here if it is documented. |
With this, there's never a reason to override
I'm not happy with the ES6 class spec either, but I don't think that should stop us from adding support for it. And this solves issues like #3060 (comment). |
After thinking about it some more, I like preinitialize a lot as a way to avoid having to manage Can you add a couple tests @benmccormick |
instantiation logic is run. This allows greater flexibility for several use cases including - jashkenas#3620 - jashkenas#3685 - Easing use of ES6 classes with Backbone
@megawac Tests added, and passing. Sorry for the delay, I missed your original message somehow. |
@jridgewell @akre54 I'm very happy with this PR. Any more criticisms or is it good to merge? When merged can we try to get a 1.4 out asap? |
LGTM, let's merge at the end of the day if no one chimes in. |
Now that static property support has landed in Chrome, do we need this anymore? |
Yup. Static properties are set after the |
Right right. I'm referencing the comment above it. (By static properties I mean the static keyword, not property initializers. Those haven't, to my knowledge, landed in the spec yet). |
Ohh. I'm kinda partial to this solution over the |
@akre54 to be clear, in your example, the If that is right, I'm not sure what has changed here? You can use methods to define the various things here with or without the static keyword. |
Yes the class properties / stage-2 proposal. Check out the example @akre54 posted with just the ES2015 preset; things fail. Now click the presets section to the right of the evaluate checkbox and add stage-2. |
@typhonrt I don't think we're saying different things. If I understand correctly static methods are currently part of the spec, are supported in the latest version of every major environment and will be supported everywhere eventually. As far as I can tell (and it seems you agree) static properties are a proposal that may or may not eventually end up in the spec, and can currently be used through Babel. I understand how static properties potentially solve the problem of this PR. I don't understand how static methods do, and that was what I was trying to ask about Edit: I initially read that as you trying to disagree with me saying that static events wasn't part of the spec, looks like you were actually just trying to answer my first question by pointing to the proposal. Reading comprehension fail. Thanks! |
Indeed, though at stage-2 / draft it's quite likely to proceed onward and is a fairly popular feature. An article to peruse on ES6+ with React; see the "property initializers" / "arrow functions" sections. |
Hi, is there any ETA to get this PR via #4028 merged? This is more than one year old already and from what I read from related threads and posts, most people agreed on while not the perfect solution provides necessary support for current specifications. I would like to start using Backbone since it fits better than a full bloated framework like Angular or Vue.js but the fact that I can not have members (properties) initialized in my constructors, because of problems stated already, is hindering me severely. Could you please decide on adding it? Thanks in advance |
@davidmpaz I've resigned myself to this possibly never getting released. I don't think Backbone is getting significant attention from any of the maintainers these days (see recent issue activity). I'd recommend working off a fork if this is super important to you, or if you just need the data modeling, using a different data library like MobX. |
I thought on introduce it bit by bit to have time to check how is accepted, starting with Views using it also with TypeScript by the way. Just wanted to have goodies (bit more structure, events, jQuery scoped access to DOM) from Backbone.View. But having your comments in mind as well as issues activity I will probably quit, I can not risk on a fork for this. I really don't like one man orchestra shows :) and I also don't want to rely on decorators as long as they don't became standard, which is the alternative I like the most for this situation. I will discuss this with colleagues and see what we can do. I was strongly hoping to not end re-implementing wheels by my self ;) Backbone was looking like a perfect match for our needs. Thanks for the hints |
This is my attempt to allow for better support for ES6 classes through a new method, localProps.
localProps
allows you to define a function (or an object), that is evaluated in the constructor for Models/Collections/Views/Routers, with the result being mixed in to each local object.As a happy side effect this allows a declarative format for instance properties on an object (instead of using
this.foo = 'bar'
statements in the constructor).This allows us to write something like
instead of adding properties to the prototype separate from the class declaration or using non-standard features like decorators.
See #3560 for more background on why this is useful/necessary