Sencha Touch 2 Ext.List - programmatically add detail view - javascript

I’m trying to add different detail view based on taped item in list.
I have home screen that is using List component and this view is displaying ['Real estate', 'Vehicles', 'Jobs']... as menu items.
Based on selected item in list, I want to display different view.
And I want to follow MVC design pattern..
Here is some code...
App.js
Ext.application({
name: 'App',
controllers: ['Main'],
views: ['Viewport', 'HomePage'],
stores: ['HomePageItems'],
models: ['HomePageItem'],
launch: function () {
Ext.Viewport.add(Ext.create('App.view.Viewport'));
}
});
Viewport.js
Ext.define("App.view.Viewport", {
extend: 'Ext.navigation.View',
requires: [ 'App.view.realestate.Realestate',
'App.view.HomePage',
'App.view.jobs.Jobs',
'App.view.other.Other',
'App.view.vehicles.Vehicles'
],
config: {
items: [
{
xtype: 'homepage'
}
]
}
});
HomePage.js ( xtype = "homepage" )
Ext.define('App.view.HomePage', {
extend: 'Ext.List',
xtype: 'homepage',
id: 'homepage',
config: {
title: 'Oglasi',
itemTpl: '<strong>{name}</strong><p style="color:gray; font-size:8pt">{description}</p>',
store: 'HomePageItems',
onItemDisclosure: true
}
});
Main.js
Ext.define('App.controller.Main', {
extend: 'Ext.app.Controller',
config: {
refs: {
main: '#homepage'
},
control: {
'homepage': {
disclose: 'HookUpDetailView'
}
}
},
HookUpDetailView: function (element, record, target, index, ev) {
// TO DO: I have 4 differente views to load programmaticaly based on selected item in List
//'App.view.realestate.Realestate'
//'App.view.jobs.Jobs'
//'App.view.other.Other'
//'App.view.vehicles.Vehicles'
}
});
I found one example, but it's not working for me (push method doesn't exist)
this.getMain().push({
xtype: 'realestatehome'
});
Thank in advance!

The method you're looking for is
http://docs.sencha.com/touch/2-0/#!/api/Ext.Container-method-add
this.getMain().add({xtype: 'realestatehome'});
But what you have doesnt make sense, realestatehome is a list, you can't add a component under it. You need to read about layoutsFrom the link above

Push should work. You could try something like this.
HookUpDetailView: function (list, record) {
if(record.data.description==='real estate'){
this.getRealEstate().push({
xtype: 'realestatehome',
title: record.data.description,
data. record.data
});
if(record.data.description==='Vehicles'){
this.getVehicles().push({
xtype: 'vehicles',
title: record.data.description,
data. record.data
});
}
}
And a particular view could look like
Ext.define('App.view.RealEstateHome', {
extend: 'Ext.Panel',
xtype: 'realestatehome',
config: {
styleHtmlContent: true,
scrollable: 'vertical',
tpl: [
'{description}, {example}, {example}, {example}'
]
}
});
And the refs to access your particular view should look something like
refs: {
realEstate: 'realestatehome',
vehicles: 'vehicleshome'
},
Hope that helps

I made a mistake in controller this.getMain()
get main is returning Ext.List and I need Ext.navigation.View that have 'push' method.
So... I added xtype and id to my viewport ("container")
and quick change in my controller solved my troubles...
refs: {
main: '#homepage',
container: '#container'
}
and instead of getting Ext.List object
this.getContainer().push({
xtype: 'realestatehome'
});
And this work like a charm :)

Related

How to control the scope of event listeners in the extjs app controller

