Backbone.js getting callback of render, within the view - javascript

I have a Backbone app and we have stuff like this:
render: function() {
this.$el.html(_template());
$('#id').plugin();
return this;
}
The #id is from an element that's being rendered. This only works sometimes, as it can take longer for it to actually insert into dom.
Is there a way within this view, to define a callback or somehow know for sure that the dom has been updated, before calling our plugin() function?
Thank you!

Your problem is that this:
$('#id').plugin();
is looking for #id inside the DOM but this.$el isn't in the DOM yet; so, if #id is inside this.$el, then #id isn't in the DOM either and $('#id') will be empty. You want to look for #id inside this.$el and Backbone provides the this.$ shortcut for that:
$ (jQuery or Zepto) view.$(selector)
If jQuery or Zepto is included on the page, each view has a $ function that runs queries scoped within the view's element. [...] It's equivalent to running: view.$el.find(selector).
So you want to do this:
this.$('#id').plugin();
If your plugin needs its element to be rendered (perhaps it needs size and position information), then you'll have to kludge around a bit:
The caller can call a method on your view after it has added it to the DOM.
You can use setTimeout(..., 0) or _.defer to bind the plugin after the browser has updated the DOM. This only works if everyone is using the common x.append(v.render().el) pattern though.

Symply trigger an event:
MyView = Backbone.View.extend({
initialize: function(){
this.on('view:rendered', this.initPlugin, this);
},
render: function(){
this.$el.html(_template());
this.trigger('view:rendered', this/*or some useful data*/)
},
initPlugin: function(){
this.$('#id').plugin()//watch on this.$ to apply selector inside this view only
}

I am not sure but give a try
bind it to the view as event , fire it after the view being rendered
var viewobject = {};
_.extend(viewobject, Backbone.Events);
viewobject.on("alert", function(msg) {
$('#id').plugin();
});
object.trigger("alert", "an event");

Related

In a Durandal viewmodel, the view isn't always attached when viewAttached() called

I have a ViewModel in Durandal that has a function viewAttached().
According to the docs this is supposed to be called after the view is attached to the DOM.
In my function I have a jQuery selector for an element in the View that is being attached:
function viewAttached() {
console.log( $("#myViewId").length );
}
Most of the time I correctly get "1" printed to the console, but maybe 10% of the time I get 0. The view is not yet actually attached to the DOM.
Has anyone else had this problem?
viewAttached gets the produced DOM sub tree passed in as argument.
So you could either change the code to something along the line
function viewAttached(view) {
console.log( $(view).length );
}
if you want to wrap the whole view with jQuery or to
function viewAttached(view) {
console.log( $(view).find('mySubSelector').length );
}
if you need to work with some specific sub tree elements.
There's a couple of things to note:
viewAttached fires after the current module's view is attached to it's direct parent, which is NOT necessarily when it is attached to the DOM. This is an obvious limitation in some cases. For Durandal 2.0 we are adding an additional callback called documentAttached which fires when everything is attached to the page's dom.
viewAttached is passed the view for the current module. You should use this to provde a context to any jQuery code that you write.

Why would .on() work but .listenTo() fail?

I am working on a backbone project and inside of the initialize function of a view, I am trying to set a listener on a collection, like so:
this.listenTo(this.collection, "change:attr", this.render)
However this is not catching the event. What is extremely confusing is that this actually works in its place:
this.collection.on("change:attr", this.render);
Does anyone know why this might happen? If I have to, I'll just use on but I'd much rather take advantage of the listenTo method.
Thanks!
UPDATE:
Please see my answer below for additional information about the circumstances of this problem...
Ok, I figured out what the problem is (sort of) and am not sure if this question should be closed... but figured I would share this in case anyone has the same problem and it doesn't get closed.
In a subview of the original view, I had an event hash that looks like the following:
// ...in subview definition
events: {
'keyup input': 'changeAttr'
},
changeAttr: function(){
var value = this.$('input').val();
this.model.set('attr', value); // this.model is in the collection
}
With this setup, this was working:
// inside parent_view:
initialize: function() {
this.collection.on('change:attr', function(){ alert("changed") });
}
...But this was failing:
initialize: function() {
this.listenTo('change:attr', function() { alert("changed") });
}
What I failed to mention was that I was simulating the 'keyup' event on the subview using jQuery, eg. $('input').trigger('keyup') (this was for automated testing purposes). For some reason that I'm not quite clear of, Backbone does not like this. What's more strange still is that the attribute would actually change! Hence the on method above working... Perhaps someone out there can explain to me why this is the case (perhaps some attribute of the keyup event is missing and listenTo doesn't like that?)

How can i manipulate dom in controller in emberjs?

Here is a scenario:
I load data when controller is init. And when loading is finished. I want to resize the container element's size according the load data. So here is the problem, how can i access the view in a controller?
I know i can manipulate dom in view by this.$() but how can i access dom in controller or how can i access view in controller. I use Ember.Router here. So i dont create view and controller manually.
http://jsbin.com/oxudor/edit#javascript,html I show some code sample here. The code can not be executed, but it can show my problem. I did some comments on the code where have the problem.
I think you should not play with dom in the controller. That breaks MVC. Perhaps one solution could be to define an observer in the view, listening to the content of the controller. In this observer, then play with the dom (you're in the view, so no problem).
As #pangratz suggests, with some code, perhaps I can give you a more complete answer.
EDIT: I think you could put the carousel function in the related view, and observes the controller.loading property.
Here you could retrieve the jquery object of the view by using this.$().
carousel: function() {
var context = this.get('controller'),
self = this;
if(context.get('loading') === false) {
setTimeout(function() {
var width = self.$('#page_wrapper').width(),
num = self.$('.page').size();
self.$('#pages').width(width * num);
self.$('.page').width(width);
console.log([width, num]);
console.log(context.get('elements'));
}, 100);
}
}.observes('controller.loading')
Hope this help...
Now you can use Modifiers, Install Ember-midifier library and see this tutorial Ember-Modifiers

Issue in caching using coffeescript

I've this script
class Raffler.Views.EntriesIndex extends Backbone.View
div: $('#input')
initialize: ->
console.log #div.val()
As you can see this is a backbone's view.
I would like to cache $('#div') into a variable and call it. See the console.log #div.val().
But this seems not working..
Using normal javascript I'd write something like this:
var ToDoView = Backbone.View.extend({
div : $('#input'),
initialize: function(){
console.log(this.div.val());
}
})
And this is working fine. Where I'm going wrong with coffeescript?
There are some differences in how Coffeescript classes work and Backbone's extend mechanism work, which could be the issue you are running into. But I'm guessing that is not the issue here. There could be differences in exactly when and where you are running this code as well. If you put the javascript code in the same place where you are executing your coffeescript code, does it then work ok? And on a related note, what exacly is the problem, i.e. what error messages are you getting? Is #div initialized at all?
It's likely the $("#input") element hasn't been loaded at the moment your code runs.
The problem with caching the value in the class is that the class is most likely being defined outside the jQuery.ready callback (before the DOM has finished loading) so at the moment your class sets $("#input") as #div jQuery doesn't actually find that element.
You could set #div in the initialize function since that will most likely be called after the DOM has loaded.

Extending widgets in Jquery UI with redefining parent methods

I try to extend UI dialog according to documentation (UI version 1.8.16):
(function($) {
$.widget('ui.mydialog', $.extend(true, $.ui.dialog.prototype, {
_create: function() {
return $.Widget.prototype._create.apply(this, arguments);
}
}));
})(jQuery);
$(function() {
$('div#dialog').mydialog();
});
Executing of this code causes JS error: "this.uiDialog is undefined".
And if try to override the _init() method there are no errors, but parent method call takes no effect.
I'm confused.. Which way is legal to extending for e.g. put some custom initialize code?
I think this post would solve your question: Inherit from jQuery UI dialog and call overridden method.
In short, if you want to build a widget inheriting jQuery UI Dialog, you can do this:
(function($) {
$.widget("ui.mydialog", $.ui.dialog, {
_create: function() {
$.ui.dialog.prototype._create.call(this);
}
});
})(jQuery);
See this in action: http://jsfiddle.net/william/RELxP/.
This tutorial will enlighten you: http://wiki.jqueryui.com/w/page/12138135/Widget%20factory. In short, $.Widget is the base widget object. Even though it has a _create function, it by default does nothing, leaving the initialisation code to the subclass. Take a look at this updated example: http://jsfiddle.net/william/RELxP/1.
From jQuery 1.9 and on, if you want to add functionality to a widget and don't want to replace the existing function, after you do your code call the parent method. To do this, instead of what William Niu suggests, you can simply do this:
_create: function()
{
// Custom code here
// Call the _create method of the widget
this._super();
}
This applies to all existing methods. (eg _setOption, _trigger etc)
I posted a simple example of extending a jQueryUI Dialog using the Widget factory.
http://jsfiddle.net/Artistan/jWUGZ/
This example extends a dialog to create a simple loading modal.

Categories