I am working on a JavaScript application with a server-side component. The aim is to have multiple different objects on a canvas that are synchronized (i.e., have the same appearance) between multiple browsers. The synchronization is done by the server-side component which broadcasts the individual changes to all browsers. Whenever an object changes, it has to notify the server about which will then take care of notifying the other browsers.
The objects on the canvas are represented by JavaScript objects whose attributes determine the appearance for the user. Of course, not all of the attributes are important for the appearance. Hence, only changes of important attributes have to be transmitted to the other browsers. There are different 'classes' of objects, but all objects 'inherit' from a common 'superclass' (I know, the class inheritance terminology doesn't really work in JavaScript, but in this case, I think it is easier that way).
Now, I have some trouble to send the client-server notifications in an elegant way. Currently, I have setter-methods for all the important attributes of the different objects. These setter-methods call a function which sends the notifications to the server.
I don't really like that solution, since it introduces much boilerplate code. Ideally, when I create a new object, I would like to be able to just specify the important attributes an be done with it. The logic that takes care of monitoring the changes of these attributes could be inside the 'superclass'. But I have no idea how to implement this. Maybe there is a way to build the setters dynamically at runtime? Or maybe there is some other way I did not think about?
If anyone can think of a solution to this problem, I would be glad to hear about it.
Thanks in advance!
Edit:
I had to revoke the accepted answer (creating getters and setters dynamically via Object.defineProperty) since though I thought it would solve my problem, it doesn't really. I now get notified when a property is changed via direct attribute assignment which fires the setter, e.g.:
SynchronizedObject.importantProp = 'someValue';
But as I noticed, in many cases the importantProp is some more complex object. And those objects are usually updated via the getter, not the setter.
SynchronizedObject.importantProp.x = 'someValue';
As far as I can see, I have no chance to notice changes done in this way with my dynamic getters/setters implementation. To use it, I have to change the way I am accessing my objects. Something that works:
prop = SynchronizedObject.importantProp;
prop.x = 'someValue';
SynchronizedObject.importantProp = prop;
That way, the setter is used and everything works out fine. But this feels really awkward and I don't want to have to think about the synchronization every time, I set a property. So it seems to me, the solution is not really usable.
Can anyone help?
How about one set function?
MyObj.prototype.set = function(key, value) {
this[key] = value;
// do other things
};
You could combine this with an EventEmitter implementation to make it easy to observe changes.
This is exactly what __defineSetter()__ is intended to support. Check out John Ressig's JavaScript Getters and Setters blog post for some good examples. It would be pretty simple to fire off an event from inside a setter.
You may want to consider the MeteorJS framework if wheel reinvention is not really your bag.
Related
Learning Ember.js and have a reasonable understanding of getters and setters (accessors) through Ruby and Java.
In Ember/Javascript, I seem to have a very serious lack of understanding. For instance in my controllers/models, I don't have a clue whether to use object.set(property,value) or refer them directly object.property = 'value'
As an example, in my earlier question (How to get Model values in Controller), part of working answer was to use object.name instead of object.get('name'). It worked but I miss the basic understanding.
Would appreciate some clarifications.
The rule is: You should always use .get()/.set() when you are in your .js-files. When you are in your templates (.hbs or other) you should not (you can't do it).
If you access a property via myObj.myProp it will work for regular properties, but computed ones won't. If you set a property via myObj.myProp you can still get the value back, but bindings and observers won't be notified that it has changed and won't updated properly.
This is a design decision by the Ember team which allows for efficient bindings/observers instead of doing dirty-checking of all bound/observed properties (which is what Angular currently does).
I made a small jsbin showing this. Three values are bound initially, the button then changes one and logs it into the console (so make sure to have the console open), the binding isn't updated but the value can be retrieved. It then tries to get a computed property via myObj.myProp which returns undefined and then the regular way.
http://emberjs.jsbin.com/cipapaxevi/2/
Also, as a side note. If you want a property thats on a child object to what you have you can access it via myObj.get('myProp.myOtherProp') instead of doing myObj.get('myProp').get('myOtherProp'). It saves you from the worry that myProp could return null or undefined.
That's the way Javascript works and it's one of its disadvantages. It doesn't have public/private proprieties nor methods. Therefore, we should rely on good comments in the code.
I am not an expert in Ember, but when I face the similar problems in JS I usually look at the source code of the library to see how it's constructed and then make a decision. That's why probably js libraries are usually shipped with both min and dev versions.
However, if the object has special methods to access its properties then they are there for a reason, so use them instead of the direct access to the properties. As I said above, in Js you can't make properties be private or protected.
There appear to be a number of different ways how to access properties of a Sencha (Touch) model. However, I don't seem to be able to find proper documentation of which is the "correct" way of doing it.
Model creation
var model = Ext.create('MyApp.model.MyModel', {
name: value,
foo: bar,
...
})
Property access
model.get('name') or model.set('name', newValue)
model.data.name or model.data.name = newValue
model.raw.name seems to always return a string no matter what the data type in the model definition is?
Let's sort this all out:
get and set methods are the intended accessors for model field values.
model.data is the object that stores the client side model value, that is that have been converted from the data received from the server proxy using the fields configuration (type, convert method, etc.).
model.raw is the raw data that was received from the server proxy, before it was converted to client side application domain model values. You should avoid using it, or you will tie yourself to your proxy/server.
model['name']: as you've said, it doesn't work. Don't hope for it to come back (I don't even really understand that it worked at one point).
Now, which one should you use? Clearly, the last two ones are already out of the match.
The model.data object should give you the expected result in most cases (see bellow), and should give you a marginal performance gain other calling a function.
However, IMO you should always prefer to use the getters and setters, for two reasons.
First, it might happen that someone in your team (or you from the past) decides that the getter/setter is a good point to add some custom logic. In this case, bypassing the accessor by using the data object directly will also bypass this logic, and yield unpredictable result.
Secondly, getters and setters make it really easier to debug some situations, by making it easy to know from where modifications of the model values are coming. I mean, if one day you were to ask yourself "f**k, why is my model field value changing to this??". If all the code uses the getters, you'll just have to put a breakpoint in there, and you'll catch the culprit hand in bag. On the other hand, if the incriminated code uses the data object directly, you'll be stuck to do a whole project search for... You can't tell exactly what... data.name =? data['name'] =? name:? etc.
Now that I think about it, there is yet another reason. A deprecated one apparently. But the data object name used to be customizable using this persistenceProperty option. So, in some cases, the data object won't even be available, and code doing model.data.name instead of model[model.persistenceProperty].name would crash, plain and simple.
Short answer: use the accessors.
I know enough jQuery/JavaScript to be dangerous. I have a JSON array that I'm interacting with using two different elements (a calendar and a table, to be precise). Is there an event handler (or any other way) I could bind to so that the table would refresh when the JSON changes?
Basic programming, parse the json (=string) into a javascript object or array. (you probably have already done that.) Use an implementation of the observer patern.
I suggest taking a good look at #Adam Merrifield 's interesting links.
Most of the time using getters and setter where you can fire a custom event (or call a callback method) inside a setter is the key in this.
KnockoutJS is a good framework to help you do such binding. It also uses the observable - observer/subscriber pattern.
using timers is not a really good idea.. little to much overhead. (doing stuff also when nothing gets changed. And you will always hop x ms behind (depending on the polling frequency).
You might want to consider Knockout.JS
It allows bi-directional mapping, so a change to your model should reflect on your view and vice/versa.
http://knockoutjs.com/documentation/json-data.html
However, it might be late stages of your dev cycle, but something to consider.
what is the advantage/ reason for backbone-js to use the syntax
//using a Model instance called model
model.get('attribute')
and not
model.attribute
Im just starting to use backbone and I always ind myself trying to access the attributes directly
If you look at the source code, the get function just calls to this.attributes[name].
http://backbonejs.org/docs/backbone.html#section-31
The benefit, though, is at least two-fold:
1) a consistent API that reduces the amount of code you are writing
2) the ability to override the get method and provide more complex access control
For example, there are several plugins for backbone that override how models work in order to provide nested model capabilities. it's very easy for them to allow you to write a get method like this:
model.get("submodel.attr")
and have that parse out the attr of the submodel sub-model. Without the get method, it would be more difficult to make this consistent with the API.
The underlying benefit from this is encapsulation, though. Until JavaScript provides true get/set properties that let us write code for getters and setters, we'll be stuck with work-around methods like Backbone's get and set.
Well .. for starters, model.attribute is absolutely NOT correct. model.set() is required in order to get change events to fire. You're very likely to forget this if you get in the habit of using model.attributes[attribute] instead of model.get(attribute)
I've recently started using Backbone.js. I like the architecture, in terms of features it's almost exactly what I need...
... However I found the following caveats:
For Collections get means something different than for Models. There is no set. Attributes should be accessed in a regular way. I find it rather inconsistent. It's easy to confuse models and collections sometimes. Is there anything that can be done to overcome this?
Assigning initial values inside Model.extend doesn't always work. For example assigning url will not override the default behaviour. This can only be achieved through a call to set() method. Again very error prone.
I still don't know whether it's required to use get/set inside initialize() call.
I don't understand why I can't just call _.bindAll(this) inside initialize() and I have to list specific function names to be bound like this: _.bindAll(this, firstFunc, secondFunc, ...). This is not very DRY.
I would like to know: what are the best practices regarding the mentioned situations? What do you do to make the framework more consistent - any monkey patching? Am I doing anything wrong / against the convention?
I'd be grateful for any good real world examples. I did find this: http://documentcloud.github.com/backbone/docs/todos.html and http://liquidmedia.ca/blog/2011/01/backbone-js-part-1/ and those don't address any of the mentioned problems. In fact they just present the simplest ideas and absolutely no border cases, so anything more complicated could be useful.
EDIT:
Ok, and there is one more fundamental think I don't understand:
Am I ever allowed to place additional attributes on extension like this: var SomeModel = Backbone.Model.extend({ myattribute: myvalue }) ?
If so, then why don't subsequent calls to new SomeModel().get("myattribute") work ?
What exactly is this inside initialize() ? Is it model class or model instance ?
EDIT(2):
Well, I found this: http://maccman.github.com/spine/. It looks like Backbone.js 2.0, shares a similar name too :). Haven't tested it yet, which might be a bit of a show stopper, as the library is very recent. However from the docs side of things it looks very promissing. It gets rid of most of the problems that I found, it simplifies the API, it even gets rid of the dependency on underscore.js which for a library is a good thing. I'll post my further findings here.
Ok, I think I can say it quite confidently now: Backbone is dead, long live Spine.
Spine isn't exactly a fork of Backbone. It is however very similar and clearly inspired by some of the design decisions. It could be said that the author tried to retain as much as it was possible the original backbone API, getting rid of everything unnecessary or illogical. I find it also easier to extend. The list of changes includes among other things:
Getting rid of the dreaded Collections. "class methods" are used instead,
Getting most out of js prototypical nature (i.e. no get/set is needed). Attributes are accessed directly. An explicit call to save() is required in order to trigger an event.
Views and Controllers are now merged into new type of Controllers together whose purpose is to respond to DOM events and bind to model events.
The name :)
I find those design decisions coherent and sensible.
The reason there is no 'set' for Collections is because Collections are not arrays, they are sets, which are potentially ordered. The only supported way to place an element at a particular position is to add it to the collection and then sort the collection.