ExtJS 5: Set child View Model data from bound value - javascript

I have a class that has its own view model, and I create 2 instances of this class in my main view. In the main view, I want to pass down values for my 2 class instances, but I can't seem to get this working... I think I'm just not understanding some very simple concept.
The expected result is the value1 + value2 field has the concatenation of value1 and value2, the first myValue shows value1, and the 2nd myValue shows value2. Here's my code and example:
Ext.application({
name: 'Fiddle',
launch: function() {
Ext.define('MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myView',
formulas: {
doSomething: function(getter) {
console.log(getter('value1'), getter('value2'));
return getter('value1') + getter('value2');
}
}
});
Ext.define('MyView', {
extend: 'Ext.panel.Panel',
xtype: 'myView',
viewModel: {
type: 'myView'
},
config: {
myValue: null
},
publishes: {
myValue: true
},
items: [
{
xtype: 'displayfield',
fieldLabel: 'myValue',
bind: {
value: '{myValue}'
}
}
]
});
Ext.create('Ext.container.Container', {
renderTo: Ext.getBody(),
items: [
{
xtype: 'displayfield',
fieldLabel: 'display',
bind: {
value: '{doSomething}'
}
},
{
xtype: 'myView',
reference: 'view1',
title: 'View1',
bind: {
myValue: '{value1}'
}
},
{
xtype: 'myView',
reference: 'view2',
title: 'View2',
bind: {
myValue: '{value2}'
}
}
],
viewModel: {
data: {
value1: 'Something',
value2: 'something else'
}
}
})
}
});

Your first displayField will never "see" the doSomething formula, because that formula is not part of it's parent, so you will need to move the formula from MyViewModel to your Ext.container.Container viewModel.
Also, when you publish a custom value, it will have reference.publishedvalue format. This should fix your panel:
Ext.application({
name: 'Fiddle',
launch: function() {
Ext.define('MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myView'
});
Ext.define('MyView', {
extend: 'Ext.panel.Panel',
xtype: 'myView',
viewModel: {
type: 'myView'
},
config : {
myValue : null
},
publishes : ['myValue'],
items: [{
xtype: 'displayfield',
fieldLabel: 'myValue',
initComponent : function() {
var me = this,
owner = me.$initParent || me.initOwnerCt;
this.setBind({
value: '{' + owner.reference + '.myValue}'
});
this.callParent();
}
}]
});
Ext.create('Ext.container.Container', {
renderTo: Ext.getBody(),
viewModel: {
data: {
value1: 'Something',
value2: 'something else'
},
formulas: {
doSomething: function(getter) {
console.log(getter('value1'), getter('value2'));
return getter('value1') + getter('value2');
}
}
},
items: [{
xtype: 'displayfield',
fieldLabel: 'display',
bind: {
value: '{doSomething}'
}
},{
xtype: 'myView',
reference: 'view1',
title: 'View1',
bind: {
myValue: '{value1}'
}
},{
xtype: 'myView',
reference: 'view2',
title: 'View2',
bind: {
myValue: '{value2}'
}
}]
})
}
});

Related

Ext JS: tpl config is ignoring html in Panel