Ext JS - v 6.2.1
I'm interested in reusing a main component developed in ExtJs which i've written in different tabs of the tabpanel. The main component is composed of two child components and each of these components have their respective controllers and the child components interact among themselves. Since the event listeners are added in the controller domain the events fired in one instance of tab affects the other tabs as well.
Pseudo code of the scenario explained
*********** Views ***********
Ext.define('MainApp.view.main.Main', {
extend: 'Ext.tab.Panel',
xtype: 'mainapp-main',
controller: 'main',
...
items: [{
title: 'Main Component Instance 1',
closable: true,
items: [{
xtype: 'mainapp-maincomponent'
}]
}, {
title: 'Main Component Instance 2',
closable: true,
items: [{
xtype: 'mainapp-maincomponent'
}]
}]
});
Ext.define('MainApp.view.maincomponent.MainComponent', {
extend: 'Ext.panel.Panel',
xtype: 'mainapp-maincomponent',
controller: 'maincomponent',
...
items: [{
xtype: 'mainapp-component1'
},{
xtype: 'mainapp-component2'
}]
});
Ext.define('MainApp.view.component1.Component1', {
extend: 'Ext.panel.Panel',
xtype: 'mainapp-component1',
controller: 'component1',
...
items: [{
xtype: 'button',
cls: 'contactBtn',
scale: 'large',
text: 'Fire Event',
handler: 'onComponentButtonTapped'
}]
});
Ext.define('MainApp.view.component2.Component2', {
extend: 'Ext.panel.Panel',
xtype: 'mainapp-component2',
controller: 'component2',
...
items: [{
xtype: 'textfield',
value: 'Button is not clicked yet',
width: 500,
readOnly: true
}]
});
*********** Controllers ***********
Ext.define('MainApp.view.component1.Component1Controller', {
extend: 'Ext.app.ViewController',
alias: 'controller.component1',
onComponentButtonTapped: function (btn, eventArgs) {
this.fireEvent('component1ButtonTapped', btn, eventArgs);
}
});
Ext.define('MainApp.view.component2.Component2Controller', {
extend: 'Ext.app.ViewController',
alias: 'controller.component2',
listen: {
controller: {
'component1': {
component1ButtonTapped: 'onComponent1ButtonTapped'
}
}
},
onComponent1ButtonTapped: function(){
this.getView().down('textfield').setValue(Ext.String.format('Button tapped at {0}', Ext.Date.format(new Date(), 'Y-m-d H:i:s')));
}
});
Can somebody please suggest the correct way of addressing this used case.
More Details
The tab panel is loaded in viewport
Tab 1 has first instance of Main Component - M1
Tab 2 has second instance of Main Component - M2
Every instance of Main Component has two child components
Component1 - M1C1 > M1C1 View and M1C1 Controller
Component2 - M1C2 > M1C2 View and M1C2 Controller
Similarly for the second instance of Main Component
Component1 - M2C1 > M2C1 View and M2C1 Controller
Component2 - M2C2 > M2C2 View and M2C2 Controller
Requirement here is to restrict Actions done on M1C1 view should be processed by M1C2 Controller only.
Issue is that with the code above M2C2 Controller also listens to the event
Change handler: 'onComponentButtonTapped' to
{
xtype: 'button',
cls: 'contactBtn',
scale: 'large',
text: 'Fire Event',
listeners: {
click: 'onComponentButtonTapped',
scope: SCOPE-YOU-WANT
}
}
First, I believe you are overnesting in your MainApp.view.main.Main component and you don't use a layout for your overnesting either. That can lead to unnecessary consequences like extra layout runs and bloated DOM affecting performance.
Ok, onto your question! I don't think scope is what you're actually after here. I see it as how to properly architect your ViewControllers. While understanding this isn't your real code, I will say that in this example you don't need that Component1Controller controller. Get rid of it and the button's handler will get resolved up to the maincomponent controller which is where you can control both child items. I only say this fully knowing that your Component1Controller is likely doing other things so it's not going anywhere just to say that not all containers need controllers. It also serves what I would do in this case if Component1Controller is to stick around. Instead of firing an event on the Component1Controller instance and use the event domain to get to Component2Controller, I would fire a view event on Component1 and add a listener on your config object so that maincomponent controller gets the event to do it's thing.
That sounds messy and hard to follow so I created this fiddle.

