I have a ViewModel that contains some initial data... this initial data is based off of a global variable that I have created. In the ViewModel, I have a formula that does some logic based on the data set from the global variable. The interesting thing is, this formula does not fire when the ViewModel is created. I'm assuming this is because the Something.Test property does not exist, so the ViewModel internals have some smarts to not fire the method if that property does not exist.
If the property doesn't exist, how do I fire the formula anyway? I know I could look for Something check to see if it has the property Test, but I'm curious why this example wouldn't work. Here's the example:
Ext.application({
name : 'Fiddle',
launch : function() {
// Define global var Something
Ext.define('Something', {
singleton: true
});
Ext.define('MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myView',
data: {
Something: window.Something
},
formulas: {
testSomething: function(getter) {
console.log('here', getter('Something.Test'));
return getter('Something.Test');
},
myTitle: function(getter) {
return 'My Title';
}
}
});
Ext.define('MyView', {
extend: 'Ext.panel.Panel',
bind: {
title: '{myTitle}'
},
viewModel: {
type: 'myView'
}
});
var view = Ext.create('MyView', {
renderTo: Ext.getBody()
});
// This will fire the ViewModel formula
//view.getViewModel().set('Something', window.Something);
console.log(Something, window.Something)
}
});
You can workout some logic to handle when Something.Test is not available, something like:
data: {
Something: window.Something && window.Something.Test || {Test: null}
},
formulas: {
testSomething: function(get) {
var val = get('Something.Test');
console.log('Test');
return val;
},
myTitle: function(getter) {
return 'My Title';
}
}
I am trying to get this to work in Sencha Fiddle. The problem I am facing is that I get an error on this line
MyApp.app.getView('MyApp.view.test2').test();
When you click inside the textbox, it fails with an error (in console log) Uncaught TypeError: MyApp.app.getView(...).test is not a function
//Controller
Ext.define('MyApp.controller.test', {
extend: 'Ext.app.ViewController',
alias: 'controller.test',
myVar:0,
init: function() {
}
});
//View
Ext.define('MyApp.view.test', {
extend: 'Ext.form.field.Text',
alias:'widget.test',
controller: 'test',
title: 'Hello',
listeners: {
focus: function(comp){
MyApp.app.getView('MyApp.view.test2').test(); //Fails with an error that test is not a function }
},
renderTo: Ext.getBody()
});
//View
Ext.define('MyApp.view.test2', {
extend: 'Ext.form.Panel',
alias:'widget.test2',
title: 'Hello2',
renderTo: Ext.getBody(),
test:function()
{
alert('in MyApp.view.test2');
}
});
Ext.application({
name: 'MyApp',
launch: function() {
Ext.create('MyApp.view.test');
Ext.create('MyApp.view.test2');
}
});
The getView() function just returns the class and not the instance of the view. See ExtJs 5.1.1 Controller.getView() :
Returns a View class with the given name. To create an instance of the view, you can use it like it's used by Application to create the Viewport:
this.getView('Viewport').create();
To get the created view instance you can archive it with a Ext.ComponentQuery.
//query returns an array of matching components, we choose the one and only
var test2View = Ext.ComponentQuery.query('test2')[0];
// and now we can execute the function
test2View.test();
See the running minimalistic fiddle.
//View
Ext.define('MyApp.view.test', {
extend: 'Ext.form.field.Text',
alias: 'widget.test',
title: 'Hello',
listeners: {
focus: function (comp) {
//query returns an array of matching components, we choose the one and only
var test2View = Ext.ComponentQuery.query('test2')[0];
test2View.test();
//MyApp.app.getView('MyApp.view.test2').test();//Fails with an error that test is not a function
}
},
renderTo: Ext.getBody()
});
//View
Ext.define('MyApp.view.test2', {
extend: 'Ext.form.Panel',
alias: 'widget.test2',
title: 'Hello2',
renderTo: Ext.getBody(),
test: function ()
{
alert('in MyApp.view.test2');
}
});
Ext.application({
name: 'MyApp',
launch: function () {
Ext.create('MyApp.view.test');
Ext.create('MyApp.view.test2');
}
});
I want to perform selection operation on List after the data has been loaded, because based on the data which I received I have to select one cell in that list and also need to update the detail view base on that.
Ext.define('WaxiApp.view.ProductViews.ProductList', {
extend: 'Ext.Container',
alias: "widget.ProductList",
requires: [
'Ext.Img',
],
config: {
layout: Ext.os.deviceType == 'Phone' ? 'fit' : {
type: 'hbox',
pack:'strech'
},
cls: 'product-list',
items: [{
xtype: 'list',
id:'product-list-view',
width: '100%',
height:'100%',
store: 'ProductsList',
infinite: true,
plugins: 'sortablelist',
itemCls: 'productList',
itemId:"product-item",
itemTpl: new Ext.XTemplate(
'<div class="list-content-div ',
'<tpl if="this.needSortable(isNeedInventry)">',
Ext.baseCSSPrefix + 'list-sortablehandle',
'</tpl>',
'">',
'<b>{UpcCode} {Name}</b>',
'<tpl if="isActive">',
'</div>',
'</tpl>',
{
// XTemplate configuration:
compiled: true,
// member functions:
needSortable: function (isneedInventry) {
return !isneedInventry;
},
}),
masked: { xtype: 'loadmask',message: 'loading' },
onLoad: function (store) {
this.unmask();
console.log('list loaded');
this.fireEvent("productListLoadedCommand", this,store);
},
}
],
listeners: [
{
delegate: "#product-list-view",
event: "itemtap",
fn: function (list, index, target, record) {
console.log(index);
console.log('list selection command fired');
this.fireEvent("productListSelectedCommand", this,index,record);
}
}
],
style: 'background-image: -webkit-gradient(linear, left top, right bottom, color-stop(0, #FDFDFD), color-stop(1, #DBDBDB));background-image: linear-gradient(to bottom right, #FDFDFD 0%, #DBDBDB 100%);'
}//End of config
});//End of Define
Above this actual view I used to display the list. My problem is I tried onLoad() method it work but i want do everything in my Controller to make it more clear.
As you saw my itemTap event has been handled in Controller by firing event. But same is not working for load event.
As mentioned by #Jimmy there is no onLoad method on list. However there are a few ways to work around it. My understanding of what you want to achieve is that when the store backing the list is loaded, you want an event to be fired from the ProductList instance (not the list) such that in the controller you can configure the control to be:
control: {
ProductList: {
productListSelectedCommand: 'productListSelectCommand',
productListLoadedCommand: 'productListLoadedCommand'
}
}
If so then we can modify the listeners in your existing ProductList class to do the following:
listeners: [
{
delegate: "#product-list-view",
event: "itemtap",
fn: function (list, index, target, record) {
console.log(index);
console.log('list selection command fired');
this.fireEvent("productListSelectedCommand", this,index,record);
}
},
{
delegate: "#product-list-view",
event: "show",
fn: function (list) {
var store = list.getStore();
var handler = function() {
list.unmask();
console.log('list loaded');
this.fireEvent("productListLoadedCommand", this, store);
};
if (store.isLoaded()) {
handler.apply(this)
} else {
list.getStore().on('load', handler, this);
}
}
}
]
What this is does is to what for the list to be shown and then get it's store, if the store has loaded then invoke the handler, otherwise register a load listener directly on it. Note that the this object here will be the ProductList not the product-list-view.
Per the Sencha Touch documentation, I do not see an onLoad function for Ext.dataview.List. However, there is a load event listener for the Ext.data.Store, which the list contains. So, your event listener should probably be on the data store, not necessarily the list itself.
Inside your controller's launch method, you could setup a listener for the Store's load event like so:
launch: function () {
// your store should be setup in your Ext.application
Ext.getStore('NameOfStore').on('load', this.productListLoadedCommand);
},
productListLoadedCommand: function(store, records, successful, operation, eOpts) {
// do some after load logic
}
You should set up your event listener for your list in the controller as well. There should be no need for you to create a listener in the view config only to call a fireEvent method in the Controller. Instead, do all event handling in the controller. To get a handle on your list in the Controller, add a xtype: 'productlist' inside the Ext.define for your WaxiApp.view.ProductViews.ProductList. Then, add your list to the Controller's config as a ref and attach the itemtap event for the list in the control like so:
config: {
ref: {
productList: 'productlist'
},
control: {
productList: {
itemtap: 'productListSelectCommand'
}
}
},
productListSelectCommand: function (list, index, target, record, e, eOpts) {
// do some itemtap functionality
}
In the end, your controller might look something like this:
Ext.define('MyApp.controller.Controller', {
extend: 'Ext.app.Controller',
requires: [
// what is required
],
config: {
ref: {
productList: 'productlist'
},
control: {
productList: {
itemtap: 'productListSelectCommand'
}
}
},
launch: function () {
// your store should be setup in your Ext.application
Ext.getStore('NameOfStore').on('load', this.productListLoadedCommand);
},
productListLoadedCommand: function(store, records, successful, operation, eOpts) {
// do some after load logic
// this.getProductList() will give you handle of productlist
},
productListSelectCommand: function (list, index, target, record, e, eOpts) {
// do some itemtap functionality
}
}
Finally, don't forget to add a xtype: 'productlist' inside the Ext.define for your WaxiApp.view.ProductViews.ProductList. I'm not sure of your overall experience with Sencha Touch application design, but here is a good reference for understanding their view, model, store, controller structure.
I found the solution exactly how to handle this scenario and posted my
own solution.
ProductList.js
Ext.define('WaxiApp.view.ProductViews.ProductList', {
extend: 'Ext.Container',
alias: "widget.ProductList",
requires: [
'Ext.Img',
],
initialize: function () {
this.add([
{
xtype: 'list',
id: 'product-list-view',
store: 'ProductsList',
masked: { xtype: 'loadmask', message: 'loading' },
//onLoad is not a listener it's private sencha touch method used to unmask the screen after the store loaded
onLoad: function (store) {
this.unmask();//I manually unmask, because I override this method to do additional task.
console.log('list loaded');
this.fireEvent("productListLoadedCommand", this, store);
}
,
//Painted is event so added it to listener, I saw fee document state that, add event like Painted and show should to added to the
//Component itslef is best practice.
listeners: {
order: 'after',
painted: function () {
console.log("Painted Event");
this.fireEvent("ProductListPaintedCommand", this);
},
scope: this
//This is also very important, because if we using this in card layout
//and changing the active view in layout cause this event to failed, so
//setting scope to this will help to receive the defined view instead of this list component.
}
}]);
},
config: {
listeners: [
{
delegate: "#product-list-view",
event: "itemtap",
fn: function (list, index, target, record) {
console.log(index);
console.log('list selection command fired');
this.fireEvent("productListSelectedCommand", this, index, record);
}
}
],
}//End of config
});//End of Define
ProductViewController.js
/// <reference path="../../touch/sencha-touch-all-debug.js" />
Ext.define("WaxiApp.controller.ProductsViewController", {
extend: "Ext.app.Controller",
listStoreNeedLoad:true,
config: {
refs: {
ProductContainer: "ProductList",
ProductList: "#product-list-view",
ProductDetailView:"ProductDetail"
},
control: {
ProductContainer:{
productListSelectedCommand: "onProductListSelected",
ProductListPaintedCommand: "onProductListPainted"
},
ProductList:{
productListLoadedCommand: "onProductListLoaded"
}
}//End of control
},//End of config
// Transitions
getstore:function(){
return Ext.ComponentQuery.query('#product-list-view')[0].getStore();
},
onProductListPainted:function(list)
{
//Check for if need to load store because painted event called every time your view is painted on screen
if (this.listStoreNeedLoad) {
var store = this.getstore();
this.getstore().load();
}
},
onProductListLoaded:function(list,store)
{
this.listStoreNeedLoad = false;
var index = 0;
//Iterate through record and set my select Index
store.each(function(record,recordid){
console.info(record.get("isNeedInventry"));
if (record.get("isNeedInventry")) {
return false;
}
index++;
});
console.log('list load event fired');
if(Ext.os.deviceType.toLowerCase()!='phone')
{
this.setRecord(store.getAt(index));
list.select(index);
}
}
})//End of define
I am trying to call a function every time when a view shows up. but it only gets called for first time and thenafter it doesn't. Is there any event-function like viewWillAppear of iOS. I am using 'initialize' event function and it gets called only once. Please help.
View:
Ext.define('Abc.view.abc', {
extend: 'Ext.List',
xtype: 'runningList',
requires: ['Abc.store.InstancesStore','Ext.data.proxy.JsonP',],
config: {
title: 'Running',
id: 'instanceList',/*
itemTpl: '<div class="serached_listview">'+
'<div>{key} {key} </div>' +
'<div><b>{key}</b> </div>' +
'<div> {key}</div>' +
'</div>'
,*/
store: 'RunningInstanceStore',
listeners: [{
fn: 'initialize',
event: 'initialize'
}
]
}
});
Controller:
Ext.define("Abc.controller.InstancesController", {
extend: 'Ext.app.Controller',
requires: [ 'Ext.data.JsonP','Ext.device.Connection'],
config: {
refs: {
main: 'mainpanel',
Instances: '#instanceList',
ListView: 'runningList'
},
control: {
Instances: {
initialize: 'initializePanel',
activate:'initializePanel'
},
"runningInstancesList": {
disclose: 'listViewAccessoryTapped',
itemtap: 'listViewTapped'
}
}
},
listViewAccessoryTapped: function(view, index, item, e) {
if(Ext.device.Connection.isOnline())
console.log('Internet connection is available.');
else
console.log('Internet connection is not available.');
},
listViewTapped: function(view, index, item, e) {
},
initializePanel: function() {
console.log('Hi'); **////////////////Called only once...**
}
});
In ExtJS there is component.beforeactivate, which fires each time before a component is activated
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.AbstractComponent-event-beforeactivate
However, in Touch, there appears to be only container.activate, which looks like it is fired after a child component is activated
http://docs.sencha.com/touch/2-1/#!/api/Ext.Container-event-activate
One of these event handlers (instead of initialize) should help you:
Painted
http://docs.sencha.com/touch/2-1/#!/api/Ext.Component-event-painted
Show
http://docs.sencha.com/touch/2-1/#!/api/Ext.Component-event-show
Ideally these events should be handled in controller like this(one of many ways)
'#instanceList': {
show : 'onListShow'
}
but you can alternatively add listener to your view like this:
Ext.define('Abc.view.abc', {
extend: 'Ext.List',
xtype: 'runningList',
requires: ['Abc.store.InstancesStore','Ext.data.proxy.JsonP',],
config: {
title: 'Running',
id: 'instanceList',
store: 'RunningInstanceStore',
listeners: {
show : function(me, opts){
// Do whatever you want here
}
}
}
});
T writing code using ExtJS4.0.1, MVC architecture. And when I develop main form I meet problem with search extension for web site.
When I was trying to create new widget in controller, I need render result in subpanel. and so when I write sample code I meet following problem:
**Uncaught TypeError: Object [object Object] has no method 'setSortState'**
I cannot understand why it gives that error message. I need some help to resolve that problem.
Below I want to show my code:
//Panel which is showing to user
Ext.define('Semantics.view.main.menuView', {
extend: 'Ext.panel.Panel',
layout: 'fit',
alias: 'widget.Menu',
title: "",
tbar: [
{
//search field
name:'mainSearchText',
id:'mainSearchText',
xtype: 'textfield',
defaultValue: 'Search',
height: 20
},
{
name:'mainSearchButton',
id:'mainSearchButton',
xtype: 'button',
text: 'Find',
height: 20
}
]
});
//controller for search request
Ext.define('Semantics.controller.main.mainController', {
extend: 'Ext.app.Controller',
views: ['main.menuView','mainSearch.MainSearchResultForm'],
refs: [
{ ref: 'menuPanel', selector: 'Menu' },
{ ref:'mainSearchText',selector:'#mainSearchText'},
{ref: 'mainSearchForm',selector:'#mainSearchForm'},
{ref:'MainSearchGrid',selector:'#MainSearchGrid'}
],
init: function () {
this.control({
'Menu': {
render: this.onPanelRendered
},
'Menu button[name="mainSearchButton"]': {
click: this.onButtonClick
}
});
},
onButtonClick: function (button) {
var me = this;
if(button.name=="mainSearchButton") {
var mtextFiled = me.getMainSearchText().getValue();
console.log(mtextFiled);
Ext.Ajax.request({
scope: this,
url: 'app/mainSearchT/findText/',
method: 'POST',
params: {
text: me.getMainSearchText().getValue()
},
success: function (result) {
mainPanel = me.getMenuPanel();
mainPanel.removeAll(true);
loadingMask = new Ext.LoadMask(mainPanel, { msg: "Loading" });
loadingMask.show();
var mname = 'MainSearchResultForm';
var start_info_panel = Ext.widget(mname);
mainPanel.items.add(start_info_panel);
loadingMask.hide();
mainPanel.doLayout(); //that line gives that error
},
failure: function (result) {
console.log(result);
}
});
}
},
onPanelRendered: function () {
}
});
I encountered the same error when I included unsupported xtype in Ext.grid.Panel columns. I had to remove the xtype, which resolved the problem.