I need to use both of configs tpl and html within panel class but it's not let to render both configs.
How can be able to use both?
Ext.define('InfoCard.Main', {
extend: 'Ext.panel.Panel',
viewModel: {
type: 'infocardvm'
},
layout: {
type: 'table'
},
items: [{
xtype: 'infocard',
userCls: 'totalReCls',
bind: {
html: '{totalReBar}'
}, //Can't use 'tpl' here because it doesn't have setTpl()
glyph: 'xf015#FontAwesome',
// tpl: 'TotalReBar' //When I'm uncomment the config, then ignores 'bind: html'
}]
});
Here is full sample FIDDLE.
Question is related with this post at Software Engineering site.
You can use displayfield inside of items of panel. In display field have methods for setValue() and setFieldLabel(). So you can change on basis of requirement.
In this FIDDLE, I have created a demo using your code and put modification. Hope this will help/guide you to achieve your requirement.
CODE SNIPPET
Ext.define('InfoCard.Main', {
extend: 'Ext.panel.Panel',
tbar: [{
text: 'Refresh data',
handler: function () {
var vm = this.up('panel').getViewModel(),
store = vm.getStore('firstStore');
store.getProxy().setUrl('stat1.json');
store.load();
}
}],
viewModel: {
type: 'infocardvm'
},
layout: {
type: 'table'
},
defaults: {
xtype: 'infocard',
},
items: [{
items: {
xtype: 'displayfield',
fieldLabel: 'Total ReBar',
bind: '{totalReBar}'
},
glyph: 'xf015#FontAwesome'
}, {
items: {
xtype: 'displayfield',
fieldLabel: 'Total RoBar',
bind: '{totalRoBar}'
},
bodyStyle: {
"background-color": "#DFE684"
},
glyph: 'xf015#FontAwesome'
}, {
items: {
xtype: 'displayfield',
fieldLabel: 'Total PaBar',
bind: '{totalPaBar}'
},
bodyStyle: {
"background-color": "#fbe3ab"
},
glyph: 'xf015#FontAwesome'
}]
});
When I understand you correctly you want to render a static string with a variable value.
{
xtype: 'infocard',
bind: { data: '{totalReBar}' }, // bind the data for the tpl
tpl: 'TotalReBar {totalCount}' // use the data provided via binding in the tpl
}
The formula now has to return an object for the tpl data binding.
You can access the values in the tpl via the key's.
formulas: {
totalReBar: {
bind: {bindTo: '{firstStore}', deep: true},
get: function (store) {
var record = store.findRecord("code", "TOTALRE");
return {
totalCount: record ? record.get("totalcount") : "-1" // totalCount is now available in the tpl as a variable
};
}
},
See the modified fiddle.
I believe that what you want is this:
{
xtype: 'infocard',
userCls: 'totalReCls',
bind: {data: {'myVal': '{totalReBar}'}},
glyph: 'xf015#FontAwesome',
tpl: '{myVal}'
}
hope that helps

Extjs - How to invoke parent view controller methods from child view controller?

I added a listener onParentPagePopupCommit for button in popup which was declared in parent view controller and added the popup in view port, now the view model bindings are working as expected, but not sure how to invoke the parent view controller methods without exposing the same methods names in child view controller. Is there any way to extend View Controller during runtime in ExtJs Modern 6.5.
Fiddle
onShowChildPopup: function (sender) {
var popup = sender.up('panel').popups['childPopup'],
pageCtrler = sender.lookupController(),
pageVM = pageCtrler.getViewModel(),
page = pageCtrler.getView(),
popupCtrler = new Ext.app.ViewController({
parent: pageCtrler, //setting parent ctrler
//popup commit event on popup view controller
onPopupCommit: function () {
debugger;
Ext.Msg.alert("Popup Update", "Popup View Controller Invoked")
console.log("popup view controller - commit");
},
// this works but any other way
// same methods name on popup view ctrler...
/*onParentPagePopupCommit: function(){
debugger;
// I don't like this way of invoking a parent method
// this may introduce few more bugs if the parent gets value from its own
// view model - (this will be parent vm and not child/popup.)
// need something similar to extend inorder to reuse certain methods..
this.parent.onParentPagePopupCommit();
var vm = this.getViewModel().get('vm');
vm.set('fullName',this.getFullName());
}*/
}),
popupVM = new Ext.app.ViewModel({
parent: pageVM, //setting parent view model
links: {
//vm is not merging with parent
//vm: {
childvm: {
reference: 'ChildModel',
create: {
address: "child Address",
phone: "child Phone"
}
}
}
});
popup.controller = popupCtrler;
popup.viewModel = popupVM;
popup = Ext.create(popup);
popup.setShowAnimation({
type: 'slideIn',
duration: 325,
direction: 'up'
});
popup.setHideAnimation({
type: 'slideOut',
duration: 325,
direction: 'down'
});
popup.setCentered(true);
/* popup.on('show', function () {
debugger;
});*/
Ext.Viewport.add(popup).show();
//popup.showBy(page, "c-c");
}
For this you need to use extend config in controller.
For example:
Ext.define('Person', {
say: function(text) { alert(text); }
});
Ext.define('Developer', {
extend: 'Person',
say: function(text) { this.callParent(["print "+text]); }
});
In this FIDDLE, I have created a demo using your code and made some modification. I hope this will help or guide your to achieve you requirement.
Code Snippet
Ext.define('ParentModel', {
extend: 'Ext.data.Model',
alias: 'model.parentmodel',
fields: [{
name: 'firstName',
type: 'string'
}, {
name: 'lastName',
type: 'string'
}, {
name: 'address',
type: 'string'
}]
});
Ext.define('ChildModel', {
extend: 'Ext.data.Model',
alias: 'model.childmodel',
fields: [{
name: 'address',
type: 'string'
}, {
name: 'phone',
type: 'number'
}, {
name: 'fullName',
type: 'string'
}]
});
Ext.define('PageViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.pageviewmodel',
links: {
vm: {
reference: 'ParentModel',
create: {
firstName: 'firstName-ParentVM',
lastName: 'lastName-ParentVM'
}
}
}
});
Ext.define('PageViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.pageviewcontroller',
init: function () {
this.callParent(arguments);
},
//i understand fullname can also be done on model using formula/conver
//this is just a sample
getFullName: function () {
var vm = this.getViewModel().get('vm');
return vm.get('firstName') + " " + vm.get('lastName');
},
//popup commit event on parent view controller
onParentPagePopupCommit: function (button) {
var vm = button.up('formpanel').getViewModel().get('vm');
vm.commit();
Ext.Msg.alert("Parent Page Update", "Parent View Controller Invoked");
console.log("Page view controller - commit");
},
onShowChildPopup: function (button) {
var popup = button.up('panel').popups['childPopup'],
pageCtrler = button.lookupController(),
pageVM = pageCtrler.getViewModel(),
page = pageCtrler.getView(),
popupVM = new Ext.app.ViewModel({
parent: pageVM, //setting parent ViewModel
links: {
//vm is not merging with parent
//vm: {
childvm: {
reference: 'ChildModel',
create: {
address: "child Address",
phone: "child Phone"
}
}
}
});
popup.viewModel = popupVM;
popup = Ext.create(popup);
popup.setShowAnimation({
type: 'slideIn',
duration: 325,
direction: 'up'
}).setHideAnimation({
type: 'slideOut',
duration: 325,
direction: 'down'
}).setCentered(true);
Ext.Viewport.add(popup).show();
}
});
//Need to extend popup controller from PageViewController(parent)
Ext.define('PopupViewController', {
extend: 'PageViewController',
alias: 'controller.popupviewcontroller',
//popup commit event on popup view controller
onPopupCommit: function () {
Ext.Msg.alert("Popup Update", "Popup View Controller Invoked")
console.log("popup view controller - commit");
}
});
Ext.define('MainPage', {
extend: 'Ext.Panel',
config: {
title: 'Page',
width: '100%',
height: '100%',
layout: {
type: 'vbox',
align: 'stretch'
}
},
viewModel: {
type: 'pageviewmodel'
},
controller: {
type: 'pageviewcontroller'
},
width: '100%',
height: '100%',
popups: {
childPopup: {
xtype: 'formpanel',
controller: 'popupviewcontroller',
title: 'Cild Popup',
floating: true,
modal: true,
hideonMaskTap: true,
layout: 'float',
minWidth: 300,
maxHeight: 580,
tools: [{
type: 'close',
handler: function () {
var me = this.up('formpanel');
me.hide();
}
}],
items: [{
xtype: 'container',
layout: {
type: 'vbox',
align: 'stretch',
pack: 'center'
},
items: [{
xtype: 'fieldset',
items: [{
xtype: 'label',
bind: {
html: '{vm.firstName} {vm.lastName}'
}
}, {
xtype: 'textfield',
label: 'First Name',
name: 'firstName',
bind: {
value: '{vm.firstName}'
}
}, {
xtype: 'textfield',
label: 'Last Name',
name: 'lastName',
bind: {
value: '{vm.lastName}'
}
}, {
xtype: 'textfield',
label: 'Last Name',
name: 'lastName',
bind: {
//value: '{vm.address}'
value: '{childvm.address}'
}
}]
}, {
xtype: 'container',
docked: 'bottom',
layout: 'hbox',
items: [{
xtype: 'button',
text: 'Popup Update',
handler: 'onPopupCommit'
}, {
xtype: 'button',
text: 'Parent Update',
handler: 'onParentPagePopupCommit'
}]
}]
}]
}
},
bodyPadding: 10,
items: [{
xtype: 'fieldset',
title: 'Enter your name',
items: [{
xtype: 'textfield',
label: 'First Name',
name: 'firstName',
bind: {
value: '{vm.firstName}'
}
}, {
xtype: 'textfield',
label: 'Last Name',
name: 'lastName',
bind: {
value: '{vm.lastName}'
}
}]
}, {
xtype: 'button',
text: 'Show Popup',
handler: 'onShowChildPopup'
}]
});
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.Viewport.add(Ext.create('MainPage'));
}
});

