How to set (title of) detail view with Ext navigation view - extjs

What's the proper way of setting the (title of) a detail view with an Ext navigation view?
1st approach
In the Sencha list tutorial (video) the controller does
this.getMain().push({
xtype: 'presidentdetail',
title: record.fullName(),
data: record.getData()
});
and the detail view is a regular Ext.Panel with a tpl.
2nd approach
However, the navigation view example that ships with the Sencha Touch download (code here) takes a totally different approach. Here the controller essentially does
this.showContact = Ext.create('AddressBook.view.contact.Show');
this.showContact.setRecord(record);
this.getMain().push(this.showContact);
and the detail view contains quite a bit of code I don't understand (yet)
Ext.define('AddressBook.view.contact.Show', {
extend: 'Ext.Container',
...
config: {
title: 'Information',
baseCls: 'x-show-contact',
layout: 'vbox',
items: [
{
id: 'content',
tpl: ...
},
...
},
updateRecord: function (newRecord) {
if (newRecord) {
this.down('#content').setData(newRecord.data);
..
}
}
});
Confusion
For a Sencha/Ext rookie like me this is confusing. What advantages does one approach have over the other? How are they different in what they achieve?
The first approach updates the title of the detail view (which effectively is the title in the navigation bar) in the controller. I have not yet found out how to do the same using second approach. Any hints?

Related

Global View + ViewController on ExtJs 5

What I want to achieve is very simple, I want to have a main menu all across my application, with the same functionality for all the views.
I would like to create a view that contains exclusively the menu portion plus its own viewcontroller. What would be the [best] way to achieve this?
I am using ExtJS 5 implementing the MVVM paradigm.
Thanks!
This is a pretty broad question about how to architect the app that is pretty difficult to answer without knowing more about other parts of the app.
Generally, anything application global (that is not the application container/viewport) is probably easiest to implement with MVC. Menu controller (MVC controller) would listen to menu events and then it would drill down the component hierarchy to call components' API methods to execute the actions.
I could be more specific if I knew the app.
I would create a main view, in which you define the fixed parts of the application, as well as a container with layout 'fit' to hold the changing "views". Instead of layout 'fit', this could also be a tab panel or something. Nothing prevents you from add behaviour to the fixed part of this main view using a view controller for it of course.
Pretty straightforward in fact. Then, you'll change the current app view by putting it into the main view's central container. You'll need some kind of decision logic and configuration data to define the available views in your application. This would probably best to wrap that in a single place dedicated to this very task only, that is an app controller (rather than the view controller).
Here's an example fiddle, and bellow is the reasoning explaining the different parts of the code:
So, you'd start with a view like that:
Ext.define('My.view.Main', {
extend: 'Ext.container.Container',
xtype: 'main', // you can do that in ext5 (like in touch)
// shortcut for that:
//alias: 'widget.main',
controller: 'main',
layout: 'border',
items: [{
xtype: 'panel',
region: 'west',
layout: {type: 'vbox', align: 'stretch', padding: 5},
defaults: {
margin: 5
},
items: [{
xtype: 'button',
text: "Say Hello",
handler: 'sayHello'
}]
},{
// target for app's current view (that may change)
xtype: 'container',
region: 'center',
layout: 'fit'
}]
});
Ext.define('My.view.MainController', {
extend: 'Ext.app.ViewController',
alias: 'controller.main',
sayHello: function() {
Ext.Msg.alert("Controller says:", "Hello :-)");
}
});
Then, you set this main view as the "viewport" of your application. I also add a method to change the center view. I think the Application instance is a good place for that, but you could move this method to another dedicated app controller...
Ext.application({
name : 'My', // app namespace
// in real life, Main view class would lie in another file,
// so you need to require it
views: ['Main'],
// from ext 5.1, this is the config to auto create main view
mainView: 'My.view.Main',
// we also register a ref for easy retrieval of the main view
// (the value 'main' is the xtype of the main view -- this is
// a component query)
refs: {
main: 'main'
},
setCenterRegion: function(cmp) {
// getter generated by refs config
// you could another layout in the main view, and another component query of course
var center = this.getMain().down('[region=center]');
// replace the current center component with the one provided
center.removeAll();
center.add(cmp);
}
});
So, now, you can change the view with code like this:
My.getApplication().setCenterRegion(myView);
You could wire it through the ViewController of the main view, and using it as handlers in your view. For example, in your ViewController:
changeView: function() {
// retrieve the next view, this is application specific obviously
var myView = ...
// My.getApplication() gives you the instance created by
// Ext.getApplication in namespace 'My'
My.getApplication().setCenterRegion(myView);
}
And, in your main view, use an item like this:
{
xtype: 'button',
text: "Change view (by view controller)",
handler: 'changeView'
}
That could be fine for simple applications, nevertheless that seems like mixing of concern. Deciding about application level view swapping seems more like an application controller's business. So, I would rather recommend to put the changeView method in an app controller, and exposes it to components with a component query, like this:
Ext.define('My.controller.Main', {
extend: 'Ext.app.Controller',
config: {
control: {
// components will have to match this component query
// to be animated with the change view behaviour
'#changeView': {
click: 'changeView'
}
}
},
changeView: function() {
My.getApplication().setCenterRegion(/*
...
*/);
}
});
And you would hook the behaviour to components in any view like this:
{
xtype: 'button',
text: "Change view (by app controller)",
// will be matched by the controller component query
itemId: 'changeView'
}

ExtJS 4.2.1 - Cannot get View instance from the controller

In my app I have the viewport with one item, a main view, which it is a simple class extending from Ext.container.Container.
I have a main controller too, and I'm trying to get the view instance, so dynamically I can push the corresponding items if the user is logged or not.
I've tried using views: ['MainView'], refs[ { selector: 'thextype' }], etc with no luck.
I was using the reference (ref) in sencha touch to do this kind of things, can you help me with Extjs v4.2 ?
Just for clarification, I'm not trying to get the DOM element, I'm trying to get the view instance with the associated methods.
Thanks in advance,
Define xtype of your view:
xtype: 'mainview'
and then in your controller:
requires: ['...'] // your view class
// ...
refs: [{
ref: 'mainView',
selector: 'mainview' // your xtype
}]
and then you can get the instance of your view in the controller with this.getMainView()
I've tried that without good results.
What I'm trying to do is something like. Based on your response should work
Ext.define('MyApp.view.MainView', {
extend: 'Ext.container.Container',
alias: 'widget.mainContainer',
cls: ['mainContainer'],
items: [
{
xtype: 'panel',
items: [
{
html: "my view"
}
]
}
]});
Ext.define('MyApp.controller.MainController', {
extend: 'MyApp.controller.BaseController',
refs: [
{
ref: 'mainContainer',
selector: 'mainContainer'
}
],
init: function() {
this.getApplication().on({
openDashboard: this.onOpenDashboard
});
},
onOpenDashboard: function() {
var mainContainerView = this.getMainContainer();
mainContainerView.showSomething(); //mainContainerView should be an instance of the view.
}});
Where, openDashboard event is fired if after a login success.
After some time debugging, it seems the problem was the context where it was being called the function.
I've added in the init method a line like:
var openCrmDashboardFn = Ext.Function.bind(this.onOpenCrmDashboard, this);
and it worked.
Thank you!

Sencha Touch Navigation View - Changing Title does not work on Back Button

I have a Tab Panel as my initial item inside a Navigation View. When I change tab, I'm updating the Title in the Navigation Bar via:
activeitemchange: function(container){
var navigationView = container.up('navigationview'),
navigationBar = navigationView.getNavigationBar(),
newTabTitle = value.tab._title;
navigationBar.setTitle(newTabTitle);
}
The problem is that when I push a new view onto the Navigation View, the Text for the Back Button uses the old/original Title, and not the updated Title. Clicking the Back Button also sets the Navigation View Title to the old/original Title.
The closest I got to finding a solution was this:
http://www.sencha.com/forum/showthread.php?189284-Navigation-View-Title-(IPad-App)
But I get an 'undefined' error on the navigationBar.refreshProxy() call, so I'm assuming that only works for an older version of ST.
Any help would be great,
Thanks.
I don't know if you found any answer for this question or solved it by your own since question is quite old. But I tried what you wanted to do and I managed to get the result successfully. So here's the code. I'm following MVC strictly so posting necessary files that need this to work. Basically, views and controllers.
Main.js containing TabPanel
Ext.define('SO.view.Main', {
extend: 'Ext.tab.Panel',
xtype: 'main',
requires: [
'Ext.TitleBar',
'Ext.Video'
],
config: {
tabBarPosition: 'bottom',
items: [
{
title: 'Welcome',
iconCls: 'home',
styleHtmlContent: true,
scrollable: true,
html: [
"You've just generated a new Sencha Touch 2 project. What you're looking at right now is the ",
].join("")
},
{
title: 'Get Started',
iconCls: 'action',
items: [
{
xtype: 'button',
text: 'Push a new view!',
action:'push_new_view'
}
]
}
]
}
});
Nav.js having navigation view and default item as above tab panel
Ext.define('SO.view.Nav', {
extend: 'Ext.NavigationView',
xtype: 'nav',
config:{
fullscreen: true,
items: [
{
title: 'Default View',
xtype:'main'
}
]
}
});
And finally, controller. I did every user interaction handling in controller itself.
Ext.define('SO.controller.Nav',{
extend:'Ext.app.Controller',
config:{
refs:{
views:['SO.view.Main','SO.view.Nav'],
navView:'nav',
tabView:'main'
},
control:{
tabView:{
activeitemchange:'changeTitle'
},
'button[action=push_new_view]':{
tap:'pushNewView'
}
}
},
changeTitle:function(container,value,oldValue,eOpts ){
console.log(value.title);
this.getNavView().getNavigationBar().setTitle(value.title);
},
pushNewView:function(){
var activeTabTitle = this.getTabView().getActiveItem().title;
var controller = this;
controller.getNavView().push({
title: 'Second',
html: 'Second view!'
});
controller.getNavView().getNavigationBar().getBackButton().setText(activeTabTitle);
controller.getNavView().getNavigationBar().getBackButton().on('tap',function(self){
controller.getNavView().getNavigationBar().setTitle(activeTabTitle);
});
}
}
);
As you can see, I've attached function that changes title according to selected tab in changeTitle function.
the function pushNewView pushes new view and let's you ovverride back button behavior on tap. What I did is simply, get activeItem() from tab panel which holds a button that pushes new view. Once we got activeItem we can get it's title and that title need to be set to backButtnoText. So I traverse navigation view and getBackButton instance and simply by calling setText() method changed back button text.
Same time, I've attached event handler to back button, as we do want to change navigation bar title to previous title. So once, use taps back button, we set back the title to title we got in above step. You might want to detach event handler once you're done with eveything as it might cause problems or I'd rather say it'd be good.
Just try this, it just works.
You will need to change the title of the parent viewport, not just to the navigation view tile. Basically the Navigation title is already changing by itself based on the parent view title, and all pushed component title.
var view = Ext.create('Ext.NavigationView', {
fullscreen: true,
items: [{
title: 'Change this title ',
items: [{
xtype: 'button',
text: 'Push a new view!',
handler: function() {
view.push({
title: 'Second Title',
html: 'Second view!'
});
}
}]
}]
});
It should look something like this:
activeitemchange: function(container){
var newTabTitle = value.tab._title;
container.setTitle(newTabTitle);
}
//viewAppt is the reference of the view
//Use this
viewAppts.query('#headerTitlebar')[0].setTitle('Title');
// Instead of this
this.getApptsHeaderTitlebar().setTitle('Title');

extjs change panel title on an event

I have a grid panel like this
Ext.define('sCon.view.SalesGrid',{
extend: 'Ext.grid.GridPanel',
title: 'Sales Data',
//other params
initComponent: function(){
this.callParent(arguments);
}
});
On a click event, I want to change the title of this panel. My code inside the controller looks like this.
Ext.define('sCon.controller.SalesGridController', {
extend: 'Ext.app.Controller',
views: ['SalesGrid'],
// other handlers
changeTitle: function(newTitle){
this.getView('SalesGrid').setTitle('title_changed');
}
Currently it says that it does not have a function as setTitle(). But the docs say that grid panel has setTitle() function. I also tried changing the title using the 'title' variable like
changeTitle: function(newTitle){
this.getView('SalesGrid').title = 'title_changed';
Neither works.. Please help.
UPD: Here is some refs docs from Sencha for ExtJS 4.1.
Use refs property of your controller to get references to any Components.
In your example:
Ext.define('sCon.view.SalesGrid',{
extend: 'Ext.grid.GridPanel',
title: 'Sales Data',
alias: 'widget.salesgrid',
//other params
initComponent: function(){
this.callParent(arguments);
}
});
In Controller add:
refs: [
{ ref: 'salesGrid', selector: 'salesgrid', },
]
Then you can access your SalesGrid view from anywhere in your controller like this.getSalesGrid(); method.
In Your case:
changeTitle: function(newTitle){
this.getSalesGrid().setTitle('title_changed');
}
Note
In the decribed case webbandit answer is the best for accessing a view instance, this one
stays for clarification on the use of autogenerated getters.
The selector Method you use just gives you the class and not an instance of it!
You should do it this way
this.getView('SalesGrid').create().setTitle('title_changed');
See API

ExtJs4 and its MVC: how to move all executable code from "inline" handlers to separate Controller file?

In ExtJS 4 the new feature is Model-View-Controller architecture. Yet ExtJs4 examples are made for some simplified cases and thus violating that ideology.
I have created a standard hierarchy of MVC javascript files (controller, view, model, store). In view file, I have at some point a button.
If you check the examples online, it's typically such code:
items: [
{
xtype: 'button',
styleHtmlContent: true,
text: 'Upload image',
flex: 1,
formBind: false,
handler: function() {...}
...
}]
so even ExtJS4 examples are "suggesting" to put executable part inside form definition. While MVC does require all action to reside in controller javascript file.
Another issue is the actioncolumn in a table. Below is a definition of the column in standard table configuration:
xtype: 'actioncolumn',
width: 50,
fixed: true,
altText: 'Actions',
items: [
{
icon: '/delete.png',
tooltip: 'Delete',
handler:function (grid, rowIndex,colIndex){
...}
The thing is, here handler accepts some grid-specific parameters!
Just for reminder, in extjs 4 canonical example of controller file is something like
Ext.define('MyApp.controller.mainController', {
extend: 'Ext.app.Controller',
onButtonClick: function(button, e, options) {
// alert("Button has been clicked "+button);
// TODO which button?
},
init: function() {
this.control({
"button": {
click: this.onButtonClick
}
});
}
});
My question is how to rearrange the code in order to:
1. remove handler: directives from any button on screen
2. attach actions to buttons in the controller file, including, of course, separate action for separate buttons.
3. Do the same for actioncolumn table columns, and successfully recognise which action on which row and column was triggered (that is, completely transfer function call from "handler" to controller file).
Thanks in advance, Askar
The ExtJs examples are not all up-to-date with the MVC architecture. What's more, MVC is not a compulsory design - some users might prefer not using it, so still important to show examples without MVC.
Pretty much everything you've asked is answered in this docs tutorial.
With regards to 3: I think it is some misleading from your part. Although you do define `this.onButtonClick' in your controller, the called function will still get all the parameters, just like your button handler method would - the caller component is usually the first parameter delivered with each event. You can, of course, just define more specific selectors using actions or ids in your controller. Again see the link above for an example.

Resources