How to call other view in Sencha Touch - extjs

How can I call a new view on image tap which is defined in tpl.
Code:
tpl: Ext.create('Ext.XTemplate','<tpl for="sample">',
'<div> {tittle}</div>
<div><img src="{photo}"/></div>',
'</tpl>',
/////////////////////////////////////////
After your directions, I'm sure I have something wrong. I just want to know how to link an image to puncture it take you to another view. Let me explain better?
Thank you very much for your time and dedication.
//MyController
Ext.define('DemoApp.controller.ControllerR', {
extend: 'Ext.app.Controller',
config: {
refs: {
res: 'res',
},
control: {
'rest list': {
itemtap: 'showR' },
}
},
onCreateNewView: function() {
if(document.getElementById('something')) {
Ext.Viewport.setActiveItem(Ext.create('DemoApp.view.DetalTwo'));
}
},
});
//My Template
Ext.define('DemoApp.view.ViewR', {
extend: 'Ext.Panel',
xtype: 'res',
config: {
title: 'Res',
iconCls: 'info',
scrollable: true,
data: {
res: [ model: 'DemoApp.model.ModelR',
autoLoad: true,
storeId: 'resStore',
proxy: {
type: 'ajax',
url: 'data/res.json',
reader: {
type: 'json',
rootProperty:'wha.res'
}
}]
},
tpl: Ext.create('Ext.XTemplate','<tpl for="sample">',
'<div> {tittle}</div>
<div><img id="something "src="{photo}"/></div>',
'</tpl>',
{
join: function(value) {
return value.join(', ');
}
})
}
});

You can give an id to image tag i.e. <img id = "something"/> and if tpl is defined for list then go to that list's handling function in controller or in view(where you defined a list and listeners) and write this code:
if(document.getElementById('Your img tag id')) {
// create a view and active it
}
For Example:
onCreateNewView: function() {
if(document.getElementById('something')) {
Ext.Viewport.setActiveItem(Ext.create('DemoApp.view.NewView'));
}
}

First thing first... you should do some google before putting such question in Stack.Your Code is totally wrong as well as it contains major syntax errors. And we are not here to solve some stupid syntax errors.Anyways Let me explain something..
1.) In your controller you are trying to bind 'itemtap' event.You can only bind 'itemtap' event if there is a list in your view. Here, in your case there is no list.
2.) And in controller it's look like something this:
refs: {
imageList: '#imageList',
},
control: {
'imageList': {
itemtap: 'onCreateNewView'
},
}
3.) Create a list in your view:
config: {
items: [
{
xtype: 'list',
id: 'imageList',
itemId: 'imageList',
tpl:'Your tpl code'
}
]
}

Related

ExtJs panel ViewModel getStore() call returns null instead of returning local ajax store object with URL bound to ViewModel data member