ExtJS 5: disable form fields in hidden card

I have a form panel with a radiogroup, and depending on the radiogroup selection, it will show some other components. If a radiofield is not selected, then its items will be hidden, as they're not part of the active card.
Now, if I have allowblank: false set on a field (and it's empty) within the hidden card, my form is still considered invalid. Being hidden means the user would not like to use it, so it should not be considered as part of the form. Here's an example.
In the example, I have 2 forms... the top form is the one that I'm curious about... is there a way to get this working without having to bind to disabled? I tried looking at hideMode, but that wasn't what I was looking for.
Ideally, I wouldn't have to create a formula for each card that I add... that seems silly. I realize I could create a generic formula, but once again, that just seems extraneous. Another option could be ditching the card layout, and binding each card to hidden and disabled, but I'm creating more formulas. Is there some sort of property I'm missing?
Ext.application({
name: 'Fiddle',
launch: function() {
Ext.define('MyController', {
extend: 'Ext.app.ViewController',
alias: 'controller.myview',
onValidityChange: function(form, isValid, eOpts) {
this.getViewModel().set('isFormValid', isValid);
}
});
Ext.define('MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myview',
data: {
activeItem: {
myInput: 0
}
},
formulas: {
activeCardLayout: function(getter) {
var myInput = getter('activeItem.myInput');
console.log(myInput);
return parseInt(myInput);
}
}
});
Ext.define('MyForm', {
extend: 'Ext.form.Panel',
title: 'My Form',
controller: 'myview',
viewModel: {
type: 'myview'
},
layout: {
type: 'hbox'
},
listeners: {
validitychange: 'onValidityChange'
},
dockedItems: [{
xtype: 'toolbar',
dock: 'top',
layout: {
type: 'hbox'
},
items: [{
xtype: 'button',
text: 'Save',
reference: 'saveButton',
disabled: true,
bind: {
disabled: '{!isFormValid}'
}
}]
}],
items: [{
xtype: 'radiogroup',
vertical: true,
columns: 1,
bind: {
value: '{activeItem}'
},
defaults: {
name: 'myInput'
},
items: [{
boxLabel: 'None',
inputValue: '0'
}, {
boxLabel: 'Something',
inputValue: '1'
}]
}, {
xtype: 'container',
layout: 'card',
flex: 1,
bind: {
activeItem: '{activeCardLayout}'
},
items: [{
xtype: 'container',
layout: 'fit'
}, {
xtype: 'container',
layout: 'fit',
items: [{
xtype: 'textfield',
fieldLabel: 'hello',
allowBlank: false
}]
}]
}]
});
Ext.define('MyViewModel2', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myview2',
data: {
activeItem: {
myInput: 0
}
},
formulas: {
disableSomethingCard: function(getter) {
return getter('activeItem.myInput') !== '1';
},
activeCardLayout: function(getter) {
var myInput = getter('activeItem.myInput');
console.log(myInput, 'here');
return parseInt(myInput);
}
}
});
Ext.define('MyForm2', {
extend: 'MyForm',
title: 'My Form 2 (works)',
viewModel: {
type: 'myview2'
},
items: [{
xtype: 'radiogroup',
vertical: true,
columns: 1,
bind: {
value: '{activeItem}'
},
defaults: {
name: 'myInput'
},
items: [{
boxLabel: 'None',
inputValue: '0'
}, {
boxLabel: 'Something',
inputValue: '1'
}]
}, {
xtype: 'container',
layout: 'card',
flex: 1,
bind: {
activeItem: '{activeCardLayout}'
},
items: [{
xtype: 'container',
layout: 'fit'
}, {
xtype: 'container',
layout: 'fit',
bind: {
disabled: '{disableSomethingCard}'
},
items: [{
xtype: 'textfield',
fieldLabel: 'hello',
allowBlank: false
}]
}]
}]
});
Ext.create('MyForm', {
renderTo: Ext.getBody()
});
Ext.create('MyForm2', {
renderTo: Ext.getBody()
});
}
});