ExtJS Grid - how do I reference a store by id in my view

I am trying to reference a store in my app for the purpose of adding a paging tool bar at the bottom of my gird. In most examples I have studied the store is referenced by variable, ex: store: someStore. However, by I have build my app a little differently and did create a reference variable to the store. I have
tried assigning an id but this did not work.
Here is what I have:
In my view Grid.js:
Ext.define('myApp.view.user.Grid', {
extend: 'Ext.grid.Panel',
viewModel: {
type: 'user-grid'
},
bind: {
store: '{users}',
},
columns: {...},
//my paging tool bar
dockedItems: [{
xtype: 'pagingtoolbar',
dock: 'bottom',
store: 'girdStore'
//store: {users} -> did not work
}],
...
});
In my view model GridModel.js:
Ext.define('myApp.view.user.GridModel', {
extend: 'Ext.app.ViewModel',
requires: [
'myApp.model.User'
],
stores: {
users: {
model: 'myApp.model.User',
storeId: 'gridStore',
autoLoad: true
}
},
formulas: {...}
});
When I try to reference the {users} store by id 'gridStore' I get this error:
Uncaught TypeError: Cannot read property 'on' of undefined
What is the best way to proceed without completely refactoring my model?
When you have a reference to your grid, you could get the store by calling the getStore function. See the ExtJs 6.2.1 documentation.
var grid; // reference to your grid
var store = grid.getStore();
You can create the store in initComponent and then attach it to the dockedItems, so both will share the same store.
initComponent: function () {
var store = Ext.create('Ext.data.Store', {
model: 'myApp.model.User',
storeId: 'gridStore',
autoLoad: true
});
this.store = store;
this.dockedItems = [{
xtype: 'pagingtoolbar',
dock: 'bottom',
store:store
}];
this.callParent(arguments);
}
The initComponent is called once when a new instance of the class is created, see the description in the documentation.
...It is intended to be implemented by each subclass of
Ext.Component to provide any needed constructor logic. The
initComponent method of the class being created is called first, with
each initComponent method up the hierarchy to Ext.Component being
called thereafter. This makes it easy to implement and, if needed,
override the constructor logic of the Component at any step in the
hierarchy. The initComponent method must contain a call to callParent
in order to ensure that the parent class' initComponent method is also
called...
The view with the initComponent function.
Ext.define('myApp.view.user.Grid', {
extend: 'Ext.grid.Panel',
viewModel: {
type: 'user-grid'
},
initComponent: function () {
var store = Ext.create('Ext.data.Store', {
model: 'myApp.model.User',
storeId: 'gridStore',
autoLoad: true
});
this.store = store;
this.dockedItems = [{
xtype: 'pagingtoolbar',
dock: 'bottom',
store: store
}];
this.callParent(arguments);
},
columns: {...},
...
});

Sencha Touch 2: data intigration or how to share dynamic information between sencha and javascript

