Instantiating objects in initComponent to be used in items - extjs

I keep coming across this problem. I have a custom component with 'items' defined. I want someone to use my component but define some configuration values, (ie I want a constructor for my widget!).
Lets say I have a component like this. I want an argument passed through 'someArgumentPassedInAsConfig', and i want this set on some items object in the items tree.
Ext.define('MySuperDupeyComponent', {
extend: 'Ext.panel.Panel',
alias: 'widget.MySuperDupeyComponent',
initComponent: function() {
alert('ch --> ' + this.someArgumentPassedInAsConfig);
},
items: [{
xtype: 'someWidget',
somePropertyIwantToSet: this.someArgumentPassedInAsConfig
}]
})
What is the best way to do this? I want someone to be able to use my component like this.
items: [
{
xtype: 'MySuperDupeyComponent',
someArgumentPassedInAsConfig: 'Blah blah blah'
}
Is the only way to use Ext.apply in the constructor?

Declaring the items inside the initComponent method should work, if this is what you're looking for.
Ext.define('MySuperDupeyComponent', {
extend: 'Ext.panel.Panel',
alias: 'widget.MySuperDupeyComponent',
initComponent: function() {
alert('ch --> ' + this.someArgumentPassedInAsConfig);
this.items = [{
xtype: 'someWidget',
somePropertyIwantToSet: this.someArgumentPassedInAsConfig
}]
this.callParents(arguments);
}
})
Should allow you to use it like this
Ext.create('Ext.panel.Panel', {
items: [{
xtype: 'MySuperDupeyComponent',
someArgumentPassedInAsConfig: 'Blah...'
}]
});
https://fiddle.sencha.com/fiddle/1i07

Related

Duplicate references when reusing the same component in Sencha app

Suppose we have defined a component (e.g. FieldSet) that we'd like to reuse in the single app (e.g. display/use it in 2 different modal windows.) This FieldSet has a reference, which we use to access it. The goal is to have these 2 windows contain independent fieldsets, so we can control and collect the inputs from each one separately.
Here's the sample fiddle that demonstrates the problem. As soon as any function triggers any lookupReference(...) call, Sencha issues the warning for "Duplicate reference" for the fieldset. It correctly creates two distinct fieldset components (by assigning different ids) on each window, but fails to properly assign/locate the references. As a result, any actions on one of these windows' fieldsets would be performed on the "unknown" one (probably on the first created one), messing up the UI behavior.
I see how it is a problem for Sencha to understand which component to use when operating on the reference, but there should be a way to reuse the same component multiple times without confusing the instances. Any help is greatly appreciated.
According to the docs on ViewController:
A view controller is a controller that can be attached to a specific view instance so it can manage the view and its child components. Each instance of the view will have a new view controller, so the instances are isolated.
This means that your use of singleton on your ViewController isn't correct, as it must be tied to a single view instance.
To fix this, I'd recommend making some modifications to your Fiddle, mainly removing the singleton: true from your VC class, accessing the views through lookup, and getting their VC's through getController to access your func method.
Ext.application({
name: 'Fiddle',
launch: function () {
/**
* #thread https://stackoverflow.com/questions/67462770
*/
Ext.define('fsContainerHandler', {
extend: 'Ext.app.ViewController',
alias: 'controller.fsContainerHandler',
// TOOK OUT singleton: true
func: function () {
var x = this.lookupReference('fsRef');
alert(x);
}
});
Ext.define('fsContainer', {
extend: 'Ext.container.Container',
xtype: 'xFSContainer',
controller: 'fsContainerHandler',
items: [{
xtype: 'fieldset',
title: 'myFieldset',
reference: 'fsRef'
}]
});
Ext.define('mainContainerHandler', {
extend: 'Ext.app.ViewController',
alias: 'controller.mainContainerHandler',
singleton: true,
onButton1Click: function () {
var win = this.getView().window1;
win.show();
// CHANGED LOGIC
win.lookup('theContainer').getController().func();
},
onButton2Click: function () {
var win = this.getView().window2;
win.show();
// CHANGED LOGIC
win.lookup('theContainer').getController().func();
}
});
Ext.define('mainContainer', {
extend: 'Ext.container.Container',
width: 400,
controller: 'mainContainerHandler',
window1: null,
window2: null,
initComponent: function () {
this.window1 = Ext.create('window1');
this.window2 = Ext.create('window2');
this.callParent(arguments);
},
items: [{
xtype: 'button',
text: 'Window 1',
reference: 'btn1',
handler: mainContainerHandler.onButton1Click,
scope: mainContainerHandler
}, {
xtype: 'button',
text: 'Window 2',
reference: 'btn2',
handler: mainContainerHandler.onButton2Click,
scope: mainContainerHandler
}]
});
Ext.define('window1', {
extend: 'Ext.window.Window',
title: 'Window1',
modal: true,
width: 100,
height: 100,
closeAction: 'hide',
// ADDED referenceHolder
referenceHolder: true,
items: [{
xtype: 'xFSContainer',
// ADDED reference
reference: 'theContainer'
}]
});
Ext.define('window2', {
extend: 'Ext.window.Window',
title: 'Window2',
modal: true,
width: 100,
height: 100,
closeAction: 'hide',
// ADDED referenceHolder
referenceHolder: true,
items: [{
xtype: 'xFSContainer',
// ADDED reference
reference: 'theContainer'
}]
});
Ext.create('mainContainer', {
renderTo: document.body
});
}
});

Using grid as field in ExtJS

Binding model property to a form field is pretty easy in ExtJS:
// ...skipped everything until fields config for brevity
}, {
xtype: 'textfield',
bind: '{modelInstance.someField}'
}, { // ...
In this case, modelInstance string field someField will be synchronized to textbox value, thanks to two way binding. And that is great.
What I want to achieve is to get same kind of behavior in the case when model field is not a string but an array. This is the model:
Ext.define('namespace.model.CustomModel', {
fields: ['easyField', {
name: 'hardField',
type: 'auto' // This will be an array during runtime
}],
idProperty: 'easyField'
});
I would like to do something like this:
// ...skipped everything until fields config for brevity,
// assume that viewmodel and everything else are set up as expected
}, {
xtype: 'textfield',
bind: '{modelInstance.easyField}'
}, {
xtype: 'gridfield',
bind: {
gridArray: '{modelInstance.hardField}'
}
}, { // ...
Understandably, I want gridfield component to extend Ext.grid.Panel and synchronize its store data to modelInstance field hardField.
Currently I have this:
Ext.define('namespace.form.field.GridField', {
extends: 'Ext.grid.Panel',
xtype: 'gridfield',
// skip requires for brevity
viewModel: {
stores: {
gridFieldItems: {
type: 'gridfielditems' // Simple in memory store
}
},
data: {
}
},
store: '{gridFieldItems}',
// This config enables binding to take place as it creates getters and setters,
// gridArray is set initially to '{modelInstance.hardField}' as expected
config: {
gridArray: null
},
// This is necessary for this grid-field component to update
// '{modelInstance.hardField}' back in the owners viewModel.
publishes: {
gridArray: true
},
// ???
// bind: {
// gridArray: bind gridArray to store data somehow?
// }
});
Here's the problem:
how do I inject existing modelInstance.hardField array as gridFieldItems store initial data,
how do I bind gridArray config to store data so that it is updates as we go along cruding the grid,
do all of these in an elegant MVVM way without writing a bunch of listeners trying to force syncing between JS objects.
Please provide tested solution which is known to work, I already tried a lot of different ways myself, but without success so far.
Here is a working fiddle to achieve this binding. The easy way is to bind the array field with "data" attribute of a store.
A good suggestion on the work you've done is to avoid defining a viewmodel inside a generic component (gridfield) but use viewmodels only on you application specific views.
On you generic component you should define only configuration attributes with setter/getter/update logics in order to be able to use them with bind. In this case there is no need of custom properties as the store is enough.
edit
To avoid the "easy binding" you can implement the set/get logic of the array in your girdfield component. Fore example using the "updateGridArray" method called by the setter and the "datachanged" event of the store.
The fiddle is updated and the example girdfield uses the cell editing plugin to show the 2-way binding.
fiddle: https://fiddle.sencha.com/#view/editor&fiddle/2a3b
Ext.define('Fiddle.model.CustomModel', {
extend: 'Ext.data.Model',
fields: ['easyField', {
name: 'hardField',
type: 'auto' // This will be an array during runtime
}],
idProperty: 'easyField'
});
Ext.define('Fiddle.fiddleview.CustomViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.fiddleview',
data: {
currentModel: null
}
});
Ext.define('Fiddle.form.field.GridField', {
extend: 'Ext.grid.Panel',
xtype: 'gridfield',
config: {
gridArray: null
},
publishes: [
'selection',
'gridArray'
],
selModel: 'cellmodel',
plugins: {
cellediting: {
clicksToEdit: 1
}
},
columns: [{
text: 'Column 1',
flex: 1,
editor: true,
dataIndex: 'field1'
}],
initComponent: function () {
this.store = {
fields: ['field1'],
data: [],
listeners: {
scope: this,
datachanged: function (store) {
this.setGridArray(store.getRange().map(function (record) {
return record.getData();
}));
}
}
};
this.callParent();
},
updateGridArray: function (gridArray) {
this.getStore().suspendEvent('datachanged');
if (Ext.isEmpty(gridArray)) {
this.getStore().removeAll();
} else {
this.getStore().loadData(gridArray);
}
this.getStore().resumeEvent('datachanged');
}
});
var myView = Ext.create('Ext.container.Container', {
renderTo: Ext.getBody(),
viewModel: 'fiddleview',
items: [{
xtype: 'gridfield',
bind: {
gridArray: '{currentModel.hardField}'
}
}]
});
// Bind on model's array to check two-way update
// It will execute also on cell edit
myView.getViewModel().bind('{currentModel.hardField}', function (value) {
window.alert('model\'s array changed');
});
// The binding is now active, when the "currentModel" in the viewmodel changes, grid rows are refresched
myView.getViewModel().set('currentModel', new Fiddle.model.CustomModel({
hardField: [{
field1: 'value1'
}]
}));
// Also when data changes in the "currentModel"
myView.getViewModel().get('currentModel').get('hardField').push({
field1: 'value2'
});