Get container's record on "initialize" in Sencha Touch

I have a listContainer and on tap of any item in the list, I open another page known as editContainer with the record from list. In the edit page, I want to disable a dropdown based on the value of another field, is there any way I can get the values in record in the initialize function of editContainer? Here is my code:-
onListItemTap:function(dataview,index,target,record,e,eOpts)
{
if(record)
this.editContainer = Ext.create('myApp.view.EditContainer',{title:'Edit Data'});
this.editContainer.setRecord(record);
this.getMainNav().push(this.editContainer);
}
Above code opens editContainer when a list item is selected. Below is my EditContainer
Ext.define('myApp.view.EditContainer', {
extend: 'Ext.Container',
requires: [
'Ext.form.Panel',
'Ext.form.FieldSet',
'Ext.field.Select'
],
config: {
id: 'editContainer',
autoDestroy: false,
layout: 'fit',
items: [
{
xtype: 'formpanel',
id: 'editFormPanel',
padding: 10,
styleHtmlContent: true,
autoDestroy: false,
layout: 'fit',
items: [
{
xtype: 'fieldset',
id: 'nameFieldSet',
autoDestroy: false,
items: [
{
xtype: 'textfield',
id: 'siteName',
itemId: 'mytextfield',
label: 'Name',
labelWidth: '35%',
name: 'name'
},
{
xtype: 'selectfield',
id: 'role',
itemId: 'myselectfield4',
label: 'Role',
labelWidth: '35%',
name: 'role',
options: [
{
text: 'Unassigned',
value: 'Unassigned'
},
{
text: 'Role1',
value: 'role1'
}
]
},
{
xtype: 'selectfield',
id: 'type',
label: 'Type',
labelWidth: '35%',
name: 'type',
options: [
{
text: 'Default',
value: 'Default'
},
{
text: 'Custom',
value: 'custom'
}
]
},
{
xtype: 'selectfield',
id: 'roleValue',
label: 'Role Value',
labelWidth: '35%',
name: 'rolevalue',
options: [
{
text: 'value1',
value: 'value1'
},
{
text: 'value2',
value: 'value2'
},
{
text: 'value3',
value: 'value3'
}
]
}
]
}
]
}
],
listeners: [
{
fn: 'onTextfieldKeyup',
event: 'keyup',
delegate: 'textfield'
},
{
fn: 'onSelectfieldChange',
event: 'change',
delegate: 'selectfield'
}
]
},
onTextfieldKeyup: function(textfield, e, eOpts) {
this.fireEvent('change', this);
},
onSelectfieldChange: function(selectfield, newValue, oldValue, eOpts) {
this.fireEvent('change', this);
},
initialize: function() {
this.callParent();
var record;
//Code to disable roleValue selectfield if Role is unassigned.
},
updateRecord: function(newRecord) {
var me = this,
myPanel = me.down('#editFormPanel');
if(myPanel)
myPanel.setRecord(newRecord);
},
saveRecord: function() {
var me =this,
myStore = Ext.data.StoreManager.lookup('MyStore');
var formPanel = me.down('#editFormPanel'),
record = formPanel.getRecord();
formPanel.updateRecord(record);
return record;
}
});
Since creating your editContainer and setting its data are two different steps in your code you can't use the initialize method of the editContainer.
But you can override the setRecord method of the editContainer, so it additionally disables the dropdown.
Since you push the editContainer onto a navigation view you could also use the activate event of the editContainer to disable the dropdown.
Maybe you can create a quick store on the fly, as a place to have a reference to that data...
//in your controller where you set the record
var mod = Ext.define('app.model.PanelDataModel', {
extend: 'Ext.data.Model',
config: {
fields: [
'roleValue'
]
}
});
var sto = Ext.create('Ext.data.Store', {
model: mod,
id:'PanelDataStore'
});
sto.add({
roleValue: record.roleValue
});
sto.sync();
//Now in your panel's initialize method:
var pstore = Ext.getStore('PanelDataStore');
pstore.load();
if(pstore.data.all[0].data.roleValue == 'unassigned'){
Ext.getCmp('roleValue').setDisabled(true);
}