To reproduce this issue, I created a fiddle: https://fiddle.sencha.com/#view/editor&fiddle/3mk0 . The steps to reproduce are as follows:
Open your favorite browser and open the debugging tools to be able to view the JavaScript console
Navigate to the fiddle and run it
Double-click on a row - the grid is empty. Check the JS console: Uncaught TypeError: store is null (I used FF). The crux of the issue is that, in this line, let store = vm.getStore('testStore');, vm.getStore('testStore') returns null the first time. That's what I am trying to understand - why doesn't ExtJs initialize completely the ViewModel and the store, and instead it returns null. The problem is the binding in the url of the proxy. If the store doesn't have any binding in the url, it's going to work the first time as well.
Close the window, and double-click again on a row. This time the grid will show the data.
I know how to fix this - if I issue a vm.notify() before I set the store, it's going to work properly (I added a commented out line in the code).
Here is the source code from the fiddle:
app.js
/// Models
Ext.define('App.model.GroupRecord', {
extend: 'Ext.data.Model',
alias: 'model.grouprecord',
requires: [
'Ext.data.field.Integer',
'Ext.data.field.String'
],
fields: [{
type: 'int',
name: 'id'
}, {
type: 'string',
name: 'description'
}]
});
Ext.define('App.model.SomeRecord', {
extend: 'Ext.data.Model',
requires: [
'Ext.data.field.Integer',
'Ext.data.field.String'
],
fields: [{
type: 'int',
name: 'id'
}, {
type: 'string',
name: 'description'
}, {
type: 'int',
name: 'groupId'
}]
});
//// SomeGridPanel
Ext.define('App.view.SomeGridPanel', {
extend: 'Ext.grid.Panel',
alias: 'widget.somegridpanel',
requires: [
'App.view.SomeGridPanelViewModel',
'App.view.SomeGridPanelViewController',
'Ext.view.Table',
'Ext.grid.column.Number'
],
controller: 'somegridpanel',
viewModel: {
type: 'somegridpanel'
},
bind: {
store: '{testStore}'
},
columns: [{
xtype: 'numbercolumn',
dataIndex: 'id',
text: 'ID',
format: '0'
}, {
xtype: 'gridcolumn',
dataIndex: 'description',
text: 'Description'
}]
});
Ext.define('App.view.SomeGridPanelViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.somegridpanel',
requires: [
'Ext.data.Store',
'Ext.data.proxy.Ajax',
'Ext.data.reader.Json'
],
data: {
groupId: null
},
stores: {
testStore: {
model: 'App.model.SomeRecord',
proxy: {
type: 'ajax',
url: 'data1.json?groupId={groupId}',
reader: {
type: 'json'
}
}
}
}
});
Ext.define('App.view.SomeGridPanelViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.somegridpanel',
onRefresh: function (groupId) {
console.log('calling the grid panel onrefresh for groupId: ' + groupId);
let vm = this.getViewModel();
console.log(vm);
vm.set('groupId', groupId);
//vm.notify(); // <- uncomment this line to make it work properly
let store = vm.getStore('testStore');
//tore.proxy.extraParams.groupId = groupId;
store.load();
}
});
// TestWindow
Ext.define('App.view.TestWindow', {
extend: 'Ext.window.Window',
alias: 'widget.testwindow',
requires: [
'App.view.TestWindowViewModel',
'App.view.TestWindowViewController',
'App.view.SomeGridPanel',
'Ext.grid.Panel'
],
controller: 'testwindow',
viewModel: {
type: 'testwindow'
},
height: 323,
width: 572,
layout: 'fit',
closeAction: 'hide',
bind: {
title: '{title}'
},
items: [{
xtype: 'somegridpanel',
reference: 'someGrid'
}]
})
Ext.define('App.view.TestWindowViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.testwindow',
data: {
title: 'Test Window'
}
});
Ext.define('App.view.TestWindowViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.testwindow',
listen: {
controller: {
'*': {
loadData: 'onLoadData'
}
}
},
onLoadData: function (groupRecord) {
console.log('Loading data...');
console.log(groupRecord);
let vm = this.getViewModel();
vm.set('title', 'Group: ' + groupRecord.get('description'));
this.lookup('someGrid').getController().onRefresh(groupRecord.get('id'));
}
});
// ViewPort
Ext.define('App.view.MainViewport', {
extend: 'Ext.container.Viewport',
alias: 'widget.mainviewport',
requires: [
'App.view.MainViewportViewModel',
'App.view.MainViewportViewController',
'Ext.grid.Panel',
'Ext.view.Table',
'Ext.grid.column.Number'
],
controller: 'mainviewport',
viewModel: {
type: 'mainviewport'
},
height: 250,
width: 400,
items: [{
xtype: 'gridpanel',
title: 'Main Grid - double-click on a row',
bind: {
store: '{groupRecords}'
},
columns: [{
xtype: 'numbercolumn',
dataIndex: 'id',
text: 'Group id',
format: '00'
}, {
xtype: 'gridcolumn',
flex: 1,
dataIndex: 'description',
text: 'Group'
}],
listeners: {
rowdblclick: 'onGridpanelRowDblClick'
}
}]
});
Ext.define('App.view.MainViewportViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.mainviewport',
requires: [
'Ext.data.Store',
'Ext.data.proxy.Memory'
],
stores: {
groupRecords: {
model: 'App.model.GroupRecord',
data: [{
id: 1,
description: 'Group 1'
}, {
id: 2,
description: 'Group 2'
}, {
id: 3,
description: 'Group 3'
}, {
id: 4,
description: 'Group 4'
}, {
id: 5,
description: 'Group 5'
}],
proxy: {
type: 'memory'
}
}
}
});
Ext.define('App.view.MainViewportViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.mainviewport',
onGridpanelRowDblClick: function (tableview, record, element, rowIndex, e, eOpts) {
if (!this._testWindow) {
this._testWindow = Ext.create('widget.testwindow');
}
this._testWindow.show();
this.fireEvent('loadData', record);
}
});
Ext.application({
//name : 'Fiddle',
models: [
'SomeRecord',
'GroupRecord'
],
views: [
'MainViewport',
'TestWindow',
'SomeGridPanel'
],
name: 'App',
launch: function () {
Ext.create('App.view.MainViewport');
}
});
}
data1.json:
function(params, req, Fiddle) {
var range = [];
for (var i = 1; i <= 50; i++) {
var obj = {
id: i,
description: `Item ${i}`,
groupId: Math.floor( (i - 1) / 10) + 1
}
range.push(obj);
}
let allData = range.filter(it => it.groupId === params.groupId )
//console.log(params);
//console.log(req);
return allData;
}
The code is a bit contrived but it follows an issue (though not identical) I had in the real app. In the real app I have a weird intermittent issue, where I have a complex form with subcomponents, and a vm.notify() call made in a subcomponent, fires a method bound to a ViewModel data member (in other words it fires when that data member changes), which in turn tries to refresh a local store in another subcomponent but this subcomponent's ViewModel getStore() call returns null. The proxy of that store has the url property bound to a ViewModel data member. That's the common pattern. It seems to me that stores with proxies that have the url property bound to a ViewModel data property (ex: url: 'data1.json?groupId={groupId}') are not initialized properly by the time the form is rendered, and eventually, after some ViewModel computation cycles, they get initialized finally.
TIA
Update: There was a question below whether the url attribute of the proxy is bindable. I think it is bindable. Sencha Architect shows it as bindable, though, when enabled, it doesn't place the property in a bind object.
I did search the ExtJs 7.6.0 code base for samples where the {...} expressions are used in the url attribute, and I stumbled upon this test case:
packages\core\test\specs\app\ViewModel.js from line 7237:
describe("initial", function() {
it("should not create the store until a required binding is present", function() {
viewModel.setStores({
users: {
model: 'spec.User',
proxy: {
type: 'ajax',
url: '{theUrl}'
}
}
});
notify();
expect(viewModel.getStore('users')).toBeNull();
setNotify('theUrl', '/foo');
var store = viewModel.getStore('users');
expect(store.isStore).toBe(true);
expect(store.getProxy().getUrl()).toBe('/foo');
});
it("should wait for all required bindings", function() {
viewModel.setStores({
users: {
model: 'spec.User',
proxy: {
type: 'ajax',
url: '{theUrl}',
extraParams: {
id: '{theId}'
}
}
}
});
notify();
expect(viewModel.getStore('users')).toBeNull();
setNotify('theUrl', '/foo');
expect(viewModel.getStore('users')).toBeNull();
setNotify('theId', 12);
var store = viewModel.getStore('users');
expect(store.isStore).toBe(true);
expect(store.getProxy().getUrl()).toBe('/foo');
expect(store.getProxy().getExtraParams().id).toBe(12);
});
});
What is interesting is that it is expected in the test that viewmodel getStore would return null before setting the value for theUrl: expect(viewModel.getStore('users')).toBeNull(); !
Maybe it is by design...
Reason for the issue is - triggered event listener is getting executed before the View Model is initialised and grid is rendered
We can use following approach to fix these issues
Add delay before triggering the event
Listen for initViewModel method in controller and then load the store
Or add afterrender event on the grid, read its store and then load it
// Add delay to ensure view model is initialized
Ext.defer(function() {
this.fireEvent('loadData', record);
}, 1000, this);
There is a override function called "init" in each controller so use your initial code in this function. This function invokes every time when UI refresh/added new.
Example:
init: function() {
var vm = this.getViewModel();
var store = vm.data.myCurrentStore;
store.load();
}