I'd like to start quick.
What is my problem:
Within ST2 I structured my application with the MVC pattern. I have a store, a model, a controler and the views (for more information scroll down).
Workflow:
I click a list item (List View with a list of elements from store)
Controller acts for the event 'itemtap'
Controller function is looking for main view and pushes a detail view
Record data will be set as data
Detail view uses .tpl to generate the output and uses the data
Problem
Now I want to add a button or link to enable audio support.
I thought about a javascript function which uses the Media method from Phonegap to play audio
and I want to add this functionality dynamicly within my detail view.
Do you have any idea how I can achive that behavoir? I'm looking for a typical "sencha" solution, if there is any.
Detail Overview of all files starts here
My list shows up some data and a detail view visualize further information to a selected record.
The list and the detail view a collected within a container, I'll give you an overview:
Container:
Ext.define('MyApp.view.ArtistContainer', {
extend: 'Ext.navigation.View',
xtype: 'artistcontainer',
layout: 'card',
requires: [
'MyApp.view.ArtistList',
'MyApp.view.ArtistDetail'
],
config: {
id: 'artistcontainer',
navigationBar: false,
items: [{
xtype: 'artistlist'
}]}
});
List
Ext.define('MyApp.view.ArtistList', {
extend: 'Ext.List',
xtype: 'artistlist',
requires: [
'MyApp.store.ArtistStore'
],
config: {
xtype: 'list',
itemTpl: [
'<div>{artist}, {created}</div>'
],
store: 'ArtistStoreList'
}
});
Detail View
Ext.define('MyApp.view.ArtistDetail', {
extend: 'Ext.Panel',
xtype: 'artistdetail',
config: {
styleHtmlContent: true,
scrollable: 'vertical',
title: 'Details',
tpl: '<h2>{ title }</h2>'+
'<p>{ artist }, { created }</p>'+
'{ audio }'+
'',
items: [
//button
{
xtype: 'button',
text: 'back',
iconCls: 'arrow_left',
iconMask: true,
handler: function() {
var elem = Ext.getCmp("artistcontainer");
elem.pop();
}
}
]
}
});
And finally the controller
Ext.define('MyApp.controller.Main', {
extend: 'Ext.app.Controller',
config: {
refs: {
artistContainer: 'artistcontainer',
},
control: {
'artistlist': {
itemtap: 'showDetailItem'
}
}
},
showDetailItem: function(list, number, item, record) {
this.getArtistContainer().push({
xtype: 'artistdetail',
data: record.getData()
});
}
});
Puh, a lot of stuff to Read
Here you can see an example of how to load audio from an external url with Sencha Touch "Audio" Component. Haven't work with it but I think it fits your needs. Declaring it is as simple as follows:
var audioBase = {
xtype: 'audio',
url : 'crash.mp3',
loop : true
};
Iwould reuse the component and load the songs or sound items by setting the url dynamically. By the way I tried it on Chrome and Ipad2 and worked fine but failed on HTC Desire Android 2.2 default browser.

Defining items of an object inside the initialize function

