Backbone models, JSON and HTML Forms - javascript

Backbone models .get() and .set() routines are fine if your values are simple attributes. I have inherited an app however where the values are also JSON objects.
i.e. model.get("key") returns
{start:
{top:5, bottom:6},
end:{...}
}
etc. Setting any of these values using .set() is a PITA because you have to pull out the old value, make the change you want, and punt it back in again. i.e. if I want bottom = 7, you can't just do .set("start.bottom",7). I need to do something like:
var tempValue = model.get("start");
tempValue.bottom = 7;
model.set("start",tempValue)
Models have an attributes property, where you can do model.attributes.start.bottom = 7. Now this presumably doesn't fire any event handlers, because looking at the Backbone code, that is done in set.
So two questions:
If I don't need the change handlers to fire, is there anything wrong
with setting attributes directly.
Is there anyway of firing the
change handlers manually after setting attributes?
The Backbone changelog says - "The Model#change method has been removed, as delayed attribute changes are no longer available" -but I'm not entirely sure why this would be. It sounds useful.
Related to this: I'm also trying to parse a HTML form into the model. I've been trying to use ModelBinder, but it can't handle nested JSON. e.g. <input type="text" name="start.top">
Edit:
I've just realised you can do model.set({}) with an object. e.g. model.set({start :{top:7}}); but it's a bit clunky

If I don't need the change handlers to fire, is there anything wrong with setting attributes directly.
I think that this may answer your question:
Backbone.js get and set nested object attribute
Is there anyway of firing the change handlers manually after setting attributes?
The set method (without silent option) triggers 2 events: "change" and "change:attributeName".
If you need it, you can trigger them manually by invoking:
model.trigger("change change:attributeName")

Related

purpose of data in jquery? (vs adding my own fields to Jquery objects)

This question is related to mine and explains what .data method is in Jquery.
Apart from the relation to HTML5 data-* element attributes (like <p class='foo_cl' data-bar='gee'>, for example) why would I code:
$('body').data("my_lab", {some:"object"}); // §1
instead of
$('body').my_lab = {some:"object"}; // §2
(I am mostly interested in the case where the Jquery selector gives one object, like for $('body') above)
The later (§2) seems more readable and shorter, and I guess would be more efficient, than the former (§1). Of course data is a Jquery selector (but I could use each to set the .my_lab field, etc...)
And I might even consider changing the DOM element with the ugly looking
$('body')[0].my_lab = {some:"object"}; // §3 is ugly
(which is perhaps undefined behavior, see this)
Of course, there is the potential issue of colliding the field name my_lab with some existing field in JQuery implementation; but I assume that using some common suffix (like my_) in field names should be enough.
FWIW, I am only interested in recent Jquery (e.g. Jquery 2.1.4) on recent Firefox (e.g. 38 or 42) on Linux.
In other words, why would adding my own fields in JQuery objects be frowned upon?
By doing
$('body').my_lab = {some:"object"};
You are setting value to a specified jQuery wrapper. You would not be able to reaccess the data with another selector :
$('body').my_lab = {some:"object"};
console.log($('body').my_lab); // will log undefined
This is why using data is basically more reliable
$('body').data('my_lab', {some:"object"});
console.log($('body').data("my_lab")); // will log {some: "object"}
For the 3rd option : $("body")[0].attr = { my : "object" } part :
The jQuery data method prevents potential memory leaks when manipulating the dom / removing elements from the page (by removing the binded data and stuff) and avoid attribute conflicts (like setting domElement existing attributes and other subtle things)
So basically, if you have jQuery in you app, you don't really have any good reason to reinvent the wheel by doing manual binding between dom element and javascript data.

About saving DOM elements in object properties

I want to make an UI object that saves references to DOM objects and also some UI strings, so I can use it all over my code and can handle name changes easily. Kind of like this:
var UI = {
DOMelem0: document.getElementById('imUnique'),
DOMelem1: document.getElementById('imSpecial')
};
However, I think that everytime I would access DOMelem0 (by calling UI.DOMelem0), for instance, I'd be calling the getElementById() function, is that what happens?
Or is it no different than storing the elem in a scoped variable? (Like so: var elem = document.getElementById('cow');)
I'm worried about any performance issues this might cause if I were to have lots of UI elements, although I guess they'd be minimal. Either way, I wouldn't want to be calling the DOM method all the time.
Thanks.
Calling UI.DOMelem0; will not call document.getElementById('imUnique').
document.getElementById('imUnique') is only called when you first create the UI object.

What's the reason for storing data with .data() instead of using plain variables?

Let's say I have to remember the initial height of certain element.
A common practice I see is to save this value with $.data on that element.
I fail to understand the benefits of this. Why not simply keep a simple variable with that value, or an array with values if there are multiple elements? Keeps the code easy to understand.
The main reason for using data() is to store data specific to a certain element so it can be accessed later, here's an example
$('.elements').on('click', function() {
$(this).data('value', this.value);
});
$('.elements').on('keyup', function() {
$(this).val( $(this).data('value') );
});
Note that the event handler could match a number of different elements, and using data() keeps the data associated to each element without using several variables for each element or a complex array.
EXAMPLE
It allows the function to be reused to apply the same effect to other elements (without having to deal with closures).
It allows the value to be initialized with HTML.
It makes it easier for developer tools to inspect the data.
Basically because le you save information INSIDE A NODE, preventing possible variable name conflicts and without the need to pass variables around. All the needed informations about a node, stay with the node itself

Unset all attributes backbone at once