navigate to next view on image tap?

I am using dataview itemTpl to display my images (thumbnail view).
I am able to display the images properly. How can I add itemtap on these images?
So that when I tap on a particular image a new view is shown with the tapped image?
I tried this with controller but no luck. Can you help me solving this.
I am using Sencha Architect 2 for my example.
I am trying this code .Please correct me
First View
Ext.define('MyApp.view.MyPanel', {
extend: 'Ext.Panel',
alias: 'widget.galleryview',
config: {
layout: {
type: 'fit'
},
items: [
{
xtype: 'dataview',
id: 'gallerythumbnail',
layout: {
type: 'fit'
},
itemTpl: [
'<div class="lb-album">',
' <ul>',
' <li>',
' <img src="{filename}" width="120px" heigth="120px"/>',
' </li>',
' </ul> ',
'</div>'
],
store: 'ImageStore'
}
]
}
});
Second View - this view must be displayed when img is tapped
Ext.define('MyApp.view.FullViewPanel', {
extend: 'Ext.Panel',
config: {
layout: {
type: 'fit'
},
items: [
{
xtype: 'carousel',
id: 'mycarousel'
}
]
}
});
Controller
Ext.define('MyApp.controller.GalleryController', {
extend: 'Ext.app.Controller',
config: {
refs: {
nav: 'galleryview'
},
control: {
"dataview": {
itemtap: 'viewFullScreen'
}
}
},
viewFullScreen: function(dataview, index, target, record, e, options) {
Ext.ComponentQuery.query('galleryview').push('MyApp.view.FullView');
console.log(record);
}
});
Thank You
If you only want to do it on image tap you can check for the event in that listener.
Method 1
listeners : {
itemtap: function (list, index, item, record, senchaEvent) {
if (senchaEvent.event.target.nodeName == 'IMG') {
// Show next view
}
}
}
I used like this
Ext.create('Ext.List', {
itemCls : 'my-dataview-item',
id : 'myList',
itemTpl : '<div><img src="' + localStorage.httpServerPrefix + '{imageURI}"/><span id="name">{fullname}</span></div>',
store : aroundStore,
listeners : {
itemtap: function (list, index, item, record, senchaEvent) {
if(senchaEvent.event.target.nodeName =='IMG') {
me.othersProfileImageClicked(record.data);
}
else
me.onMessageClickedInAround(list, record);
}
}
});
Update
listeners : {
itemtap: function (dataview,index,target,record,e,option) {
if (e.event.target.nodeName == 'IMG') {
// Show next view
}
}
}
Method 2
You can use event delegation, Like this
Ext.create('Ext.List', {
itemCls : 'my-dataview-item',
id : 'myList',
itemTpl : '<div><img src="' + localStorage.httpServerPrefix + '{imageURI}"/><span id="name">{fullname}</span>',
store : aroundStore,,
listeners: [{
element: 'element',
delegate: 'img',
event: 'tap',
fn: function() {
alert('One!');
}
}
]
});