ExtJS 5: Using lookupReference in ViewController beforeInit

I'm trying to look up a reference to one of my components in the ViewController's beforeInit, so I can set some variables on myComponent before it's created, but lookupReference does not find my component in the beforeInit call... it's found in the ViewController's init method, but I'm assuming because the view hasn't actually been rendered, the reference hasn't been created.
So I figured I'd try initComponent, but once again, the lookupReference doesn't work in there... I know I could create an itemId and look for this in initComponent, but that's last resort for me... I'd rather use the reference. See this Fiddle example and code:
Ext.application({
name: 'Fiddle',
launch: function() {
Ext.define('MyViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.myview',
beforeInit: function() {
console.log('beforeInit: I want this to exist before myComponent is rendered', this.lookupReference('myComponent'));
},
init: function() {
console.log('init', this.lookupReference('myComponent'));
}
});
Ext.define('MyView', {
extend: 'Ext.panel.Panel',
controller: 'myview',
title: 'My View',
height: 400,
width: 400,
items: [{
xtype: 'textfield',
reference: 'myComponent',
fieldLabel: 'My Component'
}],
initComponent: function() {
// I want this to happen before myComponent is rendered
console.log('initComponent: I want this to exist before myComponent is rendered', this.lookupReference('myComponent'));
this.callParent();
console.log('initComponent callParent', this.lookupReference('myComponent'));
}
});
Ext.create('MyView', {
renderTo: Ext.getBody()
});
}
});
Does anyone have any guidance on how to accomplish something like this?

