In an MVC style Ext JS 4 application, assuming I have a single controller/view/store/model and that loads up properly- for an example, let's say this example is my code base.
My application loads up a view with a grid (list of Users), then when a row is clicked it opens and edit (User) view with a form.
Now, I want to add additional views to be displayed inside that form.
For example, I want to show a 'last 10 logins' (a list of activity records for the user) below the fields in the User form. I will have a separate view/controller/model/store for 'ActivityLog'. So I need to create the ActivityLog List view/controller and place it inside my User view (it will also have it's own code/logic to handle clicks on those records in it's grid, etc.).
Is that the correct approach?
Would the User controller load the view/controller of ActivityLog and somehow place it in to the User edit view, or do I have config data directly inside the User edit view that loads it in ?
Ok. Probably you can use something like this
// Navigation controller
Ext.define('App.controller.Navigation', {
extend: 'Ext.app.Controller',
models: [],
stores: [],
views: ['navigation.TabHost'],
init: function() {
this.control({
'tabhost': {
'render': function(tabHost) {
// Grid panels are panels too. So It does not make any sense to use additional container.
tabHost.add(Ext.create('App.view.users.List'));
tabHost.add(Ext.create('App.view.activitylogs.List'));
}
}
}
});
// Users controller
Ext.define('App.controller.Users', {
extend: 'Ext.app.Controller',
models: [],
stores: [],
views: ['users.List'],
init: function() {
this.control({
'userslist': {
'render': function(gridPanel) {
console.info('Im here');
}
}
}
});
// ActivityLog controller
Ext.define('App.controller.ActivityLog', {
extend: 'Ext.app.Controller',
models: [],
stores: [],
views: ['activitylogs.List'],
init: function() {
this.control({
'loglist': {
'render': function(gridPanel) {
console.info('Im here');
}
}
}
});
// Also you should have tree view files
// navigation.TabHost
Ext.define('App.view.navigation.TabHost', {
extend: 'Ext.tab.Panel',
alias: 'widget.tabhost',
initComponent: function() {
// ......
}
});
// users.List
Ext.define('App.view.users.List', {
extend: 'Ext.grid.Panel',
alias: 'widget.userslist',
initComponent: function() {
// ......
}
});
// activitylog.List
Ext.define('App.view.activitylogs.List', {
extend: 'Ext.grid.Panel',
alias: 'widget.logslist',
initComponent: function() {
// ......
}
});
Navigation controller will create widges and insert them to its tabhost.
But ActivityLog and Users Controllers will manipulate their views.
Would the User controller load the view/controller
Yes, you can do this.
User controller can use view from another controller. In other case controller can listen to view which was added in the another controller.
First off all you should register controllers in app.js
Ext.application({
name: 'App',
autoCreateViewport: true,
appFolder: 'js/App',
controllers: [
'User',
'ActivityLog'
],
launch: function() {
}
});
After that all views/stores/models, which was described in the controllers files, will be loaded automatically.
You will have ability to manipulate (add/remove) views wherever you want. But controllers will be listening to the views that you definitely specified.
Related
I have a layout view, with an itemView inside it. I have an event in my item view that triggers a save function. Inside that save function I would like to trigger another event that the layout captures.
So in the code below, in the onClickSave modelSaveSuccess I'd like to trigger a function in the parent layout, I have tried this.methodInParent() but it doesnt work
childView
define(["marionette", "underscore", "text!app/templates/client/form.html", "app/models/client"], function(Marionette, _, Template, Model) {
"use strict"
return Backbone.Marionette.ItemView.extend({
events: {
"submit #saveClient": "onClickSave"
},
onClickSave: function(ev) {
ev.preventDefault()
return this.model.save({}, {
success: function() {
console.log('success - trigger ')
},
error: function(request, error) {
console.log(error.responseText)
}
})
}
})
})
A good way to do it without introducing heavy coupling is to use Marionette's event aggregator as in the linked exemple if you use Backbone.Marionette.application.
// in your view
...
success: function() {
app.vent.trigger('myview:modelsaved');
}
...
// in your layout initialize()
...
app.vent.on('myview:modelsaved', function(){
console.log('model saved in itemView');
});
...
If you don't use Backbone.Marionette.Application you can always create your own Backbone.Wreqr.EventAggregator.
I'm having a weird problem with some Backbone Marionette controller in a sub-application module.
I cannot make it to work capturing one of its view events with the "controller.listenTo(view, 'event', ...)" method, although the "view.on('event',...)" works no problem.
Here is the sample code for the module views :
MyApp.module("SubApp.Selector", function (Selector, App, Backbone, Marionette, $, _) {
"use strict";
// Category Selector View
// ------------------
// Display a list of categories in the selector
// Categories list item
Selector.CategoryOption = Marionette.ItemView.extend({
template: "#my-category-template",
tagName: "option",
onRender: function () { this.$el.attr('value', this.model.get('id')); }
});
// Categories list container
Selector.CategoryListView = Marionette.CompositeView.extend({
template: "#my-category-list-template",
tagName: "div",
id: "categories",
itemView: Selector.CategoryOption,
itemViewContainer: "select",
events: {
'change #category_items': 'categoryClicked'
},
categoryClicked: function() {
var catID = this.$('#category_items').val();
console.log("category clicked "+catID);
this.trigger("category:changed", catID);
}
});
// Selector Component Controller
// -------------------------------
Selector.Viewer = Marionette.Controller.extend({
initialize: function (_options) {
this.region = _options.region;
this.collection = _options.collection;
},
show: function () {
var view = new Selector.CategoryListView({ collection: this.collection });
this.listenTo(view, 'category:changed', this.categoryChanged);
//view.on('category:changed', this.categoryChanged, this);
this.region.show(view);
},
categoryChanged: function (_category) {
console.log("category changed in controller");
}
});
});
Is there anything I got wrong about event listening from a controller object ?
Shouldn't I use the listenTo syntax as is seems to be recommended widely for proper event handling and listener destruction ?
I have an app with a carousel. On all of the carousel pages there are elements such as buttons and datepickers. I would like to handle the tapStart event on each of these elements using Sencha Touch but I haven't been able to find anything to allow me to do this.
Does anyone have an idea?
UPDATE
I asked this question on the Sencha Forums as well. Here is the link to the Sencha Forum thread: http://www.sencha.com/forum/showthread.php?262804-Handle-tapStart-Event-on-a-button&p=963782#post963782
You can try using touchstart which can be bound to any element including button
I figured out a solution to my problem with help from the Sencha Touch Forums.
First I used the initConfig function to initialize my configuration of my container.
Ext.define('MyApp.view.ViewName', {
...
// Very Important, this is what I use in the controller to handle the events
xtype: 'myxtype',
...
initConfig: function () {
var me = this;
this.config = {
...
items: {
...
{
xtype: 'button',
...
listeners: {
element: 'element',
// This is where my code handles the tapstart
// (touchstart) event
touchstart: function () {
// Fire an event on the controller (me)
me.fireEvent('buttondown');
}
}
},
...
}
}
this.callParent([this.config]); // Very Important when using initConfig
}
});
Then, in my controller I added this code:
Ext.define('MyApp.controller.MainController', {
...
config: {
views: [
'ViewName',
...
],
...
},
...
init: function () {
this.control({
'myxtype': {
buttondown: this.myFunction
}
})
},
myFunction: function () {
// Do something
}
});
I used mixins(EXT Js 4) in my project. I have following structure. Two classes named Class A and Class B with following function.
Ext.define('ClassA', {
classAFunction: function() {
alert("ClassAFunction in class A");
}
});
Ext.define('ClassB', {
classBFunction: function() {
alert("ClassBFunction in class B");
},
classAFunction: function() {
alert("ClassAFunction in class B");
}
});
In my controller i used mixins to extend the capability of my controller as shown in following code.
Ext.define(Site_Controller', {
extend: 'Ext.app.Controller',
views: [
'ui.Site_View'
],
mixins: {
classA: 'ClassA',
classB: 'ClassB'
},
init: function() {
this.control({
'MainPanel button[action=save]':{
click: this.CreateSite
}
});
},
CreateSite: function() {
alert("HELLO");
this.classAFunction;
this.classBFunction;
}
});
I call createsite method of controller on the click of button which is present in view. so when createsite method called first i got "HELLO" and than classAFunction called and it gives me "ClassAFunction in class A". So it call classA function But i want to call ClassAFunction which is present in classB. So In extjs how can i achive this.
Thanks in advance.
You can access the mixins and call the function directly
this.mixins.classB.classAFunction.call(this);
so here is the problem, I use MVC and I have several stores that I declared on app.js.
But now I need to do a login validation, and only load the stores after I get the response from the server, but if leave the declaration in app.js when the app loads it automatically loads all the stores.
Here is what my app is going needs to do:
LoginView make the validation, if validation is successful it changes the view to ListView, this view has a list that loads data from a store and this view can create other views with other lists.
I tried to require the stores in the ListView, but it throws errors cannot call method getCount of null.
What can I do to make it work. Thanks for the help.
Here is some code:
Ext.define("App.view.Listview", {
extend: 'Ext.Container',
xtype: 'listview',
requires: ['App.view.Listviewdetails',
'App.view.Filtros.FiltroJanelaPrincipal.Janelafiltrotiempoview',
'App.view.Menuview',
'App.view.Resultadopesquisaview',
'App.view.Pesquisaview',
'App.view.Maisinfousuarioview',
'Ext.Label',
'Ext.field.Search',
'App.store.Tiempos',
'App.store.Empresas',
'App.store.Produtos',
'App.store.Usuarios',
'App.store.FiltrosEvento',
'App.store.Historicos',
'App.store.Pesquisas'
],
config: {
id: 'listView',
layout: 'card',
items: {
layout: 'vbox',
id: 'listaEventos',
items: [
{
xtype: 'list',
id: 'listaTiempos',
flex: 6,
emptyText: 'Empty',
store: 'Tiempos',
itemTpl: '{dataTermino} {descricaoPrevia}'
}
]
}
and one of the stores:
Ext.define("App.store.Tiempos",{
extend: 'Ext.data.Store',
config: {
model: 'App.model.Tiempo',
autoLoad: true,
proxy: 'searchProxy'
}
});
You can add the stores in require in the respective view classes. It is not compulsory to add in app.js.
So after login when the view will be instantiated the store will be loaded automatically.