I am trying to create items inside a component as it gets initialized, with a function.
Consider the following:
Ext.define('mobi.form.Login',{
extend:'Ext.form.Panel',
config:{
items: [{
xtype: 'textfield',
name: 'Name',
label: 'Name'
}]
});
Ext.application({
viewport: {
layout:'fit'
},
launch: function(){
Ext.Viewport.add(Ext.create('mobi.form.Login'));
}
})
I am trying to get The mobi.form.login to generate its config from a function that runs on initialize ( or whatever I can use to over write the config I specify ).
I know Sencha touch 2 has the constructor, and initialize function, but both of them seem to have arguments=[] ( eg an empty array )
This is more or less how it would look if I was doing it in ExtJS 4.x:
Ext.define('mobi.form.Login',{
extend:'Ext.form.Panel',
initComponent:function(config){
config=Ext.apply({}.config,{});//make sure config exists
config.items= [{
xtype: 'textfield',
name: 'Name',
label: 'Name'
}]
Ext.apply(this, config);
this.callParent(arguments);
}
});
If you ever wanted to do this, you could use constructor or initialize.
Constructor you would use for synchronous logic which will be fast and you want to happen before the component is initialized. You can access the configuration through the constructors first argument:
Ext.define('MyComponent', {
extend: 'Ext.Component',
constructor: function(config) {
console.log(config);
this.callParent([config]);
}
});
Ext.application({
launch: function(){
Ext.create('MyComponent', { test: 1 })
// Will log out:
// {
// test: 1
// }
}
});
Remember you will always need to callParent with the config/arguments within constructor.
In any other situation, you should use initialize which is called after all the config's have been... initialized. :) We use this a lot internally for adding listeners.
initialize: function() {
this.on({
...
});
}
you don't need to call initialize manually it is already done by constructor and when calling this function you can access items data using this.items and create panel items there
Ext.define('mobi.form.Login',{
extend:'Ext.form.Panel',
config: {
items: []
},
initialize : function()
{
this.items = [Ext.create({
xtype: 'textfield',
name: 'Name',
label: 'Name'
})];
this.callParent();
}
});
Ext.application({
viewport: {
layout:'fit'
},
launch: function(){
Ext.Viewport.add(Ext.create('mobi.form.Login'));
}
})
Use the following:
Ext.apply(this, {
items: [
....
]
});
Have you tried something similar to this? I'm just passing a config object to Ext.create, though I can't test it right now. See http://docs.sencha.com/touch/1-1/#!/api/Ext-method-create
Ext.Viewport.add(Ext.create(
{
xtype: 'mobi.form.Login',
items: [ /*A list of items*/ ]
}
));
You could stick this snippet in its own function as well, one that takes in items as a parameter. Hope this solves your problem!

Defining a Panel and instantiating it in a viewport

I am trying to get Ext.define & Ext.create working in Sencha touch 2, so that I can define everything in my library and just create stuff pre-configured.
However, Ext.define is not doing what I would expect it to in anything I've tried.
Why does the following code not create a panel inside the viewport with the field label "Tame"?
Ext.define('mobi.form.Login',{
extend:'Ext.form.Panel',
items: [{
xtype: 'textfield',
name: 'Tame',
label: 'Tame'
}
]
});
Ext.application({
viewport: {
layout:'fit'
},
launch: function(){
var form = Ext.create('Ext.form.Panel', {
items: [{
xtype: 'textfield',
name: 'name',
label: 'Name'
}
]
});
Ext.Viewport.add(Ext.create('mobi.form.Login')); // This doesnt add anything to the viewport
Ext.Viewport.add(form); //magically this works
}
})
When using Ext.define, all configurations must go inside the config block. So your code should look like this:
Ext.define('mobi.form.Login',{
extend:'Ext.form.Panel',
config: {
items: [{
xtype: 'textfield',
name: 'Tame',
label: 'Tame'
}
]
}
});
In general the only exceptions to this are:
extend
requires
xtype
singleton
alternateClassName
Anything else should be inside the config object, but remember, only when using Ext.define.
It looks like you are trying to use the sencha MVC concept but this is wrong if this is your only piece of code.
First create the following folder structure:
MyAppFolder
index.html (include the sencha lib here)
app.js (main file)
app (folder)
controller
Main (main controller)
model (optional if no model is defined)
store (optional if no model is defined)
view
Viewport.js (your main viewport)
resources
css
style.css (your custom style)
images (your icons and images if you have)
Then in your app.js you would define something like this:
// enable loader for dynamic loading of .js classes
Ext.Loader.setConfig({
enabled : true,
paths : {
}
});
/**
* Better performance is achived when knowing which .js classes we need to load prior to execution of this class.
*/
Ext.require([
]);
/**
* This is the definition of our mobile application.
* The name of this app is MVCTest.
*/
Ext.application({
name : 'MVCTest',
controllers : ['Main'],
views : ['Viewport'],
launch : function() {
Ext.create('MVCTest.view.Viewport');
}
});
Then your main controller:
Ext.define('MVCTest.controller.Main', {
extend : 'Ext.app.Controller',
config : {
refs : {
viewport : 'mvctest-viewport'
}
}
});
Then your viewport would look something like this, according to your example:
Ext.define('MVCTest.view.Viewport', {
extend : 'Ext.Container',
xtype : 'mvctest-viewport',
config : {
fullscreen : true,
layout : 'card',
items:
[
{
xtype: 'textfield',
name: 'name',
label: 'Name'
},
{
xtype: 'mvctest-tame'
}
]
}
});
By specifying the xtype mvctest-tame it will search for this xtype and add this in as a new item to this card. So you need the tame view:
Ext.define('MVCTest.view.Login',{
extend:'Ext.form.Panel',
xtype: 'mvctest-tame',
items: [{
xtype: 'textfield',
name: 'Tame',
label: 'Tame'
}
]
});
And do not forget to add the Login view to the app.js..

Categories