events flow of Panel in sencha touch 2 - sencha-touch

This is my application architecture.
var loginView = Ext.create('Ext.Panel',{
id: 'LoginView',
.....
});
var homeView = Ext.create('Ext.TabPanel',{
id: 'HomeView',
items: [
{
xtype: 'list',
title: 'Home',
store: Ext.create('TweetStore'),
disableSelection: true,
....
},
{
title: 'Reply',
....
},
{
title: 'DM',
....
}
]
});
var mainView = Ext.create('Ext.Panel',{
id: 'MainView',
layout: 'card',
items: [ loginView, mainView ]
});
Ext.define('TweetStore', {
extend: 'Ext.data.Store',
config: {
fields: ...
pageSize: 25,
autoLoad: true,
proxy: {
type: 'ajax',
url: '/home',
pageParam: 'page',
limitParam: 'count',
reader: {
type: 'json'
}
}
}
});
There are two panels in MainView. LoginView is a login page where user input his username and password. When the authorization succeed, the second panel HomeView shows.
My questions:
I want the data in TweetStore to be loaded after the authorization, that is, when the HomeView shows, the data begins to load. But the show event is triggered even the
panel is still hidden. What event should I catch.
When the application starts, I want to send a ajax request to check whether the user is login, if true, the LoginView hide and the HomeView shows. In which event should I check this?

Q. 1) You should listen for painted() event.
Fires whenever this Component actually becomes visible (painted) on
the screen. This is useful when you need to perform 'read' operations
on the DOM element, i.e: calculating natural sizes and positioning.
Q. 2) Send the ajax request for user authentication on initialize() event.
Fires when the component has been initialized

- 1st question:
painted event seems good but in fact, it's terribly full of bugs in current release of Sencha Touch 2, as my experience. Do NOT rely on its existence and documentation.
According to what you've described, it's clear that you want to load your store after a specific event (authorization), so how about firing a custom event after that? For example:
loginForm.fireEvent('authenticated', this) (or any extra-params you need)
then in your controller, just listen to event authenticated on loginForm view.
- 2nd question:
As you said, you want to run some processes right after your application started. So the right place to put your code is in launch() function of your app.js. Just simple.

Related

Multiple attachment with single callback_id: slack interactive component

Is it possible to have multiple menu attachment and allow users to select each menu before sending back the collated response?
return Promise.resolve({
text: `Rate each game`,
attachments: [
...games.map(game => ({
color: "#5A352D",
title: game,
callback_id: "game:done",
actions: [
{
name: "done",
text: "Select a score",
type: "select",
value: "game:done",
options: [
{ text: 1, value: 1 },
{ text: 2, value: 2 }
]
}
]
}))
]
});
This images shows how it renders
But, I need to call the callback only when the user has finished scoring each game.
Perhaps, I can provide an additional button for that, but how can I handle callback for these menu actions
Choosing a menu option will always fire a request to your app. But you could replace the former message and recreate the menu list each time and show the remaining menus to the user until all are chosen. Technically it will be a new message each time, but by replacing the old message the user will not notice.

How to use durandal router to activate dialogs?

