PDA

View Full Version : Problem with Sencha Profiles



rvelasquez
23 Apr 2012, 12:58 PM
Hi,

I'm trying to wrap my head around Sencha profiles and I'm finding it a little awkward to use. I would like to have a different layout between Android and iPhone (different position of tab bar). Here are the two profile specific views:



Ext.define("myapp.view.iphone.MyView", {
extend: 'Ext.TabPanel',
xtype: 'myview',


config: {
tabBarPosition: 'bottom',
.........
});




Ext.define("myapp.view.android.MyView", {
extend: 'Ext.TabPanel',
xtype: 'myview',


config: {
tabBarPosition: 'top',
.........
});


Then in my profiles I'm loading the profile specific view:



Ext.define('myapp.profile.Android', {
extend: 'Ext.app.Profile',


config: {
name: 'Android',
views: [
'myapp.view.android.MyView'
]
},
..........
});




Ext.define('myapp.profile.iPhone', {
extend: 'Ext.app.Profile',


config: {
name: 'iPhone',
views: [
'myapp.view.iphone.MyView'
]
},
..........
});



Finally in my main application view:



Ext.define("myapp.view.Main", {
extend: 'Ext.Container',
xtype: 'mainview',
config: {
layout: 'card',
items: [
{ xtype: 'someotherview' },
{ xtype: 'myview' }
]
}
});


What I was hoping to happen is that Sencha would only load the views of the first profile that returns true when isActive is called. Then there would be only one view with the xtype "myview". That way when the Main view loads it will load the myview that's specific to the device I'm launching. However it looks like I can't use xtype that way. It looks like it loads all the views (both Android specific and iPhone). So when it tries to resolve the xtype "myview" it doesn't get the right one. It seems really awkward if I have to create a profile specific version of the Main view just so I can get this work. How should I go about getting this to work?

edspencer
23 Apr 2012, 1:15 PM
The framework does indeed load all of the specified views, even if the profile isActive function did not match. The reason for this is that if we don't do it then we don't have a universal app - you'd have to build an Android version of the app and an iPhone version of the app, then figure out which to serve to which devices, presumably somewhere on the server. Instead, we create a universal version of the app that can bootstrap itself on the device and figure out what to do.

The main downside of this approach is exactly what you've just pointed out - you can't overload the xtypes in this manner. It's something I'd like us to come up with a better solution around in the next minor release or two, and would welcome ideas and feedback.

rvelasquez
23 Apr 2012, 1:18 PM
So is the recommended approach to create different versions of all controllers/views etc for each profile? That could be really messy if you are dealing with more than two profiles. I'm pretty new to Sencha and I'm only using the JSON config style to configure my views/controllers. It seems like there must be a way to do this dynamically like a factory that creates different views based on the profile that is currently active. What's the proper way to construct objects dynamically instead of through the JSON style construction.

edspencer
23 Apr 2012, 1:25 PM
At the moment I'd just say to avoid using the same xtype when you have profile-specific views. That may mean you end up with xtypes like 'overviewios' and 'overviewandroid'. You could factory this if you want to keep your code cleaner - should be simple enough but that's not built into the framework at the moment.

rvelasquez
23 Apr 2012, 1:35 PM
Yeah ... that's what I was trying to avoid because I have controllers that ref the view so if I create a profile specific view I also need to create a profile specific controllers for each controller that references that view. I realize I can use inheritance to reduce code duplication but overall it's just awkward. Using Sencha has been awesome but the experience of using profiles has been inconsistent with how the amazing the rest of Sencha is.

edspencer
23 Apr 2012, 1:43 PM
Yup, definitely something we want to improve

anselmtmcclain
5 Jun 2012, 3:06 PM
I'm glad I found this thread as I had the exact same misconception as the original poster.

I thought that the xtypes existed to provide exactly this kind of abstraction. It seems like the profile mechanism could work with the ClassManager to wire an xtype to the appropriate implementation class when multiple profile-specific implementations exist.

Since for my app profile-specific controllers would be overkill, I'll just switch manually between view classes within controller methods as needed. Not a big deal, but I'm not getting much then from the entire convention of the profile. It seems like you need to branch your view construction at the top "launch" level if you want to use profiles as presented in the docs.

If there are any updated discussions or ideas on this one, I would really appreciate a link to the thread,etc. Thanks.


Edit: One approach might be to set/overwrite aliases within the profile's launch method, via Ext.ClassManager.setAlias(). Using that, I can re-write aliases to profile-specific subclasses myself within the profile, then use the aliases in my controllers/views without any switching and still get the implementation class I want. Curious if anyone has tried such a thing or sees any downsides.

DodgyDave
5 Jun 2012, 4:48 PM
We encountered a similar problem and add the xtype in the launch method of the profile.

So the code would be something like



Ext.define("myapp.view.iphone.MyView",
...
Ext.define("myapp.view.android.MyView"
,...

Ext.define('myapp.profile.android', {
extend: 'Ext.app.Profile',
launch: function() {
myappp.view.android.MyView.addXtype('myView');
}
...

Ext.define('myapp.profile.iphone', {
extend: 'Ext.app.Profile',
launch: function() {
myappp.view.iphone.MyView.addXtype('myView');
}

...

gcw07
6 Jun 2012, 12:54 AM
I experienced the same problem recently and my solution was to setup different xtypes for each profile views. Then setup controllers where they extend a base controller. Within the profile controllers, put the references to point to the correct view xtype. So you might have something like.

controller/phone/Search.js

Ext.define('App.controller.phone.Search', {
extend: 'App.controller.Search',
config: {
refs: {
mainPanel : 'mainPanelPhone',
searchPanel : 'searchPanelPhone',
}
}
});
controller/tablet/Search.js

Ext.define('App.controller.tablet.Search', {
extend: 'App.controller.Search',
config: {
refs: {
mainPanel : 'mainPanelTablet',
searchPanel : 'searchPanelTablet',
}
}
});
controller/Search.js

Ext.define('App.controller.Search', {
extend: 'Ext.app.Controller',
});Since Sencha only executes the controller from the active profile, the references are correct within "controller/Search.js". No idea if this is the correct way to do it or not, but it does seem to work and keeps majority of the code within the base controllers since the logic isn't different for different views.

anselmtmcclain
6 Jun 2012, 12:15 PM
Thanks for both replies - both of those are very useful suggestions.

I hadn't thought of just overriding the refs in profile-specific controller subclasses, and leaving all the logic (using the refs) in the base controller class.

Dodgy Dave, I think that your approach is the same / similar to what I noted in my edit above. I'm going to stick with ClassManager.setAlias() as addXtype() is documented as private, but I think the end result is exactly the same, and gets us the re-wired xtypes we want.

jep
9 Nov 2012, 9:19 AM
The main downside of this approach is exactly what you've just pointed out - you can't overload the xtypes in this manner. It's something I'd like us to come up with a better solution around in the next minor release or two, and would welcome ideas and feedback.

Any progress on this, Ed? This is probably one of the roughest areas of the API design to me. You can do marvelous things designing your interface in separate files as object configs. But then when you go to use multiple profiles, there's simply a big gap in the road that you have to build your own bridge to. Every time.

It seems like Controllers already work the way we'd expect them to. The API knows to prune off the inactive profile-specific controllers and treat them as "dead" code. I really wish it'd do the same thing for inactive profile-specific views (and I suppose models and stores).

As you noted, all the code has to be in the app. But the API doesn't have to pay attention to all of it if it ties in with the profiles at all levels.

edspencer
9 Nov 2012, 10:15 AM
Hey jep, I actually left Sencha a few months back so not totally clued in on the current state of things... I see 2.1 just went GA so maybe there's something in there.

Agreed that it's the worst part of MVC at the moment - it's probably the only part of that stack that I would have liked to have fixed before I moved on.

jep
9 Nov 2012, 11:55 AM
Ah, I see, Ed. Well, thanks for the update. I'm poking around in the guts of the code right now to see if there's anything I can hack in to prune out the unused profiles. But I'm sure you're even more aware than I am of how nitty-gritty that code can get!

jep
12 Nov 2012, 12:31 PM
Well, I never could figure out a way to fix it at the framework level. The code is just too complicated and seems like it would take some major work. So here's my hack:



//<debug>
Ext.Loader.setPath({
'Ext': 'touch/src',
'default': 'app'
});
//</debug>

var profiles;

if (window.location.search.indexOf('profile1') !== -1) {
profiles = ['profile1'];
}
else {
profiles = ['profile2'];
}

Ext.application({
profiles: profiles,

name: 'TestApp',


I basically do all my testing for which profile(s) should be loaded right there in the app.js. For this simple test I used URL params but you'd do similar by using Ext.os.is('iPad') or whatever.

edspencer
12 Nov 2012, 1:03 PM
The issue (as I recall, it was a while back now so my recollection could be rusty) comes when it's time to build the app for production. The way profiles originally worked was that they'd only load the files they need, rather than loading everything. Unfortunately this means you need one build for each Profile, and some logic somewhere that replicates the profile detection system and serves up the right build.

Having everything load for all profiles alleviates these problems, but causes that annoying xtype overloading issue. The way I saw it when I implemented it is that this is the lesser of two evils but it still grates...

jep
12 Nov 2012, 1:06 PM
Right, I fully understand the "one build for all" concept. You have to load all the code into memory. However, the API then does a lot of stuff to process that code (such as building the xtype map). What I'm suggesting is that you check the profiles before processing the profile-dependent code. That code can be effectively "pruned" at runtime, if you structure the API to do so.

Check out my workaround above for a very crude way of doing the pruning.

edspencer
12 Nov 2012, 1:14 PM
Yup makes sense. The class system actually handles all of that logic - from loading the file onto the page to actually constructing the class (at which point the xtype is registered). There would need to be some way to have the class loaded but not parsed by the class system to enable this to work. Something to ask Jacky for I think

jep
12 Nov 2012, 1:23 PM
I posted a request for it here:
http://www.sencha.com/forum/showthread.php?248824-pruning-of-unused-profiles-and-more

andyjv
22 Nov 2012, 6:08 PM
I too, share the thoughts in this thread. It seems like the current implementation virtually defeats the spirit and purpose of Profiles in specific and Sencha Touch as a whole.

kisscool82
11 Dec 2012, 3:23 AM
Same issue for me.
I tried jep's hack but it still does'nt work.

Changing the xtypes with profile name cause bugs with refs in the controllers and dynamic views creation. I can't refactore the entire code of my apps. If the solution is to write entire code for each profiles, it's not the profile spirit anymore...

What's the solution please ?
Is there an update in the framework ?

Thx