I have an alphabet filter view (a, b, c, d etc) that a lot of views will be using. I have setup a method on the main view to fetch results from the API using the letter clicked.
I've setup this by passing a callback function down to the alphabet filter as per below:
view = new App.Views.Common.AlphabetFiltersIndexView(filterCallback: #paginationFilter)
#$(".pagination-vertical").replaceWith(view.render().el)
Calling the filterCallback and passing arguments works, however the paginationFilter method called now belongs to the Alphabet Filters.
Question: How do I call a parent view's method and keep the method's relationship with the original top view?
You could go about this two ways:
Pass the parent view to your child view, OR have your child view find its parent via a global object, if you're using one. Call the parent view's function from the child
You could have your child view raise an event when you need the parent view's function to be called. Your parent view should be listening for this event from your child view, and respond by calling the method required.
You should trigger an event in the child view using something like
this.trigger('event', [args]);
and listen to it from the parent view
this.listenTo(childView, 'event', this.paginationFilter);
(Recommend giving the event a proper name. Consider a name such as 'page:change'. 'page' is a sort of namespace just to make the events in large application easier to name and organize. There is no real namespace functionality.)
You can use the following Underscore call in your AlphabetFiltersIndexView in it'sinitialize method
_.bindAll(callBackContext, "YourCallBack");
This will ensure the callback is bound to the object of your choice (The parent view).
You can pass in the values of callBackContext and "YourCallBack" as parameters to the AlphabetFiltersIndexView, which you can then control based on the context of the parent view.
Related
I am facing an issue with my controllers refs and the returned object being invalid. And by invalid it returning me an object that says it has not been rendered even though i am in the process of using it.
In my controller I have a ref defined as the following.
refs : [ {
ref : 'selectDeltaView',
selector : '[itemId=selectDeltaView]'
}]
and later in my controller I access this view using the created accessor
this.getSelectDeltaView()
What is strange is that the first time this view is presented, inside of an Ext.Window it works just fine. However, on the second launch the view I get back is not rendered.
this.getSeletDeltaView().rendered === false
Reading the documentation over at Sencha it seems that this simply passes my selector to the Ext.ComponentQuery.query function. However, if I call Ext.ComponentQuery.query('[itemId=selectDeltaView]') I get an array with a single element of which rendered is true.
Am i missing something? Why is my controllers reference returning me invalid data.
Update
Some additional details not mentioned in the original post. My initial assumption was that my view was not being destroyed. However, I have log statements in my ondestory and beforedestory events and can confirm that on closing the window my view is being destroyed.
What is the most confusing is that if refs simply use Ext.ComponentQuery.query, why does Ext.ComponentQuery.query only return one view when the window is re-opened and it returns the correct view. I understand from the question below that refs will catch the view, but that view no longer exist.. or shouldn't.. If the view is destroyed, can I force the controller to clear its catch?
The controllers reference is cached after the first evaluation.
What happens is this:
You open your view a first time.
getSeletDeltaView() finds that view, and stores a reference to it in the reference lookup cache.
You close the view, but you do not destroy it completely.
You open the view a second time, but it is a different instance.
getSeletDeltaView() finds the first view, that is no longer rendered, but still exists, because it was not destroyed. You cannot use this reference, because it point to the wrong view.
Solution:
Make sure to destroy() your view, when you hide it. You can also use autoDestroy as a property. Usually, autoDestroy is true by default.
In the initialize function of my backbone View, I created the following listener:
this.listenTo(this.model.get('clusters'), 'add remove', this.saveChanges);
This successfully causes my saveChanges function to be called the first time a model is added/removed from the 'clusters' Collection. There's only one line in that saveChanges function:
this.model.save();
Once that is invoked, adding/removing clusters no longer invokes the "add" or "remove" event. Why would saving the model destroy the listener? Can that be prevented, or is there a way to re-establish the listener?
Or is there something fundamental I'm not understanding about Models and/or Collections...?
Let's break down your code:
You write
this.listenTo(this.model.get('clusters'), 'add remove', this.saveChanges);
Which is equal to
var clusters = this.model.get('clusters');
this.listenTo(clusters, 'add remove', this.saveChanges);
Now I only assume that after you get that event you set() a new clusters object inside your model.
The issue here is that your view still listens to events from that same old clusters object, which is not relevant anymore - your model deals with another object!
Another case could be that Backbone clears your view's event handlers from its model when it's being removed.. Could easily answer for sure if you'd share the whole code.
I'd just like to understand the decisions behind Backbone.Marionette's view regarding UI elements.
When instantiating a Marionette.View on an existing DOM element, like this:
view = new Marionette.ItemView({
el: "#element",
ui : {
whatever : "#whatever"
}
});
I am able to access view.$el, the jquery selector inside view.initialize, so far so good.
However, when I try to access view.ui.whatever, I only have access to the selector, ie the string "#whatever" instead of the actual $("#whatever") jquery selector.
The reason for this is because Marionette.View.bindUIElements() is only called on render and not before initialize.
I would like to know if you think this behaviour is logic and why?
I am only asking in the case of attaching of the view to an existing el, if the view is created with a template, I do understand why the binding is in render().
Attaching a view to an existing element is the exception. The normal view lifecycle involves calling render, and without doing that there would be nothing for the UI elements to bind to.
Just call this.bindUIElements() in your initialize method when you need to attach a view to an existing element.
When I am working with Marionette, I put the code that has to access the ui elements inside the onShow method. This event is fired after the dom is ready and the elements are ready to be manipulated. Inside this method, your ui.whatever will now be pointing to an element and not a string.
I think you have that problem because you have to access to the jQuery element with
this.ui.whatever
Because "this" is already a view instance.
See: http://marionettejs.com/docs/v2.4.4/marionette.itemview.html#organizing-ui-elements
My backbone.js model has an array property. I bound the change event to save().
After sync() (triggered by save(), my app server returns an identical JSON, but backbone thinks the array has been changed (due to a different reference to the array I guess?), and trigger changes again. Then an infinite loop occurs.
save() -> sync() -> triggered `change` -> save()...
What shall I do?
Idea: I can bind the change event to a function that checks if the changed attributes are of type object/array, and do a deep comparison and call save only if the array/object really changed. If true then save()?
Thanks!
Try the Edge version of Backbone (master branch) this behavior changed after 0.9.9 - see https://github.com/documentcloud/backbone/pull/2004
Backbone has a special option on many methods to prevent just this sort of issue: silent:true. If you pass that option to your save method, the resulting sync won't trigger a change event.
So, if you want to set your change event handler to save silently, something like:
changeHandler: function() {
this.save({silent:true});
}
should do the trick.
I took a small code from backbone home site, and consoled the function, in this example, sidebar.on('change:color'), takes the function. but it requires two parameter, one is abiously we need that is 'color', and we defining the element inside the function, still why we giving another parameter as 'model' here, what that parameter does?
if i remove that parameter send only the color, the function doesn't work at all... any one help me to understand this?
sample function here:
var Sidebar = Backbone.Model.extend({
promptColor : function(){
var cssColor = prompt('Please enter a css color');
this.set({color:cssColor});
}
});
window.sidebar = new Sidebar;
sidebar.on('change:color',function(model,color){ // what model parameter do here?
console.log(model);
$('#sidebar').css({
background:color
})
})
sidebar.set({color:'green'});
sidebar.promptColor();
when i console the model i got this:
d
_callbacks: Object
_changing: false
_escapedAttributes: Object
_pending: Object
_previousAttributes: Object
_silent: Object
attributes: Object
changed: Object
cid: "c1"
__proto__: x
It is possible that you want to know which model was affected.
Consider a case where you are listening to an event on a collection instead. Which model's color value was modified? The model parameter tells you this.
Also, consider a case where the same handler is listening to "change:color" on multiple models. Again, you might want to know which model sent the event.
Just like in other event-driven environments, the "sender" is always passed along with the event data. model, in this case, is the sender.
Backbone is a Javascript MVC framework. (Unlike standard MVC, Backbone doesn't have controllers, instead it has collections).
The model you are receiving is a standard model from the MVC paradigm. Model's are the underlying data structures that hold the data that the user is working with.
When you do
sidebar.on('change:color', function(model, color) {
// some code here
});
you are attaching an event handler to the sidebar model. Specifically, you are saying that when the color attribute on this model changes, call the function. Since this event can and will trigger at a later point in time, Backbone passes the event handler function two arguments: the first is the model on which the event fired, and the second is the attribute that changed.
The arguments are passed in a specific order, that is model is the first argument, and the changed attribute is the second. Thus if you omit the model argument from your event handler function, the passed in model gets assigned to color, and color doesn't get assigned to any argument.
Recommended reading:
More about MVC and models
More about backbone models