Unable to get Store inside Sencha Controller - sencha-touch

I'm using Sencha Touch 2.3. I'm trying to get a Store instance inside a controller in a similar way thats defined in this article http://www.sencha.com/learn/architecting-your-app-in-ext-js-4-part-3/.
I've defined the 'Location' store in the Controller config. I then try to get the store using 2 methods that both fail. First through Ext.getStore and the second through getLocationStore which should be an autogenerated function. Both fail. The first call returns undefined and the second call throws an exception because the function is not available.
Ext.define('MyApp.controller.Location', {
extend: 'Ext.app.Controller',
config: {
refs: {
locationSearchField: '#locationSearchField'
},
control: {
locationSearchField: {
action: 'onSearchAction'
}
},
stores: [ 'Location' ]
},
onSearchAction: function() {
var locationSearchStore = Ext.getStore('Location');
if (locationSearchStore == undefined) {
Ext.Logger.warn('Could not locate locationSearchStore');
locationSearchStore = this.getLocationStore();
if (locationSearchStore == undefined)
Ext.Logger.warn('Could not location locationSearchStore again!');
else
Ext.Logger.info('Success!');
}
else
Ext.Logger.info('Success!');
}
});

You can get your store by: Ext.data.StoreManager.lookup('Location') (if it's called MyApp.store.Location).
To be sure, that you are in the right context in the onSearchAction, try to call console.dir(this); and check that this is the controller object itself

First of all, you want to access store in sencha touch but you have given link of extjs. Second, you need to define your store first and then add it in app.js file. And then you can access your store by Ext.getStore('Location') method. For reference you shold learn this http://miamicoder.com/2012/sencha-touch-2-stores-adding-removing-and-finding-records/

Related

How to access the elements in a sencha touch 2 store

I am new to Sencha Touch so I am still struggling with the usage of stores.
I have created this store which I am successfully using to populate a list:
Ext.define('EventApp.store.events',{
extend: 'Ext.data.Store',
config: {
model: 'EventApp.model.event',
autoLoad: true,
storeId: 'events',
proxy:{
type:'ajax',
url: './resources/EventData.json',
reader: {
type: 'json',
rootProperty: 'events'
}
}
}
});
As I mentiones this store works correctly when referenced from a list and I can display the contents of it. Therefore I am assuming the store is correctly defined.
Unfortunately when I try to access the store from a controller for one of my views (which will be used to populate the items of a carousel) I don't seem to get any data back from the store. The code I am using is the following:
onEventCarouselInitialize : function(compon, eOptions) {
var past = compon.getPast();
var eventsStore = Ext.getStore('events');
eventsStore.each(function(record){
console.log('Record =',record); //<-- this never gets executed.
},this);
}
I have tried executing an eventsStore.load() on an eventsStore.sync() but I never seem to get any elements available in the store.
What am I missing?
Thanks
Oriol
What i have understand is, perhaps your store data has not been loaded when you are accessing it. So put you each() function on store inside this for delaying 500ms:
Ext.Function.defer(function(){
// Put each() here
}, 500);
Have a try by delaying more or less.

Profile feature in Sencha Touch 2 causes problems in production mode build

I have created a Sencha Touch 2 app and built a production mode version. However, I have encountered a big issue with the production build and it running in Phone/Tablet modes.
The current profile implementation of ST2 seems flawed as even if you have a specific profile activated, all views are still loaded in. In my application I want to be able to specify views using the xtype alias in the view config, and have the correct view for phone or tablet profile loaded in without any special coding. If all views from profiles are loaded in then this can't work (one view will always override another).
The only way I could achieve this was to dynamically add the profile at bootup stage (within app.js) like so:
Ext.application({
name: 'MyTestApp',
var activeProfile = Ext.os.is.Phone ? ['Phone'] : ['Tablet'];
requires: [ ... ],
profiles: activeProfile
});
This has worked fine. It means I can then load the correct view and still just use the xtype alias within the config of another view and/or ref in a controller. However, I noticed that when I generate a production build and load up a console window, both of the following are defined:
MyTestApp.views.phone.Login
MyTestApp.views.tablet.Login
Normally the tablet or phone version would be undefined depending on the profile. I'm assuming this is the case because the production mode build has parsed ALL dependencies and then included all views regardless of the profile.
So in my start-up controller I have a button handler which then creates a login view from the xtype.
Controller:
refs: {
loginView: {
selector: 'loginview',
xtype: 'loginview',
autoCreate: true
}
}
Handler:
var loginView = this.getLoginView();
In development mode, the loginView variable will either be MyTestApp.views.tablet.Login or MyTestApp.views.phone.Login depending on the profile.
How do I ensure that the loginview instantiated here gets the correct version depending on the profile when in production mode?
I had been struggling with this, when I would move either of the solutions to the devices, I would be stuck with the fact that all views are referenced and would get some xtype collision always giving me the phone view. ( i had to move to aliases eventually - not sure why :( ). I finally managed to crack this for my use case, just sharing for future reference.
I am running touch 2.3.1 and cordova 3.3.1 with the latest cmd 4.0.2.67
I use the solution from Christopher except I had to change the source code in the sencha touch source directory rather than keep it in the app.js [truthfully I don't know why it hangs when I leave it as an override]
In addition I have had to configure the views the following way in order for:
define a base class for the view with an alias so the controller to understand the ref as it loads first
dynamically assign the alias to the view instantiated by the profile
strip out (using Christopher code)
Base class for the views
Ext.define('MyApp.view.CatalogView', {
extend: 'Ext.Container',
alias: 'widget.catalogview'
});
Assign an alias to the profile specific view
Ext.define('MyApp.profile.Phone', {
extend: 'Ext.app.Profile',
config: {
name: 'Phone',
views: ['CatalogView'],
},
isActive: function() {
return Ext.os.is('Phone');
},
launch: function() {
Ext.ClassManager.setAlias('MyApp.view.phone.CatalogView', 'widget.catalogview');
}
});
Repeat for the tablet view
For all who want to know how I resolved this, I'm now left bald after pulling all my hair out;)
All my profile views where I want to have the xtype names remain the same even though they might belong in the phone or tablet profiles, I have to remove the alias/xtype config on the class. I then have a profile base class defined like so with a shared helper function:
Ext.define('MyApp.profile.Base', {
extend: 'Ext.app.Profile',
config: {
},
mapViewAliases: function () {
var self = this;
var views = this.getDependencies().view;
var newAliasMap = null;
Ext.each(views, function (view) {
Ext.Array.some(self.getViewsToAliasMap(), function (map) {
if (map[view]) {
if (!newAliasMap) {
newAliasMap = {};
}
newAliasMap[view] = [map[view]];
return true;
}
});
});
if (newAliasMap) {
console.log('view aliases being mapped for: ' + this.$className);
Ext.ClassManager.addNameAliasMappings(newAliasMap)
}
}
});
Then I have the profile class inherit from the base class (this is repeated with the tablet profile except the viewsToAliasMap holds classes belonging to the tablet profile instead of the phone profile):
Ext.define('MyApp.profile.Phone', {
extend: 'MyApp.profile.Base',
config: {
name: 'Phone',
views: ['Login', 'Home', 'Welcome' ],
viewsToAliasMap: [
{ 'MyApp.view.phone.Login': 'widget.loginview' },
{ 'MyApp.view.phone.Home': 'widget.homeview' },
{ 'MyApp.view.phone.Welcome': 'widget.welcomeview' }
]
},
isActive: function () {
return Ext.os.is.Phone;
},
launch: function () {
console.log("Phone profile launched");
this.mapViewAliases();
}
});
So basically, the profile calls the function mapViewAliases() on the base class in the launch function. The mapViewAliases() registers the view class names with the aliases defined in the profile with the class manager. So effectively the xtype names are resolved at run-time.
I'm sure this code can be improved and/or a better way to do this.
Please feel free to let me know.
I am using a pretty naive implementation... I'm sure it could be made more robust, but I've been hacking at this for 5 hours or so now.
Ext.define('MyApp.override.Application', {
override : 'Ext.app.Application',
onProfilesLoaded: function() {
var profiles = this.getProfiles(),
length = profiles.length,
instances = [],
requires = this.gatherDependencies(),
current, i, profileDeps;
for (i = 0; i < length; i++) {
var instance = Ext.create(profiles[i], {
application: this
});
/*
* Note that we actually require all of the dependencies for all Profiles - this is so that we can produce
* a single build file that will work on all defined Profiles. Although the other classes will be loaded,
* the correct Profile will still be identified and the other classes ignored. While this feels somewhat
* inefficient, the majority of the bulk of an application is likely to be the framework itself. The bigger
* the app though, the bigger the effect of this inefficiency so ideally we will create a way to create and
* load Profile-specific builds in a future release.
*
CMK - PSHAW!
*/
if (instance.isActive() && !current) {
console.log('Profile active: ' + instance.getName());
current = instance;
profileDeps = instance.getDependencies();
requires = requires.concat(profileDeps.all);
var ns = instance.getNamespace();
this.setCurrentProfile(current);
// Merge Controllers, Models, Stores, and Views
this.setControllers(this.getControllers().concat(profileDeps.controller));
this.setModels(this.getModels().concat(profileDeps.model));
this.setStores(this.getStores().concat(profileDeps.store));
this.setViews(this.getViews().concat(profileDeps.view));
// Remove the view ref and requires for default views, when a profile specific one exists
Ext.each(profileDeps.view, function(className) {
if (className.indexOf('view.' + ns + '.') !== -1) {
// Requires
var index = requires.indexOf(className.replace('view.' + ns, 'view'));
if (index !== -1) {
requires.splice(index, 1);
}
// Views
index = this.getViews().indexOf(className.replace('view.' + ns, 'view'));
if (index !== -1) {
this.getViews().splice(index, 1);
}
}
}, this);
instances[0] = instance;
break;
}
}
this.setProfileInstances(instances);
Ext.require(requires, this.loadControllerDependencies, this);
}
});
Put this before your Ext.application, and it replaces the profile loader... This one strips out default views with the same name as one in the active profile namespace.
It requires that you define an xtype for the views that match, then even your refs in controllers will work...
I need to continue testing with this, but it looks promising so far.

Back Button - Navigating through Viewport

I'am adding content to my application viewport like this:
Ext.Viewport.animateActiveItem(item, {transition})
I'am searching for a way to get "back" to the last view. Is that possible, or does the viewport destroy the last view?
Why not just use the built in history support? You can add an entry to the history object like so:
this.getApplication().getHistory().add(Ext.create('Ext.app.Action', {
url: 'dashboard'
}));
Once you call that function, it will change the application's URL hash. You can grab the event by using routes in your controller... add it to the config like so:
config: {
routes: {
'dashboard': 'showDashboard'
},
control: {
//controls...
}
},
Sencha Touch will recognize the URL change and look to your routes to call a function like so:
showDashboard: function() {
Ext.Viewport.animateActiveItem(item, {transition});
},
Using this method, the native back button will take you back to the previous view, you can also call which view you want to go to etc... view the documentation on the history object here: http://docs.sencha.com/touch/2-0/#!/api/Ext.app.History
Why don't you use the Ext.Viewport.animateActiveItem() on your first panel then ?
I did it here : http://www.senchafiddle.com/#xTZZg
Hope this helps
Ext.app.Action is a private Sencha class so cannot be guaranteed to exist in future releases. A better way is replace ...
this.getApplication().getHistory().add(Ext.create('Ext.app.Action', {
url: 'dashboard'
}));
with this ...
this.getApplication().redirectTo('dashboard');
You can also pass a Model object provided it implements a toUrl() method ...
this.getApplication().redirectTo(myModelObj);
If required, you can now just use the following to go back:
history.back();
Refer to the Touch History Guide:
http://docs.sencha.com/touch/2-2/#!/guide/history_support

Dynamically adding html to panel

I am designing an app in sencha touch2. I have a panel object in my JS file. I need to dynamically set the text/html for this component. The store for this component is defined at the application level. Following is the thing I worked out:
Ext.define('class_name',{
....
config : {
pnlObj : null,
...
}
initialize : function() {
this.config.pnlObj = Ext.create('Ext.Panel');
var store = Ext.data.Storemanager.lookup('some_store');
store.on('load',this.loadStore,this);
this.setItems([{
//some items here
{
flex : 2,
// id : 'somepnl',
config : this.config.pnlObj
}
}]);
},
loadStore : function(store, rec) {
var text = rec.get('text');
var panel = this.config.pnlObj;
// var panel = Ext.getCmp('somepanl');
panel.setHtml(text);
}
});
When I inspect the inspect the element using Firebug console, I can find the panel added there. But I am not able to set the html dynamically. no html text is set there. I tried adding it using panel.add() & panel.setItems() method which doesn't work. If I give an id to that panel(somepanel here) and try to access it using Ext.getCmp('smpanel') then in that case it works fine. I have found that using Ext.getCmp() is not a good practice and want to avoid it as it might somewhere break my code in the future.
I guess the way I am instantiating the panel object is creating some issue. Can someone suggest the best way of doing it?
The recommended way to manipulate your components in Sencha Touch 2 is using controller, through refs and control configs. For example, your panel has a config like this: xtype:'myPanel', then in your controller:
refs: {
myPanel: 'myPanel'
}
control:{
myPanel: {
on_an_event: 'set_html_for_my_panel'
}
}
Lastly, define your function:
set_html_for_my_panel: function()
{
this.getMyPanel().setHtml('my_updated_html');
}
P/S: Behind the scene, Sencha Touch 2 uses Ext.ComponentQuery for refs in controllers

ST2: Using 2 unique instances of a store & MVC

I have a simple test app where I have a carousel that will instantiate multiple of the same type of grid, each grid having it's own copy of a store.
Carousel:
Ext.define('App.view.TopPageCarousel', {
extend: 'Ext.Carousel',
xtype : 'app-toppagecarousel',
requires: ['Ext.Carousel', 'App.view.TopPageGrid'],
config: {
title: 'Top Pages',
items: [{
xtype : 'app-toppagegrid',
title : 'titleA'
},{
xtype : 'app-toppagegrid',
title : 'titleB'
}]
}
});
At first I was defining the store in the grid as a property in its config and I have the controller listening for store changes, just to let me know it was being loaded. The ajax call is made and the grid was populated. However, All grid's were populated with the same data even though unique data was being returned with each call.
Then I found a post that said I needed to instantiate the stores as the grid is being populated, like so:
constructor : function() {
var me = this;
Ext.apply(me, {
store : me.buildStore()
});
me.callParent(arguments);
me.store.load({params : {ufq : this.title}});
},
buildStore : function() {
return Ext.create('App.store.Links');
}
This sort of works. The ajax call is being made, but the grid isn't being populated now and I am not seeing the console.log("Store loaded"); being executed that I placed in the controller. What am I doing wrong?
It turns out in ST2 instead of using Ext.create, the best thing to do is (in my particular instance, not as a standard):
constructor : function() {
this.callParent(arguments);
Ext.setStore('App.store.Links');
},
Using Ext.getStore & Ext.setStore are necessary now if you want a lot of the benefits that go along with events & the store manager.