How to change views when I click a button?

I'm using, or abusing, Sencha Touch for the first time and I just want to push a list view, when i click a button. Here is my view:
Ext.define('TouchNuts.view.Decision', {
extend: 'Ext.Panel',
xtype: 'decision',
config: {
title: 'Decision',
scrollable: true,
styleHtmlContent: true,
styleHtmlCls: 'Decision',
tpl: '<h2>{name}</h2>, <h3>{description}<h3>, <h4>{price:ellipsis(15)}</h4> <h1>you can do this </h1>',
items: [
{
xtype: 'button',
text: 'SEND',
ui: 'confirm',
docked: 'bottom',
action: 'doSomething'
}
]
}
});
Here is the view I'd like to push:
Ext.define('TouchNuts.view.File', {
extend: 'Ext.Panel',
xtype: 'file',
config: {
title: 'File',
iconCls: 'star',
layout: 'fit',
items: [
{
xtype: 'list',
id: 'file',
store: 'TransactionStore',
itemTpl: '<h2>{name:ellipsis(15)}</h2>, <h3>{description:ellipsis(8)}<h3>, <h4>{price:ellipsis(15)}</h4>',
itemCls: 'SummaryItems'
}
]
}
});
And here is my controller:
Ext.define('TouchNuts.controller.doSomething', {
extend: 'Ext.app.Controller',
config: {
refs: {
},
control: {
'button[action=doSomething]' : {
tap: function() {
getMainView('TouchNuts.view.Decision').push('TouchNuts.view.File');
}
}
}
}
});
I'm pretty good with HTML, CSS, and jQuery, but new to JS and totally clueless when it comes to Sencha so any advice is appreciated.
It is good to give your views an itemId inorder to reference them in your controller. So for instance:
TouchNuts.view.Decision can have an itemId:decisionPanel
and
TouchNuts.view.File can have an itemId:filePanel
Now in your Controller you would do this:
...
config: {
refs: {
decisionPanel: {
autocreate: true,
selector: '#decisionPanel',
xtype: 'decision'
},
filePanel: {
autocreate: true,
selector: '#filePanel',
xtype: 'file'
}
},
control: {
'button[action=doSomething]' : {
tap: 'onButtonTap'
}
}
onButtonTap : function(button, e, options) {
var me = this;
Ext.Viewport.setActiveItem(me.getDecisionPanel());
}
...
You will notice that I used getDecisionPanel() to get the decisionPanel view. This is because a getter function is automatically generated for each ref you specify and in order to access it, you new to use get+ the Capitalized ref name.
More info here: http://docs.sencha.com/touch/2-1/#!/api/Ext.app.Controller
Instead of
getMainView('TouchNuts.view.Decision').push('TouchNuts.view.File');
You have to create the view first and then push it to view
getMainView('TouchNuts.view.Decision').push(Ext.create('TouchNuts.view.File'));

Ext.getCmp is not working in ExtJS4

I am trying to create a component and access it inside controller. But while accessing the component by id, it is not returning the component. It always returns undefined. Please find below the code.
enter code here
//Component creation in view layer as below
Ext.define('MyApp.view.performance.grid.IFrameGridCmp', {
extend: 'Ext.panel.Panel',
alias: 'widget.crMainPanel',
id:'mainPanelId',
layout: {
align: 'stretch',
type: 'vbox'
},
border:0,
resizable: false,
forceFit: true,
autoWidth: true,
initComponent: function() {
Ext.apply(this, {
items: [
{
xtype:'panel',
flex:.02,
border:0
},
{
xtype:'crHeaderPanel',
flex:.05
},
{
xtype: 'crUpperPanel',
flex: 0.93
},
Ext.create('Ext.Component', {
autoEl: {
tag: 'iframe',
cls: 'x-hidden',
src: Ext.SSL_SECURE_URL
},
id:'FileDownloader',
height:0,
listeners: {
afterrender: function () {
this.getEl().on('load', function () {
console.log('loaded download frame');
});
}
},
load: function(config){
var e = this.getEl();
e.dom.src = config.url + (config.params ? '?' + Ext.urlEncode(config.params) : '');
e.dom.onload = function() {
Ext.getBody().unmask();
if(e.dom.contentDocument.body.childNodes[0].wholeText == '404') {
Ext.Msg.show({
title: 'Attachment missing',
msg: 'The document you are after can not be found on the server.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.ERROR
});
}
};
}
})
]
});
this.callParent(arguments);
}
});
========================================
enter code here
//Accessing the controller as below in controller
views: ['performance.grid.IFrameGridCmp'],
//The below line gives error
var downloader = Ext.getCmp('FileDownloader');
downloader.load({
url: '/APXUI/data/json/xls?exportData='+Ext.JSON.encode(records)
});
Well, the view is not created at the time you are calling Ext.getCmp()
Note that views: ['performance.grid.IFrameGridCmp'], is only a sort of binding that view to the controller, which means the controller will create a getter for you, nothing more. You still need to instantiate the view by calling .create(); or Ext.widget('crMainPanel')
In you controller use control for example to handle it:
me.control({
'crMainPanel #FileDownloader': {
afterrender: me.addDownloader
}
});
Don't use Ext.getCmp() it is really slow and you will have many issues with that.
Don't use id - better use itemId.
Why you need to call this from controller?

ExtJS4: Passing parameters / properties from Treestore to Controller

I´m using a simple TreeStore without proxies for building my Menu. Here is a simple Example:
Store:
Ext.define('LT.store.MnuApplication', {
extend: 'Ext.data.TreeStore',
root: {
expanded: true,
children: [{
text: "Lists",
expanded: true,
children: [{
text: "Countries",
leaf: true,
}]
}]
}
});
In the controller I´ve added a listener to the "click" event:
Controller:
init: function() {
this.control({
/**
* Catch the treepanels in the menu accordion
*/
'menu-main treepanel': {
itemclick: function (view, record, item, index, event){
var clickedMnuNode = record;
var tabPanel = this.getTabPanel();
// Open tab only if the menu node is a leaf node
if (clickedMnuNode.isLeaf()) {
tabPanel.add({
xtype: clickedMnuNode.raw.loadTabComponent
}).show();
}
}
}
});
this.callParent();
}
And now to my problem with this solution: I want to define in the TreeStore, which actions should be done by the controller - especially which component should be loaded.
For example I would like that the TreeStore looks like this:
Ext.define('LT.store.MnuApplication', {
extend: 'Ext.data.TreeStore',
root: {
expanded: true,
children: [{
text: "Lists",
expanded: true,
children: [{
text: "Countries",
leaf: true,
loadComponent: 'country-list',
loadTarget: 'tabPanel'
}]
}]
}
});
As you can see I´ve added two new parameters to the Tree Leaf. Now I cann access those datasets via the "RAW" method of the record. But that´s not really a nice solution - isn´t it?
So has anyone an idea for me, how to pass additional parameters (like "loadComponent" or "loadTaget") from my TreeStore to the controller?
Thanks in advance & cheers,
Michael
In the past I created similar tree with my custom parameters and I used a custom model for my TreeStore. Because by default your node, just has properties of NodeInterface. Something like this:
Ext.define('TreeConfig.model.Config', {
extend: 'Ext.data.Model',
fields: ['properties', 'id', 'text', 'guid'],
proxy: {
type: 'ajax',
url: rootConfigURL,
actionMethods: 'POST',
reader: {
type: 'json'
}
}
});
Ext.define('TreeConfig.store.CurrentConfig', {
extend: 'Ext.data.TreeStore',
model: 'TreeConfig.model.Config',
requires: 'TreeConfig.model.Config',
root: {
text: 'Config root',
expanded: true,
properties: []
}
});

Resources