Re-using EXTjs views with different "view models"

I'm creating a simple SCRUM system in order to teach myself to use EXT 4, so it goes without saying that I'm very new to the framework.
I have a little experience with MVC3 but I'm having some trouble adapting to the way EXT4 works.
My idea is to have 4 grids in a column layout Viewport. Each of the grids is currently its own View. However the Views are almost completely identical. Below is my first View. I've marked the lines that I have to change for the other 3 views.
Ext.define('AM.view.card.BacklogList', { // *** Variable
extend: 'Ext.grid.Panel',
alias: 'widget.backlogcardlist', // *** Variable
title: 'Backlog', // *** Variable
store: 'BacklogCards', // *** Variable
selType: 'cellmodel',
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
})
],
columns: [
{
header: 'ID',
dataIndex: 'external_id',
field: 'textfield',
width: 50
},
{
header: 'Name',
dataIndex: 'name',
field: 'textfield',
width: 200
},
{
header: 'Priority',
dataIndex: 'priority_id',
renderer: function (value) {
if (value == 3) {
return "L";
}
else if (value == 2) {
return "M";
}
else {
return "H";
}
},
width: 70,
field: {
xtype: 'combobox',
queryMode: 'local',
typeAhead: true,
store: 'Priorities',
displayField: 'name',
valueField: 'id',
listClass: 'x-combo-list-small'
}
},
{
xtype: 'actioncolumn',
width: 16,
items: [{
icon: 'Styles/Images/zoom.png', // Use a URL in the icon config
tooltip: 'Zoom In',
handler: function (grid, rowIndex, colIndex) {
var rec = grid.getStore().getAt(rowIndex);
alert("Edit " + rec.get('name'));
}
}]
}
]
});
Is there a way in EXTjs to pass a 'ViewModel'/Parameters to my View so that I can re-use it for each of my grids?
app.js
Ext.application({
name: 'AM',
appFolder: 'app',
controllers: ['BacklogCards', 'InprogressCards', 'ReviewCards', 'DoneCards'],
launch: function () {
Ext.create('Ext.container.Viewport', {
layout: 'column',
items: [
{
xtype: 'backlogcardlist'
},
{
xtype: 'inprogresslist'
},
{
xtype: 'reviewlist'
},
{
xtype: 'donelist'
}
]
});
}
});
Ok! I figured it out by reading this post here:
Extjs 4 MVC loading a view from controller
This is how I modified my app.js file:
Ext.application({
name: 'AM',
appFolder: 'app',
controllers: ['BacklogCards', 'InprogressCards', 'ReviewCards', 'DoneCards'],
launch: function () {
Ext.create('Ext.container.Viewport', {
layout: 'column',
defaults: { flex: 1 },
layout: {
type: 'hbox',
align: 'stretch',
padding: 5
},
items: [
Ext.widget('backlogcardlist',
{
title: "Backlog"
}),
Ext.widget('backlogcardlist',
{
title: "In Progress"
}),
{
xtype: 'reviewlist'
},
{
xtype: 'donelist'
}
]
});
}
});
Here I'm just changing the title property, but I imagine changing the other properties shouldn't be anymore difficult.

Categories