ExtJs How to Loading data into form panel from store/model

i'm really new to extjs 4. im try to load default value form load as bellow
launch: function() {
Ext.create('panelid', {renderTo: 'form'});
var form = this.up('panelid').getForm();
var model = Ext.getCmp('modelid');
form.loadRecord(model);
}
that not work form me that gave to me undefined form
this my view class
Ext.define('panelid', {
extend: 'Ext.form.Panel',
alternateClassName: [
'confpanel'
],
requires: [
'Ext.container.Container',
'Ext.form.field.File',
'Ext.button.Button'
],
initComponent: function() {
var me = this;
Ext.applyIf(me, {
items: [
{
xtype: 'textfield',
itemId: 'NAME_2',
fieldLabel: 'SDFA',
name: 'NAME_2'
},
{
xtype: 'button',
handler: function(button, e) {
var form = this.up('form').getForm();
if(form.isValid()){
form.submit({
url: 'AA?submit=true'
});
}
},
itemId: 'btnSave',
text: 'Save'
}
]
});
me.callParent(arguments);
}
});
my store userClassName name is 'storeid' and my model userClassName is modelid this very simple question but it really hard to understand data communication in between server-client. please help to me this simple question
I see a couple of errors on your code:
renderTo should only be used with Elements and not components.
E.g. Ext.getBody() or a div id on your html page
For containers you should use applyTo.
And a model is not a Component. You cannot use getCmp to get the model reference.
You will have to define it the same way you defined the panel and then use the name you specified there as a reference.
Check the sencha documents on how to do all this. Its very well explained there.

