I have the following flow: before the app launches I want to check something on the server. Based on the response I want to make a decision. I've created an utility class that wraps my js event and also an app controller.
Bellow is app controller:
Ext.define('MyApp.controller.AppController', {
extend: 'Ext.app.Controller',
appEventDispatcher:function (){
// Create a dummy DOM element
var dummy = document.createTextNode('');
// Create custom wrappers with nicer names
this.off = dummy.removeEventListener.bind(dummy);
this.on = dummy.addEventListener.bind(dummy);
this.trigger = function(eventName, data){
if( !eventName ) return;
var e = new CustomEvent(eventName, {"detail":data});
dummy.dispatchEvent(e);
}
}
});
And my utility class:
Ext.define('MyApp.util.Util', {
statics : {
checkSomethingOnServer: function(customEvent){
var store = Ext.StoreManager.lookup('appStore');
store.load({
scope: this,
callback: function(records, operation, success){
if (success === true){
customEvent.trigger('success', true);
if (success === false)
debugger;
customEvent.trigger('fail', true);
}
}
});
}
}
});
Using the utility class I load a store. In the callback method, I trigger my custom event. This event is handled in the app.js file.
The code works in fiddle and also using app watch, when I want to build the code some errors are occurring complaining(syntax error).
I've created also a fiddle.
How to create a custom event in ExtJS and how to use it? I need the same behavior as with the js event but Extjs implementation.
In ExtJS, you would just attach an event listeners to the store with your custom event's name:
store.on('myownevent', function(success) {
console.log(success);
});
and your code may go ahead and fire events on the store by that name:
store.load({
scope: this,
callback: function(records, operation, success){
store.fireEvent('myownevent', success);
}
});
If no listener for that event is attached, nothing happens; if one or more listeners are attached, they are executed in the order of priority, for those with the same priority, in the order they were added.
https://fiddle.sencha.com/#view/editor&fiddle/21g7
Related
I am exploring the BBCloneMail demo application for MarionetteJS, but I am not seeing how the events are triggering the rendering actions. I saw some global 'show' event here:
https://github.com/marionettejs/bbclonemail/blob/master/public/javascripts/bbclonemail/components/appController.js#L25
show: function(){
this._showAppSelector("mail");
Marionette.triggerMethod.call(this, "show");
},
But I don't see, where/how the Marionette.triggerMethod results into rendering the Mail component. I was trying to call the triggerMethod for my case, but I get a 'cannot call apply for undefined'. Why is the call above working for the BBcloneMail application.
The Application controller for my case:
MA.AppController = Marionette.Controller.extend({
initialize: function(){
_.bindAll(this, "_showGenres");
},
show: function() {
if (MA.currentUser) {
MA.navbar.show(new MA.Views.Items.LogoutNavbar({model: MA.currentUser}));
}
else
{
MA.navbar.show(new MA.Views.Items.LoginNavbar());
}
this._showGenres();
},
_showGenres: function() {
var categoryNav = new MA.Navigation.Filter({
region: MA.filter
});
this.listenTo(categoryNav, "genre:selected", this._categorySelected);
categoryNav.show();
MA.main.show(MA.composites.movies);
},
showMovieByGenre: function(genre){
var movies = new MA.Controllers.MoviesLib();
that = this;
$.when(movies.getByCategory(genre)).then(that._showMovieList);
Backbone.history.navigate("#movies/genres/" + genre);
},
_showMovieList: function(movieList){
var moviesLib = new MA.Controllers.MoviesLib({
region: MA.main,
movies: movieList
});
Marionette.triggerMethod.call(this, "show");
}
});
I init the application controller in a init.js with:
app = new MA.AppController();
Looking at the source for triggerMethod, this is a way of both triggering an event (the string being passed in), and additionally (if it exists) running a method on the object that has an 'on' prefix.
In your case the error relates to line 560, specifically that there is no method apply on undefined. Based on the code its (in your case) trying to call the equivilent of this.trigger('show') - but AppController doesn't have a method called trigger.
In which case I'm guessing that in the BBCloneMail example this (being bassed into triggerMethod.call) is not actually the controller, but instead the view that is to be shown.
I wish to read a whole database table to fill a Backbone.js Collection, before updating a View.
I am using fetch and listening to the reset event.
My problem is the reset event fires up before the http request is made to the server.
My question is: how can I render the view AFTER the data is received back from the server on a fetch?
Here is a jsfiddle showing the problem (with a debugger placed at reset):
http://jsfiddle.net/GhaPF/16/
The code:
$(document).ready(function() {
var Item = Backbone.Model.extend({
urlRoot : './items'
});
var ItemList = Backbone.Collection.extend({
model: Item,
url: './items/',
});
var ItemListView = Backbone.View.extend({
el: 'body',
initialize: function(myitemList) {
this.itemlist = myitemList;
this.itemlist.bind('reset', this.debuggThis());
},
debuggThis: function() {
debugger;
},
render: function() {
},
events: {
"keypress #new-item": "createOnEnter"
},
createOnEnter: function(e) {
}
});
$("#new-item").focus();
var itemlist = new ItemList();
var myitemListView = new ItemListView(itemlist);
itemlist.fetch();
});
The following code works, but it just doesn't feel like proper backbone.js (MVC) code since it would be placed outside of the View definition:
itemlist.fetch().complete(function(){
Maybe the issue is this line:
this.itemlist.bind('reset', this.debuggThis());
Should actually be:
this.itemlist.bind('reset', this.debuggThis);
Your debugThis function was getting run at the time you set up the listener for the 'reset' event - not when the event is triggered. This was telling JavaScript that you wanted debugThis to return a callback function instead of having debugThis "be" the callback function.
Also, orangewarp's comment about passing 'this' as the third parameter is probably relevant too. Sot it would end up as:
this.itemlist.bind('reset', this.debuggThis, this);
That's strange. When you fetch() the reset event should be triggered AFTER your collection is populated. So I'm thinking the phenomena that reset happens before the http request is fired up may not be what you think it is.
Instead of using the complete... you could always just use the success callback option like this:
itemlist.fetch({
success: function() {
// Whatever code you want to run.
itemlist.debuggThis();
}
});
Also, when binding your reset you probably want this:
this.itemlist.bind('reset', this.debuggThis, this);
How do I know what event is triggered on a Backbone collection when binding multiple events to it using .on()? See the following example for clarification. (Also see the jsFiddle: http://jsfiddle.net/PURAU/3/)
var Car = Backbone.Model.extend({
nrOfWheels: 4,
color: 'red',
speed: 'slow'
});
var Garage = Backbone.Collection.extend({
model: Car
});
var myGarage = new Garage(),
myCar = new Car();
myGarage.on('add change reset', function() {
// How do I know what event was triggered?
console.log('add change reset', arguments);
});
myGarage.on("all", function() {
// In here, the name of the event is the first argument.
console.log('all', arguments);
});
// Trigger add
myGarage.add(myCar);
// Trigger change
myCar.set('speed', 'fast');
// Trigger reset
myGarage.reset();
If we want specific actions per event the most ordered way is to listen every one, also if you want to have an overall change listener when anything change call it inside your listeners and specify the event name at yourself.
myGarage.on('add', function() {
yourGlobalFunction(arguments, 'add');
//specific actions for add
});
myGarage.on('change', function() {
yourGlobalFunction(arguments, 'change');
//specific actions for change
});
myGarage.on('reset', function() {
yourGlobalFunction(arguments, 'reset');
//specific actions for reset
});
function yourGlobalFunction(prevArguments, eventName){
log(prevArguments, eventName);
}
You'll need to override the trigger method for doing so, have a look at the code:
https://github.com/documentcloud/backbone/blob/master/backbone.js#L148
suppose I have a model and a view ,ths view have two method:one is bind the document mousemove event and the other is unbind method,defalut I give the document mousemove event, once the model's enable value changed I will call the view's unbind method:
window.ConfigModel = Backbone.Model.extend({
defaults: {
'enable':0
},
initialize: function(){
this.bind("change:enable", function () {
var portView2 = new PortView();
portView2.viewOff();
});
},
change:function () {
this.set('enable', 9);
}
})
window.PortView = Backbone.View.extend({
viewOn: function () {
$(document).on('mousemove', function () {
console.log('move')
})
},
viewOff: function () {
$(document).off('mousemove');
}
})
then I put an input on the document to call the model changed:
$('input').click(function () {
var configModel = new ConfigModel();
configModel.change();
})
the boot script is :
var portView1 = new PortView();
portView1.viewOn();
The problem is once I call the click the input button ,the chrome would tell me an error:Maximum call stack size exceeded it seems the change be invoke many times.So what's the problem with my problem ,how can I solve this problem
Backbone models already have a change method:
change model.change()
Manually trigger the "change" event and a "change:attribute" event for each attribute that has changed. If you've been passing {silent: true} to the set function in order to aggregate rapid changes to a model, you'll want to call model.change() when you're all finished.
Presumably something inside Backbone is trying to call configModel.change() and getting your version of change which triggers another change() call inside Backbone which runs your change which ... until the stack blows up.
You should use a different name for your change method.
That said, your code structure is somewhat bizarre. A model listening to events on itself is well and good but a model creating a view is odd:
initialize: function() {
this.bind("change:enable", function () {
var portView2 = new PortView();
portView2.viewOff();
});
}
And instantiating a view simply to call a single method and then throw it away is strange as is creating a new model just to trigger an event.
I think you probably want to have a single ConfigModel instance as part of your application state, say app.config. Then your click handler would talk to that model:
$('input').click(function () {
app.config.enable_level_9(); // or whatever your 'change' gets renamed to
});
Then you'd have some other part of your application (not necessarily a view) that listens for changes to app.config and acts appropriately:
app.viewOn = function() {
$(document).on('mousemove', function() {
console.log('move')
});
};
app.viewOff = function() {
$(document).off('mousemove');
};
app.init = function() {
app.config = new ConfigModel();
app.viewOn();
$('input').click(function () {
app.config.enable_level_9();
});
// ...
};
And then start the application with a single app.init() call:
$(function() {
app.init();
});
I have an issue triggering ‘load’ and ‘beforeload’ events on stores in ext4 :
Ext.define('Emvr.store.saveFormData', {
extend: 'Ext.data.Store',
model: 'Emvr.model.saveFormData',
constructor: function(config) {
config = config || {};
this.callParent([config]);
}
});
var store_myVisitsGrid_instance = new Emvr.store.saveFormData();
store_myVisitsGrid_instance.on(load, function() {
alert('ok load');
})
It won’t get into the function when I load the data using
store_myVisitsGrid_instance.loadData(responseFromServer)
I was able to use datachanged event for that purpose.
I digged in into the sources and found that the loadData method would eventually fire datachanged event. This was not happening on the old ext 3.x.
loadData DOESN'T trigger load event. The load method does.