I got a question about Backbone, how is it possible to set all attributes of a model to empty?
unsetmodel.unset(attribute, [options])
Remove an attribute by deleting it from the internal attributes hash. Fires a "change" event unless silent is passed as an option.
But this is only meant for unsetting individual properties one by one.
Anyone an idea?
Gretz,
From Backbone site:
clearmodel.clear([options])
Removes all attributes from the model, including the id attribute.
Fires a "change" event unless silent is passed as an option.
So I would do something like:
myModel.clear();
If you want to keep the attributes, why not iterate through all of them and set them manually?
$.each(this.model.attributes, function(index, value){
// set them manually to undefined
});
I know this is an old post, but I recently came across a similar issue - mainly, that if you do unset one-by-one, you get multiple change events, with the model in an intermediate state for each one. To allow this to happen with the appropriate change events fired afterwards, you would have to unset them silently one-by-one, then manually fire change events for each one after the unsets. However, if you look at the Backbone code, you'll see that the unset method is really just a call to set, with {unset:true} in the options. So you should be able to do this instead:
model.set({ attr1: undefined, attr2: undefined, attr3: undefined }, { unset: true })
I haven't tried it in practice, but it should definitely work in theory. You would get a series of change events for each attribute, after all of the unsets have completed. This approach is going a little outside the recommended path, since it uses unexposed logic from the Backbone source, but since this particular code hasn't changed in a few years (and actually appeared to be supported as a set option before that), it should be safe to use and continue using.
There isn't a built-in method to set all properties undefined, while keeping the attributes keys. Good news is that you can easily build one yourself with a underscore one-liner:
Backbone.Model.prototype.clearValues = function(options) {
this.set(_.object(_.keys(this.attributes), []), options);
}
All models will then have a clearValues method:
var model = new Model({
id:1,
foo:'foo',
bar:'bar'
});
model.clearValues();
console.log(model.toJSON()); //-> {id: undefined, foo: undefined, bar: undefined}

Save and restore "onclick" action on jQuery objects

I want to disable a whole bunch of objects on the page, and then re-enable them later. Since some of them are tags rather than buttons, I disable them by removing their onclick attr. I've tried to store the old handler in a .data(), but unfortunately when I attempt to restore them with $(obj).attr('onclick',$(obj).data('onclick')), it calls the function rather than restoring it to the attribute. And if I try to store it in a different attribute instead of a data, it doesn't store the function, it stores the return value for the function.
Is there any way to accomplish this without re-writing every tag and every onclick handler on my page?
if( doEnable) {
$(obj).attr('href', $(obj).data('href'));
$(obj).attr('onclick', $(obj).data('onclick'));
$(obj).removeClass(EIS.config.classes.disabled);
$(obj).show();
}
else {
// Save the things you're going to remove
$(obj).data('onclick', $(obj).attr('onclick'));
$(obj).data('href', $(obj).attr('href'));
$(obj).prop("href", null);
$(obj).prop("onclick", null);
$(obj).addClass(EIS.config.classes.disabled);
$(obj).show();
}
By the way, this code seems to work fine in Chrome and Firefox, but only sometimes in IE8 and never in IE6. Unfortunately the client tests first in IE6.
$(obj).attr('onclick', ...
is ambiguous, has results that differ in different versions of jQuery and different browsers. It probably doesn't do what you want. You should avoid using attr on event handlers.
The problem is the disconnect between the onclick attribute and the onclick property. jQuery has tried to brush the difference between an attribute and a property under the carpet in the past, using attr to access both, but they're quite different. This was changed in jQuery 1.6, and partially reverted in 1.6.1, to widespread controversy, confusion and incompatibility.
For many properties, the values of an attribute and the corresponding DOM property are the same; for others, including all properties that aren't strings, they aren't. Event handlers certainly aren't: the property is a Function object, whereas the string attribute might be (a) the original string of the onclick="..." attribute in the HTML, (b) nothing (if the onclick was assigned from script to be a Function object) or (c) unavailable (in older IE).
To access the event handler Function property, use prop() in jQuery 1.6:
$(obj).data('onclick', $(obj).prop('onclick'));
...
$(obj).prop('onclick', $(obj).data('onclick'));
or just use plain old JavaScript which is actually simpler and more readable; jQuery wins you nothing here.
obj._onclick= obj.onclick;
...
obj.onclick= obj._onclick;
Either way this is not going to reliably ‘disable’ elements since they can (and very likely will, if you're using jQuery) have other event listeners registered on them, using addEventListener/attachEvent rather than the old-school event handler interfaces.
It looks like saving a function via .data() works just fine:
var f1 = function() { console.log('invoked'); };
$('a').data('func', f1)
var f2 = $('a').data('func'); // 'invoked' is not printed
f1 === f2 // true
so how are you storing the function via .data? if you're doing something like
a = $('a');
a.data('onclick', a.click()); // click handler is invoked here
then you're actually invoking the click handler(s) prematurely, and storing the return value with .data().
--edit--
it appears that .attr(function) invokes the passed function. This is a feature of jQuery. I'd suggest using jQuery's .click() method to attach the function as a click handler.
a = $('a');
a.each(function() {
this.data('onclick', handler_fn);
this.bind('click', handler_fn);
});
// later
a.each(function() {
this.unbind('click');
});
// even later
a.each(function() {
this.bind('click', this.data('onclick'));
});
What about binding the event in jQuery instead of setting the onclick attribute?
$(obj).click($(obj).data('onclick'));
Can we see the code that you use to set the data attribute?

Categories