creating xtype in Ext.Define and calling it in TabPanel items:[]

I am new to extjs in general, specially to 4 version:
I have created a class:
Ext.define('MyPanel', {
extend:'Ext.panel.Panel',
views: ["MyPanel"],
config: {
width: 200,
height: 300,
title: "HELLO"
},
constructor:function(config) {
this.initConfig(config);
return this;
},
alias: 'widget.MyPanel'
});
Next, I want to call this class in form of XTYPE in a tabPanel items:[]:
I did like this:
items: [{
title: 'Kontakt',
id: 'kontaktTab',
closable:true,
closeAction: 'hide',
layout: 'fit',
items:[{
xtype: "MyPanel"
}]
No luck yet, all I get is :
Cannot create an instance of unrecognized alias: widget.MyPanel"
You must think, what a noob....
;-)
Someone please help!!!
When you are defining your view (MyPanel), why have you set views property?
Ext.define('MyPanel', {
extend:'Ext.panel.Panel',
alias: 'widget.MyPanel'
views: ["MyPanel"], <------ Why do you need this???
config: {
width: 200,
height: 300,
title: "HELLO"
},
constructor:function(config) {
this.initConfig(config);
return this;
}
});
And when you are making use of the new view, you need to specify it in requires. Here is an example:
Ext.define('MyApp.view.Viewport',{
extend: 'Ext.container.Viewport',
layout: 'border',
requires: [
'MyPanel' <--- This will ensure that this view file needs to be loaded
],
.
.
.
items: [{
title: 'Kontakt',
id: 'kontaktTab',
closable:true,
closeAction: 'hide',
layout: 'fit',
items:[{
xtype: "MyPanel"
}]
Hrm, have you tried lowercasing your alias. I thought aliases were always stored and fetched lowercase, but not sure about it
Ah, hehe, I completely overlooked something:
with an 'alias' you are creating a new class reference in the ExtJS class list. So by adding the alias like you did above, you can instantiate it by calling
var newMyPanel = Ext.create('widget.MyPanel');
However, if you are adding an instance with a xtype specifier you have to omit the widget part and just do:
var myContainer = Ext.create('Ext.panel.Panel',{
items: [{
xtype: 'MyPanel'
}]
});
With the above code, Ext will look for class with the alias 'widget.MyPanel'.
Apart from this, I think your constructor is a bit funky looking. The constructor should not return itself (like you would do in a Perl constructor for instance)
This is enough:
constructor: function() {
this.callParent(arguments);
// Your own code here
}
Cheers and let me know if it helps
Rob
all u have to do is declare it like that :
var panel1 = Ext.create('Ext.app.myPanel',{title : 'panel 1',height:350});//title and hight are optionals if u have already defined them
and then use it like this:
...
items : [panel1 ]
...
and you may need to require it :
Ext.require([
, 'Ext.app.myPanel'
]);
and put the mypanel.js an app folder
hope this helps

Resources