I would love to a #signin route that would open a dialog on top of whatever page there was before.
Let's consider this example app this the following routes:
router.map([
{route: '', moduleId: 'vm/home', title: "Home"},
{route: 'about', moduleId: 'vm/about', title: "About"},
{route: 'signin', moduleId: 'vm/signin', title: 'Sign In'}
]);
Here are example use cases:
User is on # and navigates to #signin: we should see a Sign In dialog on top of Home page
User is on #about and navigates to #signin: we should see a Sign In dialog on top of About page
User navigates to http://localhost:9000/#signin: we should see a Sign In dialog on top of Home page
User is on #signin and closes dialog: we should see a page that was behind the dialog (there's always a page behind).
The dialog and router are both plugins and have no interactions between eachother.
Also having the router display dialog would ignore how the router works - it has a div which it dumps content into. Dialogs exist outside of all of this.
However if you wanted to (I may do this aswell), you could try this.
Add dialog: true to the route map.
Override router.loadUrl method. Check if the route is a dialog route as we marked before, and activate the dialog instead.
I would make the dialog a child route, so then you can know which view to display beneath the dialog. Otherwise you could just have to show the dialog over anything and ignore routing entirely.
Edit: I don't think this would entirely work actually. loadUrl returns a boolean. You could open the dialog and return false to cancel navigation.
Edit2:
My Attempt
The loadUrl method loops through all routes, and each has a callback, so ideally we need to insert our logic into this array.
for (var i = 0; i < handlers.length; i++) {
var current = handlers[i];
if (current.routePattern.test(coreFragment)) {
current.callback(coreFragment, queryString);
return true;
}
}
This array is added to using the routers route method. Durandal calls this method when you map routes, so ideally we could add some extra parameters to the route config and let Durandal handle these. However the configureRoute function is internal to the routing module, so we will need to edit that and make sure we copy changes over when updating Durandal in the future.
I created a new list of dialog routes:
{ route: 'taxcode/add(/:params)', moduleId: 'admin/taxcode/add', title: 'Add Tax Code', hash: '#taxcode/add', nav: false, dialog: true, owner: '#taxcodes' },
{ route: 'taxcode/edit/:id', moduleId: 'admin/taxcode/edit', title: 'Edit Tax Code', hash: '#taxcode/edit', nav: false, dialog: true, owner: '#taxcodes' }
The idea of an owner, is that if there is a case where the initial route is this, we need something behind the dialog.
Now replaced the router.route call in configureRoute with this:
router.route(config.routePattern, function (fragment, queryString) {
if (config.dialog) {
if (!router.activeInstruction()) {
// No current instruction, so load one to sit in the background (and go back to)
var loadBackDrop = function (hash) {
var backDropConfig = ko.utils.arrayFirst(router.routes, function (r) {
return r.hash == hash;
});
if (!backDropConfig) {
return false;
}
history.navigate(backDropConfig.hash, { trigger: false, replace: true });
history.navigate(fragment, { trigger: false, replace: false });
queueInstruction({
fragment: backDropConfig.hash,
queryString: "",
config: backDropConfig,
params: [],
queryParams: {}
});
return true;
};
if (typeof config.owner == 'string') {
if (!loadBackDrop(config.owner)) {
delete config.owner;
}
}
if (typeof config.owner != 'string') {
if (!loadBackDrop("")) {
router.navigate("");
return; // failed
}
}
}
var navigatingAway = false;
var subscription = router.activeInstruction.subscribe(function (newValue) {
subscription.dispose();
navigatingAway = true;
system.acquire(config.moduleId).then(function (dialogInstance) {
dialog.close(dialogInstance);
});
})
// Have a route. Go back to it after dialog
var paramInfo = createParams(config.routePattern, fragment, queryString);
paramInfo.params.unshift(config.moduleId);
dialog.show.apply(dialog, paramInfo.params)
.always(function () {
if (!navigatingAway) {
router.navigateBack();
}
});
} else {
var paramInfo = createParams(config.routePattern, fragment, queryString);
queueInstruction({
fragment: fragment,
queryString: queryString,
config: config,
params: paramInfo.params,
queryParams: paramInfo.queryParams
});
}
});
Make sure you import dialog into the module.
Well maybe all of that is not needed when using a trick with the activation data of your home viewmodel.
Take a look at my Github repo I created as an answer.
The idea is that the route accepts an optional activation data, which the activate method of your Home VM may check and accordingly show the desired modal.
The benefit this way is that you don't need to touch the existing Durandal plugins or core code at all.
I'm though not sure if this fully complies with your request since the requirements didn't specify anything detailed.
UPDATE:
Ok I've updated the repo now to work with the additional requirement of generalization. Essentially now we leverage the Pub/Sub mechanism of Durandal inside the shell, or place it wherever else you want. In there we listen for the router nav-complete event. When this happens inspect the instruction set and search for a given keyword. If so then fire the modal. By using the navigation-complete event we ensure additionally that the main VM is properly and fully loaded.
For those hacks where you want to navigate to #signin, just reroute them manually to wherever you want.
Expanding on my suggestion in the comments, maybe something like this would work. Simply configure an event hook on router:route:activating or one of the other similar events and intercept the activation of /#signin. Then use this hook as a way to display the dialog. Note that this example is for illustrative purposes. I am unable to provide a working example while I'm at work. :/ I can complete it when I get home, but at least this gives you an idea.
router.on('router:route:activating').then(function (instance, instruction) {
// TODO: Inspect the instruction for the sign in route, then show the sign in
// dialog and cancel route navigation.
});

Sencha Touch 2 changing views programmatically

