I am following the 'Getting started tutorial' and I have this simple Ext.js code:
Ext.define('TutorialApp.view.Main', {
extend: 'Ext.container.Container',
requires:[
'Ext.tab.Panel',
'Ext.layout.container.Border'
],
xtype: 'app-main',
layout: {
type: 'border'
},
items: [{
region: 'west',
xtype: 'panel',
title: 'west',
width: 150
},{
region: 'center',
xtype: 'tabpanel',
items:[{
title: 'Center Tab 1'
}]
}],
listeners: {
afterrender: function () {
alert('hi there')
}
}
});
I'd like to know how to get the "alert" pop up to say, "Hi there red" where "red" is the user that has just successfully performed Basic authentication.
It depends on whether your Basic authentication is performed on accessing the whole page, or only on your client side application accessing a restricted area on the same domain via AJAX.
In the first case you will need the server side to pass the Basic auth username to the client (there is no way JavaScript can access it otherwise). This may be done by getting the server insert a custom bit of JavaScript containing the username (and potentially other useful stuff that only the server knows):
<script type="text/javascript">
window.CONTEXT = {
// other useful stuff
username: 'red'
}
</script>
With the above in place, your alert will be simply:
alert('Hi there ' + CONTEXT.username)
In the second case (sending Basic auth credentials via AJAX) your client side will know the username in the first place, so upon authentication success it will know what name to show in the alert. The actual code will depend on the component doing the authentication, but a good approach would be to have a singleton for it that will have methods for performing authentication, telling whether user is authenticated and what the username is. So your alert will simply call it:
alert('Hi there ' + TutorialApp.Auth.getUsername())
Related
I have a controller, and I want to pass a simple string value to the next View.
For that, I am creating the View like this.
var nextView = Ext.create('MyApp.view.NextView', {
content: 'value'
});
Ext.Viewport.add(nextView);
Ext.Viewport.animateActiveItem(nextView, {
type: 'slide',
direction: 'left'
});
On the NextView, I have a label and I want to set the HTML property of the label to the value that I am passing from the controller. ie. value.
My NextView looks like this.
Ext.define('MyApp.view.NextView', {
extend: 'Ext.form.Panel',
config: {
content: 'null',
items: [{
xtype: 'label',
html: 'value'
}]
}
});
I am not sure how to proceed from here. I can't have the NextView as a form. I just need to pass one string value in this situation.
What's the best way to achieve this?
Use initialize method to access config data like this:
Ext.define('MyApp.view.NextView', {
extend: 'Ext.form.Panel',
config: {
content: 'null',
items: [
{
xtype: 'label',
html: 'value'
}
]
},
initialize : function(){
this.callParent();
var val = this.config.content;
this.down('label').setHtml(val);
}
});
PS Feel free to use your favourite selector in down function
I know the question has been answered. But I just digged up a pretty natural way to pass data from controller to a view (using view's constructor). I use this in my integration of web desktop to my app.
In controller, pass data to the constructor of the view as followed:
loiTab = Ext.create('Iip.view.giips.admission.DetailedLoiTab', {closable: true, selectedLoiData: selected[0].data});
In the view, spin up a constructor as followed:
constructor: function(selectedLoiData) {
Ext.applyIf(this, selectedLoiData);
this.callParent();
},
The following method lives in the same file as the constructor. You can access selectedLoiData from any where in the view the constructor lives as followed:
initComponent: function(){
console.log(this.selectedLoiData);
}
I am trying to write a reusable item selection panel where the user has a grid with items he can choose from and a small text field that he can use to filter the content of the grid. Right now the (simplified) view code looks like this and works.
Ext.define('MyApp.view.items.ItemSelectorPanel', {
extend: 'Ext.panel.Panel',
require: 'MyApp.view.items.SimpleItemGrid',
alias: 'widget.ItemSelectorPanel',
layout: 'form',
config: {
itemStore: false
},
constructor: function(config) {
this.initConfig(config);
this.superclass.constructor.call(this, config);
this.add([
{
fieldLabel: 'Filter',
name: 'filter'
},
{
xtype: 'SimpleItemGrid',
collapsible: true,
store: this.getItemStore()
}
]);
return this;
}
});
As you can see the ItemSelectorPanel uses the config property to expose an interface where the calling site can specify which item store to use.
Calling site (in this case the panel is added to a TabPanel):
var panelToAdd = {
xtype: 'panel',
title: 'New Selection',
closable: true,
padding: 10,
items: [{
title: 'Select node',
xtype: 'ItemSelectorPanel',
itemStore: itemStore
}]
};
Now, I love the declarative style of ExtJS 4 and how it helps to follow the MVC pattern. I would prefer to have the least amount of code possible in the views. Unfortunately this does not work:
Ext.define('MyApp.view.items.ItemSelectorPanel', {
/* ... same as above ... */
constructor: function(config) {
this.initConfig(config);
this.superclass.constructor.call(this, config);
return this;
},
items: [
{
fieldLabel: 'Filter',
name: 'filter'
},
{
xtype: 'SimpleItemGrid',
collapsible: true,
store: this.getItemStore // <-- interesting part
}
]
});
Is there a way to expose the config of a nested/sub component via the config property of the parent property in a declarative manner?
First something in general
Never add an object outside a function within a class definition unless you exactly know what you are going to do. Cause if you do so all instances will share the same instance of that object. I think I do not need to mention where this leads to...
If you have a need to place a object there you should clone it within the constructor.
To your code
I dunno what this.initConfig(config); does but the config variable is not the one from your class, it is the one from the constructor argument. I recommend you also to use initComponent() for initialization instead of the constructor() unless you have a defined need for using the constructor, which in your case you don't seem to have.
Also a 'config' is not forwarded cause it don't get executed up->bottom but bottom->up where a config get's hand up and all other properties are (already) inherited.
I still do not exactly know what your goal is, therefore I cannot give you any advice how you should do this but I can say for sure that the way you do it will lead to problems.
Edit
I still not sure that I have fully understand your needs but the following should work (if you need the listeners too you might take a look at the Ext.util.Bindable mixin)
Ext.define('MyApp.view.items.ItemSelectorPanel', {
extend: 'Ext.panel.Panel',
require: 'MyApp.view.items.SimpleItemGrid',
alias: 'widget.ItemSelectorPanel',
layout: 'form',
initComponent: function() {
// Initialize the store (might be a instance or a storeId)
var store;
if (this.itemStore) {
store = Ext.data.StoreManager.lookup(store);
}
this.itemStore = store || null;
// Add is not valid (at least not before the parent inits are executed)
this.items = [{
fieldLabel: 'Filter',
name: 'filter'
}, {
xtype: 'SimpleItemGrid',
collapsible: true,
store: this.getItemStore()
}];
this.callParent(arguments);
},
getItemStore: function() {
return this.itemStore;
}
});
No, you can't do it in the way you've described. The reason is pretty simple, let's take this as an example:
Ext.define('MyClass', {
someProp: this.foo(),
foo: function(){
return bar;
}
});
Here, we call the define() method and we pass it an object as the configuration. As such, the whole object (including the call to foo()) is evaluated before it's even passed to define, so the class/method doesn't even exist at that point.
Even if you could do that, here's also the complication that foo is an instance method on the class, but the way you're attempting to call it is as though it's a static method.
So, the answer is, you'll need to use some kind of method to do so, initComponent is typically preferred over the constructor.
You can define items in declaration of your class but you cannot call any method from your class at time of declaration. To solve it, define only items without store and than use initComponent method to set store for your view.
I didn't see an answer that addressed the original question. Here is what I've found to work ...
Creating an instance of myClass, passing in a config 'foo' with value 'bar'
var myClassInstance = Ext.create('MyApp.view.myClass', {
foo: 'bar'
});
myClass is defined as follows :
Ext.define('MyApp.view.myClass', {
extend: 'Ext.container.Container',
alias: 'widget.myclass',
config: {
foo: 'defaultVal'
},
constructor: function(configs) {
this.callParent(arguments); //create class, calls initComponent
var try1 = getFoo(); //try1 is 'defaultVal'
this.initConfig(configs); //initializes configs passed in constructor
var try2 = getFoo(); //try2 is 'bar'
},
initComponent: function() {
//myClass declaration here ...
this.callParent();
}
I am trying to dynamically create grid.Panels and add them to my View, since I don't know have many "Views" I need before I load the data. For example, I have a number of people in different groups, once I load the data, I want to create a grid.Panel for each group and put the correct people in it.
The problem is that my application hangs when I do it (probably because the Store is being loaded recursively)
How do I add a grid.Panel to my View with data from a Store without it hanging?
My Controller:
Ext.define('NG.controller.Navigation', {
extend: 'Ext.app.Controller',
refs: [{
selector: 'group',
ref: 'groupPanel'}
],
stores: ['Groups'],
init: function() {
this.control({
'navigation': {
itemdblclick: this.onNavigationSelection
}
});
},
onNavigationSelection: function(view, record, item, index, eventobj, obj) {
var groupsstore = this.getGroupsStore();
var group1 = Ext.create('Ext.grid.Panel', {
store: groupsstore,
title: 'Group 1',
columns: [
{header: 'Name', dataIndex: 'name'},
{header: 'Mail:', dataIndex: 'mail'}
]
});
groupsstore.load();
this.getGroupPanel().add(group1);
}
});
My View:
Ext.define('NG.view.Group', {
extend: 'Ext.panel.Panel',
alias: 'widget.group',
store: 'Groups',
initComponent: function() {
this.callParent();
}
});
And my Store (not sure if it is needed):
Ext.define('NG.store.Groups', {
extend: 'Ext.data.Store',
requires: 'NG.model.Person',
model: 'NG.model.Person'
});
Best regards and thank you in advance!
Andreas
After walking through each reference I could not find any errors in the code. However I found a bug report about a similar problem:
http://www.sencha.com/forum/showthread.php?141804-4.0.5-Grid-Uncaught-RangeError-Maximum-call-stack-size-exceeded
I downloaded the Framework last week, and there was an update a few days ago, which solved this bug and my problem as well.
Thank you for your help!
The store.load() event is asynchronous.
Try switching the add panel and load store events around:
this.getGroupPanel().add(group1);
groupsstore.load();
The other thing is it doesn't look like you are passing anything to the store to load it. So you can just autoload it and have the data ready ahead of time.
In one file, I am creating a tab panel with two tabs. I am trying to register the component for use in a separate file. Consider the following:
Ext.ns('DVI');
var backtestTab = {
xtype: 'groupingstore'
}
var intradayTab = {
xtype: 'groupingstore'
}
DVI.DviDashboard = new Ext.TabPanel({
activeTab: 0,
items: [backtestTab, intradayTab]
});
Ext.reg('dviDashboard', DVI.DviDashboard);
Assume that this page properly populates the tabs with the grouping stores. In the page that calls this script, the code is as follows:
var dashboard = {
xtype: 'tabpanel',
id: 'port_dash-panel',
activeTab: 0,
items:[{
title: 'Dashboard',
xtype: 'dviDashboard'
}]
};
Which returns the following error:
b[d.xtype || e] is not a constructor
Upon researching, the above error indicates the component cannot be found or is not defined.
Note however, the below code does work and render the tabpanel:
var dashboard = {
xtype: 'tabpanel',
id: 'port_dash-panel',
activeTab: 0,
items:[DVI.DviDashboard]
};
I'm only showing snipits. The dashboard tabpanel is rendered in a viewport.
I guess the first question is can I even register a component this way? And of course the second question is, why doesn't this work? :)
You are trying to register an instantiated object which won't work. Ext.reg normally takes components that were created by the Ext.extend method.
DVI.DviDashboard = Ext.extend(Ext.TabPanel,{
initComponent: function(){
Ext.apply(this, {
activeTab: 0,
items: [backtestTab, intradayTab]
});
DVI.DviDashboard.superclass.initComponent.call(this)
}
});
Ext.reg('dviDashboard', DVI.DviDashboard);
Here is an old but helpful link about writing classes in Ext 3.* :
http://blog.extjs.eu/know-how/writing-a-big-application-in-ext/
in my php code i have a table with rows:
name actions
category 1 edit
category 2 edit
..... edit
every edit buttons have an id for ex: <a href="edit('.$raw['id'].')" id="show-btn'.$raw['id'].'">
now my function edit is:
var win;
var button;
function edit(idd){
button = Ext.get('show-btn'+idd);
button.on('click', function(){
// create the window on the first click and reuse on subsequent clicks
if(!win){
win = new Ext.Window({
applyTo:'hello-win',
layout:'column',
closeAction:'hide',
plain: true,
autoHeight:true,
items: new Ext.FormPanel({
applyTo: 'hello-tabs',
}),
buttons: [{
text:'Modify',
handler: function(){
win.hide();
document.form_edit.submit();
}
},{
text: 'Close',
handler: function(){
win.hide();
}
}]
});
}
win.show(this);
});
};
This script works perfectly but on bauble click, how can i do this on one click.
I now the problem is on button.on('click')
Many thanks, please help me with this issues
It seems like some level of functionality is being added on your first click and then that is used for subsequent clicks. I'd suggest putting either an alert or a console.log (if your browser supports it) inside of the edit function both before and after the if(win) test.
I'm also curious -- if you're trying to have this execute for an entire table, wouldn't that win global variable cause problems?
I think you're better off pre-populating win and using a local variable:
function edit(idd){
button = Ext.get('show-btn'+idd);
// this can be moved into button.on('click', function(){, but that may be
// causing the problem...
// come to think of it... the example here:
// http://dev.sencha.com/deploy/dev/examples/window/hello.js
// has this inside the button.on method. You may want to try it both ways.
if(!button.win){
button.win = new Ext.Window({
applyTo:'hello-win',
layout:'column',
closeAction:'hide',
plain: true,
autoHeight:true,
items: new Ext.FormPanel({
applyTo: 'hello-tabs',
}),
buttons: [{
text:'Modify',
handler: function(){
win.hide();
document.form_edit.submit();
}
},{
text: 'Close',
handler: function(){
win.hide();
}
}]
});
}
button.on('click', function(){
// the window already exists, so no need to worry about creating it.
button.win.show(this);
});
};
Could that work? If not, we will need to see more code.