In this chapter, we are going to implement the multilingual capability of the system. This feature will allow the system to display the translation of the labels according to the language selected by the user (using some HTML5 features as well).
We will also learn how to implement the logout capability so that the user can end the session, and also for security reasons, we will learn how to implement a session timeout warning for the user, in the case of inactivity (not using the mouse or keyboard for a while).
Also, after the user is authenticated, we need to display the application. In this chapter, we will learn how to implement the base of the application.
So, in this chapter, we will cover:
While we cover all the application capabilities, we will cover some Ext JS components as well.
When we implemented the success
function in the Submit button listener on the login controller, we mentioned the Packt.view.main.Main
class. We are going to reuse this class (it was automatically created by Sencha Cmd when we created the project) as the base of our application. Before we start with the hands-on approach, let's take a look at what is going to be the result of the application by the end of this chapter:
Whenever we construct an application entirely with Ext JS (because we do have the option of using a single component rendered to a <div>
tag if we want to, in a manner similar to what is done in jQuery), we need to use a component that is going to be the base of the application. This component is the Viewport. The Viewport is a specialized container representing the viewable application area (the browser viewport). The Viewport renders itself to the document body, and automatically sizes itself to the size of the browser viewport and manages window resizing. There might only be one Viewport created in an application.
Before we create the Viewport, if we click on the Submit button of the Login screen, we will see a grayish screen, even though we are calling Ext.create('Packt.view.main.Main'),
inside the LoginController
class. This means Packt.view.main.Main
is being created, but nothing is being displayed on the screen. This is because the Main
class is not being rendered as a child of any component, and it is also not being rendered to the HTML body. But we are going to change this behavior by changing it to a Viewport.
Open the app/view/main/Main.js
file. In the second line of code, you will find the following snippet:
extend: 'Ext.container.Container'
The Main
class that was created by Sencha Cmd is extending the Container
component. The Container
component is the simplest container component in the Ext JS API. It supports adding and removing items to it, and it is also the parent class to many other components, such as Panel, Window, and TabPanel. We are going to change Ext.container.Container
to Ext.container.Viewport
so that we can use the Main
class as the base class of our application. Save the code, refresh the browser, and give it a try. The next time you click on the Submit button, you should see the original code created by Sencha Cmd after you are logged in.
Extending the Ext.container.Viewport
class is the classic and traditional way of having a Viewport in Ext JS applications. Ext JS 5 introduces a new way of using a Viewport by using the Viewport plugin (Ext.plugin.Viewport
).
To learn more about Ext JS plugins, please read http://www.sencha.com/blog/advanced-plugin-development-with-ext-js/.
To use the plugin, first roll back the changes we made in the preceding topic (the Main
class will continue to extend Ext.container.Container
) and add the following code after the extend
code:
Ext.define('Packt.view.main.Main', {
extend: 'Ext.container.Container',
plugins: 'viewport',
xtype: 'app-main',
Using the plugins
configuration has the same result as extending the Viewport
class. This plugin transforms any component into a Viewport, making it fill all the available space in the browser. The advantage of this plugin is that we can still reuse this class in other contexts, for example, inside a window.
We know that the ptype (plugin type) of the Viewport plugin is viewport by accessing the documentation:
As we learned in Chapter 1, Sencha Ext JS Overview, the Border layout can be used to organize the children of a parent container into five regions: north, south, west, east, and center.
The center region is the only one that is mandatory to have. The other ones are optional. Looking at the following screenshot, we can see that we are going to organize our main screen into four regions: center, north, south, and west:
Let's take a look at the items
configuration of the Main
class (you can replace the code that was generated by Sencha Cmd with the following code):
items: [{ region: 'center', // #1 xtype: 'mainpanel' },{ xtype: 'appheader', // #2 region: 'north' },{ xtype: 'appfooter', // #3 region: 'south' },{ xtype: 'container', // #4 region: 'west', width: 200, split: true }]
In the center
region, we have mainpanel
(#1
). In Chapter 5, Advanced Dynamic Menu, we are going to create a dynamic menu that will give the options to the user to open the screens the user is entitled to. Each screen the user opens will be created as a tab in mainpanel
. We will create it in a minute.
In the north
region, we have the header (#2
), and in the south
region, we have the footer (#3
). We will also work on them in a minute.
In the west
region, we have container
(#4
), which we will use in the next chapter to render the dynamic menu. For now, we will leave the space reserved for it.
It is important to know that for the center
region, we do not need to specify width
or height
. The container that is being rendered in the center
region is going to use whatever space is left in the Border layout. For the south
and north
regions, you are required to specify height
. We will do this when we create Header
and Footer
. The south
and north
regions are going to use all the available horizontal space that is available in the screen—limited by height
—which is why width
is not required. For the west
and east
regions, it is required to specify width
. As we are only using the west
region, we specified 200
pixels (#4
).
We need to create the mainpanel
component that we are using in the center
region of the Main
class. To do so, we are going to create a new file named Panel.js
inside the app/view/main
folder, and we are going to write the following code inside it:
Ext.define('Packt.view.main.Panel', { // #1 extend: 'Ext.tab.Panel', // #2 xtype: 'mainpanel', // #3 activeTab: 0, // #4 items: [ { xtype: 'panel', // #5 closable: false, // #6 iconCls: 'fa fa-home fa-lg tabIcon', // #7 title: 'Home' // #8 } ] });
As usual, we will start with the name of the class. The class name convention is app namespace + folder (inside app
) + filename (without the .js
extension), which will result in Packt.view.main.Panel
(#1
). The main.Panel
class is extending the TabPanel component (#2
).
In line #3
, we have xtype
of the main.Panel
class. This is the xytpe
class we used to instantiate this class inside the Main
class. In line #4
, we have the activeTab
configuration. When we set a tab to active, the TabPanel component is going to display the contents of the tab, and it is also going to highlight it.
As we learned in Chapter 1, Sencha Ext JS Overview, the TabPanel component is a container that has tabs as children organized by the Card layout, which means that the user will see the content of one active tab at a time. Each child declared inside the items
configuration is an instance of the tab
class (Ext.tab.Tab
), and it can be of any type as we have in line #5
. So as not to display an empty screen, we are displaying a 'Home'
tab (#8
) that is a panel (#5
), which means it can have toolbars and other components inside it as well. This Home
tab cannot be closed (#6
); otherwise, the user will see a blank space in the middle of the main screen, and we do not want that. We are also setting a Font Awesome icon in the format of home
in line #7
for it to look prettier.
You can use this Home tab to display announcements or to behave like a dashboard, where the user will see a summary of all the pending tasks.
The next step is creating the footer of the main screen. We are going to create a new file named Footer.js
inside app/view/main
with the following code inside it:
Ext.define('Packt.view.main.Footer', { extend: 'Ext.container.Container', //#1 xtype: 'appfooter', //#2 cls: 'app-footer', //#3 height: 30, //#4 layout: 'center', //#5 items: [ { xtype: 'component', //#6 width: 350, //#7 componentCls: 'app-footer-title', //#8 bind: { html: '{footer}' //#9 } } ] });
Footer
is going to extend from the Container
class (#1
). The Container
class is the lightest component that we can create that can have items inside it. We should give preference to use it whenever possible. We will discuss this in greater detail later. Then, we declare the xtype
class (#2
), which is the alias we are using to instantiate this class in the Main
class.
If we look at the first image of this chapter, we will note that the footer has a top border. We add a style on line #3
that adds this border to the footer. The cls
configuration allows us to add extra CSS to a component in Ext JS. It is available to all components. We will add the style to our CSS in a minute.
As we are declaring Footer
in the south region of the Main
class, you are required to set the height
parameter. We can do this inside the Footer
class or inside the Main
class when declaring the south region. In this case, we are setting it inside the Footer
class (#4
).
Inside the Footer
class, we want to have only one component in this example, which is text (you can use a copyright message), and we are going to display it using HTML. We also want this text to be centered. For this reason, we can use the center
layout (#5
). To use the center
layout, the parent container needs to have only one child component (because it inherits from the fit
layout, which only supports a single child as well). It is also required to declare the width
parameter of the child component (#7
); in this case, the text we are going to display is approximately 350
pixels wide.
In Ext JS 4, the Center layout was used as a UX plugin shipped with the Ext JS SDK. In Ext JS 5, this layout was promoted to the native API, but it kept backwards compatibility. In the layout configuration, you can use center
(introduced in Ext JS 5) and also keep using ux.center
(from Ext JS 4) if you are migrating an application from Ext JS 4 to 5.
To render the HTML, we are going to use the lightest and simplest component as possible, which is component
(#6
). As we also want to apply some CSS to our text, we are going to use the componentCls
(#8
) class, which is a CSS class that is added to a component's root-level element.
Note that there is no text declared anywhere inside the Footer
class. Instead, we are binding the html
configuration to a value (#9
) named footer
. This is also part of the new MVVM architecture that we are also going to use in this chapter. In the preceding chapter, we only used the View
and ViewController
classes for the login capability. In this chapter, we are going to use the complete feature: View, ViewController, and ViewModel from the MVVM architecture (Sencha Cmd already generated these classes when we created the project, so we better reuse them!). For now, keep in mind that this is part of the ModelView binding that we will dive into in the next topic.
Let's discuss another way of adding CSS to our application. We already know that the best practice to add CSS to our application is to add using Sass inside the sass/etc
folder as we did in the previous examples. However, there are some styles that we create to apply to specific components, and we are not going to reuse them throughout the application. Instead of adding these CSS styles to our all.scss
file and having a big file that can give us a headache later if we need to maintain it, we can use a more modular CSS approach to create specific CSS for our Ext JS views.
Inside the sass
folder, create a new folder named src
(if it has not been created by Sencha Cmd automatically), and inside src
, create a new folder named view
. Inside view
, create a new folder named main
. We will have the directory sass/src/view/main
. Inside this directory, create a file named Footer.scss
with the following content inside it:
$packt-footer-text-color: rgb(11, 103, 196); //#1 .app-footer-title { color: $packt-footer-text-color; //#2 font-size: 12px; font-weight: bold; } .app-footer { border-top: 1px solid darken($packt-footer-text-color, 15); //#3 }
In line #1
, we declare a Sass variable with a bluish color (the same blue color as the TabPanel
background). We are reusing this variable in lines #2
and #3
in the styles we created to use in our Footer
class.
In line #3
, we use the darken
function from Sass, which accepts a color and a number from 0-100, the percentage to which we want to make the color darker. For more information, please refer to the Sass documentation at http://goo.gl/JsAnVz.
The view/main/Footer.scss
file has the same path as the view/main/Footer.js
file. Note that this way, it is easier to maintain the styles specific to the Footer
class. We will do the same for the Header
class in the next topic. We separate the CSS into modules for it to be easier to read and maintain, and when we do the build, all the CSS will be concatenated into a single production CSS file—this is called modular CSS. See, developing applications with Ext JS is not all about Ext JS; we can apply the knowledge from other frontend technologies as well!
Next, we are going to create the Header
class. The Header
class contains the logo of the application along with the application name, the dropdown that offers the translation capability, and the Logout button. To create the header, we are going to create a new file, Header.js
, inside the app/view/main
folder with the following code:
Ext.define('Packt.view.main.Header', { extend: 'Ext.toolbar.Toolbar', //#1 xtype: 'appheader', //#2 requires: [ 'Packt.view.locale.Translation' //#3 ], ui: 'footer', //#4 items: [{ xtype: 'component', //#5 bind: { //#6 html: '{appHeaderIcon}' } },{ xtype: 'component', componentCls: 'app-header-title', //#7 bind: { //#8 html: '{appName}' } },{ xtype: 'tbfill' //#9 },{ xtype: 'translation' //#10 },{ xtype: 'tbseparator' //#11 },{ xtype: 'button', //#12 itemId: 'logout', //#13 text: 'Logout', reference: 'logout', //#14 iconCls: 'fa fa-sign-out fa-lg buttonIcon', //#15 listeners: { click: 'onLogout' //#16 } } ] });
Our Header
class is going to extend the Toolbar
class (#1
). The Toolbar
class is usually used inside panels and its subclasses (grid, form, tree) to organize buttons, but it can also be used to hold other components as we are going to do in this example. We will cover more about toolbars in other chapters as well. We are also declaring an xtype
class for the Header
class that we are making a reference to in the Main
class (#2
).
Whenever we use an xtype
class created by ourselves, Ext JS does not understand what component we are trying to instantiate. For this reason, we require the class we are referring to. For example, in line #3
, we reference the class of the translation component we are instantiating using its xtype
class in line #10
. We are going to develop this component later in the chapter.
The ui
configuration allows us to use a specific theme for a component. The Toolbar
component has the configuration ui: 'footer'
(#4
), which gives the toolbar a transparent background. The footer
value is included in the Ext JS SDK, and it makes the toolbar transparent. We are going to create some custom ui
configurations when we discuss themes later in this book.
The two first children items of the Header
class are the icon (#5
) and the application name. For the icon, we are going to use Font Awesome to display an icon in the format of a desktop. For the title of the application, we will use the same approach we used in the case of the Footer
class. We are also using a componentCls
configuration (#7
) to style it. We get both values (icon-#6
and application name-#8
) from the ViewModel, which we will cover in a minute.
The next item is a toolbar fill (#9
). This component will align the translation
(#10
) and logout
buttons (#13
) to the right, filling the Toolbar
class with space in the middle (between the application title and the buttons).
We also have the logout
button declared on line #12
. We are going to assign itemId
so that we can reference this button globally by the application. We will need it when we work with the session monitor capability. The itemId
needs to be unique in its scope; in this case, it needs to be unique within this class, but it is going to be even better if it is unique at the application level.
As we are going to use the ViewController
class to handle the logout, we will declare a reference
(#14
) to make it easier to retrieve the button reference inside the ViewController
class, and we are also going to declare the listener (#16
), which means that the onLogout
function from the ViewController
class is going to be executed when we click on the Logout button.
We are also setting an icon to the Logout
button from Font Awesome (#15
). By default, the icon will have the color black. The button text is white, and we want the icon to have the same color as the text. For this reason, we add a custom style (buttonIcon
).
At last, we have the toolbar separator declared on line #11
. This simply adds a separator ("|") between the buttons.
Just as we did with the Footer
class, we are also going to create a filename, Header.scss
, inside the sass/view/main
folder with the following content inside it:
$packt-header-text-color: #2a3f5d; .app-header-logo { color: $packt-header-text-color; } .app-header-title { padding: 5px 0 5px 3px; color: $packt-header-text-color; font-size: 18px; font-weight: bold; }
The app-header-logo
was created to customize the color of the icon to be the same as the application title. We are using the Sass variable inside both styles. The text color of the title is a dark blue.
By default, the Font Awesome icons will be displayed in black. But we want some of the icons to have the same color as our theme. We can do this customization using CSS as well.
We have declared two styles to customize the Font Awesome icons until now. The first one was inside the Panel
class (tabIcon
), and the second one is the Logout button (buttonIcon
). So we also need to add these styles to our CSS. To follow the modular CSS approach, let's create a new file, iconColors.scss
, under sass/etc
with the following content:
$packt-button-icon-color: #fff; $packt-tab-icon-color: rgb(11, 103, 196); .tabIcon { color: $packt-tab-icon-color; } .buttonIcon { color: $packt-button-icon-color; }
Then, all we need to do is import this file in the all.scss
file:
@import "fontAwesome/font-awesome";
@import "iconColors";
Now, it's time to put everything together. Let's take a look at how the Main
class looks with the complete code:
Ext.define('Packt.view.main.Main', {
extend: 'Ext.container.Container',
plugins: 'viewport',
xtype: 'app-main',
requires: [ //#1 'Packt.view.main.Header', 'Packt.view.main.Footer', 'Packt.view.main.Panel', 'Packt.view.main.MainController', 'Packt.view.main.MainModel' ],
controller: 'main', //#2
viewModel: {
type: 'main' //#3
},
layout: {
type: 'border'
},
items: [{
region: 'center',
xtype: 'mainpanel'
},{
xtype: 'appheader',
region: 'north'
},{
xtype: 'appfooter',
region: 'south'
},{
xtype: 'container',
region: 'west',
width: 200,
split: true
}]
});
We need to add the requires
declaration of all the classes we created (and we are referencing them by their xtype
classes) and also the Main
ViewModel and Main
ViewController class that were already created by Sencha Cmd.
The controller
(#2
) and viewModel
(#3
) declarations were already added by Sencha Cmd when we created the project. We are simply reusing them by referencing their types.
If we open the MainModel.js
file inside the app/view/main
folder, we will see some content inside it already. We are going to add more content to it, and the file is going to look as follows:
Ext.define('Packt.view.main.MainModel', { //#1 extend: 'Ext.app.ViewModel', //#2 alias: 'viewmodel.main', //#3 data: { name: 'Packt', //#4 appName: 'DVD Rental Store', //#5 appHeaderIcon: '<span class="fa fa-desktop fa-lg app-header-logo">', //#6 footer: 'Mastering ExtJS book - Loiane Groner - http://packtpub.com' //#7 } });
Let's start with the name of the class (#1
). The naming convention for ViewModel suggested by Sencha is the name of the view (Main
) + "Model
", resulting in MainModel
. A ViewModel extends from the ViewModel
class (#2
) introduced in Ext JS 5, along with the MVVM architecture. The alias (#3
) of a ViewModel is defined by "viewmodel.
" + the name of the type we want to assign. In this case, Sencha already created this class for us with the type main
. That is why we can reference this alias in the View
class using the following code:
viewModel: { type: 'main' }
The data
configuration allows us to populate values in the ViewModel
class. The name
field (#4
) was created by Sencha Cmd, so we are going to keep it (you can remove it if you want). The appName
(#5
) and appHeaderIcon
(#6
) properties are being used by Header
and footer
(#7
) is being used by the Footer
class.
MainModel
is bound to the Main
class (View). Because Header
and Footer
are items
of the Main
component, they can also reference MainModel
.
This is the simplest way in which we can create the ViewModel
class, with prepopulated data. We will provide other advanced examples throughout this book, but we need to start with baby steps!
For more information about ViewModel, data binding, and how to bind different data types, please read the following Sencha guide: http://goo.gl/qta6kH.