I am very new to Sencha Touch and I'm trying to develop an app which includes a user registration view and a user login view.
The main view has a login button and a registration button.
What I am trying to accomplish is to change views in response to the buttons being tapped.
The following is the code I am using:
Ext.define('MyApp.controller.RegisterForm', {
extend: 'Ext.app.Controller',
requires: ['Ext.data.JsonP'],
config: {
refs: {
mainPage: 'mainpage',
loginForm: 'loginform',
registerForm: 'registerform'
},
control: {
'button[action=UserNewAccount]': {
tap: 'onNewAccount'
},
}
},//config
/**
* respond when new account is requested
*/
onNewAccount: function(event){
/**
* I have tried this ...
*/
this.getLoginForm().add(this.getRegisterForm());
this.getLoginForm().setActiveItem(this.getRegisterForm());
/**
* ... and this
*/
this.getLoginForm().setActiveItem({
xtype: 'registerform'
});
console.log('New account requested: ');
}
});
The onNewAccount function responds just fine. The console.log call executes. The problem is the code above that. I've tried those two different approaches with no success.
What am I doing wrong, and how can I achieve my change of views?
It seems the registerform is not a child component of your loginform. You must call the setActiveItem from the parent container.
this.getMainPage().setActiveItem(this.getRegisterForm());

ExtJS 4 prbolem with using business logic in the controller when it's triggerd from the view

I have a view designed like that:
Ext.define('MY.view.NotificationMails', {
extend: 'Ext.grid.Panel',
alias: 'widget.NotificationMailsPanel',
id: 'id-notification-mails-panel',
and I have controller for this view which is:
Ext.define('MY.controller.NotificationMailsController', {
extend: 'Ext.app.Controller',
models: [
'NotificationMailsRecord'
],
stores: [
'NotificationMailsStore'
],
views: [
'NotificationMails'
],
// refs:[{
// ref: 'notificationMails',
// selector: 'mailGrid'
// }],
init: function() {
this.control({
'#id-notification-mails-panel': {
itemclick: this.clickedSomething
}
})
},
and just to make some test and eventually find the problem a simple function definition for itemclick :
clickedSomething: function() {
console.log('Deteceted click');
}
But nothing happens. I tried many variations and still can't get my actions from the view to execute functions in the controller.
The curios thing is that I have several controllers and in most of them everything works as expected but this one and few others - I don't know - maybe I do something wrong, maybe the reason is somewhere else.
thanks
Leron
,
This is weird. I'd try the following: console.log within init() to see if the controller is initialized (you might have forgotten to include it in your app list of controllers).

Sencha Touch 2 event: painted vs show?

I have a question regarding the show event.
in my application I'm handling the painted event of my panel like this:
Ext.define('mvcTest.controller.Test', {
extend: 'Ext.app.Controller',
config: {
refs: {
panel: '#testpanel'
},
control:{
panel: {
painted: 'onPainted'
}
}
},
onPainted: function(){
alert('painted');
}
});
the docu say's, that there is also a "show" event, but it get not fired at all:
Ext.define('mvcTest.controller.Test', {
extend: 'Ext.app.Controller',
config: {
refs: {
panel: '#testpanel'
},
control:{
panel: {
show: 'onShow'
}
}
},
onShow: function(comp, obj){
alert('show');
}
});
why this does not work?
i know, alerting is the wrong way, but that's not the question.
thanks,
mike
It seems that there's no mistake in your controller. The key reason may lie in another part of your application but ... ok, according to my experience:
painted event straight-forward. Everytime your view is really rendered on the screen, painted fired up. (Note: painted event fires BEFORE the child components of your view are totally rendered. In another word, painted first, DOM generation second.)
show event does NOT necessarily fire up, especially for the initialization time of your view. show event is something fired up when you firstly somehow hide your view, and show it later.
Just experience, may be variant. But hope it might be helpful for you.
You can't handle 'painted' event in controller, because it is not bubbled up to it.
From sencha docs: This event is not available to be used with event delegation. Instead 'painted' only fires if you explicily add at least one listener to it, due to performance reason.
You can handle it by defining a listener in you panel.
Ext.define('MyApp.view.MyPanel', {
extend: 'Ext.Panel',
config: {
},
listeners: {
painted: function (element, options) {
console.log("I've painted");
}
}
});
But 'show' event can be handled in controller. Check if another part of your application see that controller.
(Have you provided reference to your controller? Is the id of your panel is right?)
I know this is an old thread, but someone might find it useful:
Sencha does not initially fire the event 'show' and 'painted'.
You need to trigger it initially.
Use this code snippet to listen to the events from the controller:
Ext.define('MyApp.view.MyPanel', {
extend: 'Ext.Panel',
config: {
},
listeners: {
painted: function (element, options) {
this.fireEvent('painted', [element, options])
}
}
});