Below, you will find two examples using the template-render pattern. Example A does not work correctly. The value cmp.helloLiEl is undefined in the listener function. The afterrender callback does not know about the renderSelectors.
In Example B, the afterrender is assigned an inline function to attach a listener to the template list item. I thought that supplying the function with the scope would suffice, but this did not work as expected.
How can I get Example A's onRender listener to access the renderSelectors?
Example A
Ext.onReady(function() {
Ext.define('Exmple.component.Hello', {
extend: 'Ext.Component',
title: 'Hello Test',
renderTpl: [
'<ul>',
'<li class="lang" id="helloSelector-li">Hello {name}!</li>',
'</ul>'
],
renderData: {
name: 'Joe'
},
renderSelectors: {
helloLiEl: '#helloSelector-li'
},
listeners: {
afterrender: {
fn : this.onRender,
scope : this
}
},
onRender: function(cmp) {
console.log(cmp.helloLiEl);
cmp.mon(cmp.helloLiEl, 'click', function() {
alert('Hello again...');
});
}
});
Ext.create('Exmple.component.Hello', {
renderTo: Ext.getBody(),
renderData: {
name: 'Bob'
}
});
});
#helloSelector-li {
background-color: yellow;
width: 35px;
height: 30px;
}
<link href="http://cdn.sencha.io/ext-4.1.1-gpl/resources/css/ext-all.css" rel="stylesheet" />
<script src="http://cdn.sencha.io/ext-4.1.1-gpl/ext-all.js"></script>
Example B
Ext.onReady(function() {
Ext.define('Exmple.component.Hello', {
extend: 'Ext.Component',
title: 'Hello Test',
renderTpl: [
'<ul>',
'<li class="lang" id="helloSelector-li">Hello {name}!</li>',
'</ul>'
],
renderData: {
name: 'Joe'
},
renderSelectors: {
helloLiEl: '#helloSelector-li'
},
listeners: {
afterrender: function(cmp) {
console.log(cmp.helloLiEl);
cmp.mon(cmp.helloLiEl, 'click', function() {
alert('Hello again...');
});
}
}
});
Ext.create('Exmple.component.Hello', {
renderTo: Ext.getBody(),
renderData: {
name: 'Bob'
}
});
});
#helloSelector-li {
background-color: yellow;
width: 35px;
height: 30px;
}
<link href="http://cdn.sencha.io/ext-4.1.1-gpl/resources/css/ext-all.css" rel="stylesheet" />
<script src="http://cdn.sencha.io/ext-4.1.1-gpl/ext-all.js"></script>
The issue here is your scope.
Also the onRender function is overriding the method of the same name which it inherits (http://docs.sencha.com/extjs/4.1.3/#!/api/Ext.util.Renderable-method-onRender) so hence I have changed the function name to myTest
Ext.onReady(function() {
Ext.define('Exmple.component.Hello', {
extend: 'Ext.Component',
title: 'Hello Test',
renderTpl: ['<ul>', '<li class="lang" id="helloSelector-li">Hello {name}!</li>', '</ul>'],
renderData: {
name: 'Joe'
},
renderSelectors: {
helloLiEl: '#helloSelector-li'
},
initComponent: function() {
me = this;
me.listeners = {
afterrender: {
fn: this.myTest,
scope: this
}
}
this.callParent();
},
myTest: function(cmp) {
cmp.mon(cmp.helloLiEl, 'click', function() {
alert('Hello again...');
});
}
});
Ext.create('Exmple.component.Hello', {
renderTo: Ext.getBody(),
renderData: {
name: 'Bob'
}
});
});
Have a look at the answer added here https://stackoverflow.com/a/23806475
Demo: https://fiddle.sencha.com/#fiddle/gqj
Related
On ExtJS 6.02 I would like to bind a ViewModel to my component config parameters.
I tried what it says here: https://stackoverflow.com/a/27284556 but it doesn't work, it's not binding.
This prints Null instead of 123:
Ext.define('MyApp.viewmodel.Test', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.test',
data: {
serverPathData: ''
}
});
Ext.define('MyApp.view.TestFileField', {
extend: 'Ext.form.field.Text',
xtype: 'TestFileField',
viewModel: {
type: 'test'
},
config: {
serverPath: null
},
publishes: 'serverPath',
bind: {
serverPath: '{serverPathData}'
}
, initComponent: function() {
this.getViewModel().set('serverPathData', '123');
this.getViewModel().notify();
console.log(this.getServerPath());
this.callParent()
}
});
Ext.application({
name: 'MyApp',
launch: function() {
var testFileField = Ext.widget({
xtype: 'TestFileField',
renderTo: Ext.getBody()
});
}
});
Using testFileField.getViewModel().notify(); does solve the problem in this example, but in my case it doesn't.
I have a shared viewModel.
Found one solution, if I call this.initBindable(); on initComponent it works:
Ext.define('MyApp.viewmodel.Test', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.test',
data: {
serverPathData: ''
}
});
Ext.define('MyApp.view.TestFileField', {
extend: 'Ext.form.field.Text',
xtype: 'TestFileField',
viewModel: {
type: 'test'
},
config: {
serverPath: null
},
publishes: 'serverPath',
bind: {
serverPath: '{serverPathData}'
}
, initComponent: function() {
this.initBindable();
this.getViewModel().set('serverPathData', '123');
this.getViewModel().notify();
console.log(this.getServerPath());
console.log(this.getViewModel().data);
this.callParent();
}
});
Ext.application({
name: 'MyApp',
launch: function() {
var testFileField = Ext.widget({
xtype: 'TestFileField',
renderTo: Ext.getBody()
});
}
});
The problem with this is that this method is private and is already called on beforeRender and afterRender and I don't know if calling it on initComponent or constructor could cause some problem.
I'm trying to add a message alert to an ext object but I can't seem to get it. If a user is logged in then an object is pushed with the URL and they can access it. If they're not logged in, a pop-up is supposed to appear and tell them to login.
Is there an easy way to add a pop-up to an Ext pdf link?
Code
var treeObj = [];
function loggedIn() {
if (typeof isLoggedIn != 'undefined') {
return isLoggedIn;
} else {
return false;
}
}
if (loggedIn()) {
treeObj.push({
text: 'Test File',
leaf: true,
href: '/secure/test.pdf',
cls: 'tree-pdf',
});
} else{
treeObj.push({
text: 'Test File',
leaf: true,
cls: 'tree-pdf',
listeners: {
render: function(c){
c.getEl().on('click', function(){
Ext.Msg.alert('Insufficient Rights', 'You have selected a secure resource.');
}, c);
}
}
});
}
You can try this Fiddle
Code snippet:-
Ext.application({
name: 'Fiddle',
launch: function () {
var treeObj = [],
isLoggedIn;// = true;
function loggedIn() {
if (typeof isLoggedIn != 'undefined') {
return isLoggedIn;
} else {
return false;
}
}
if (loggedIn()) {
treeObj.push({
text: 'Test File',
leaf: true,
href: '/secure/test.pdf',
hrefTarget: '_blank',
cls: 'tree-pdf',
});
} else {
treeObj.push({
text: 'Test File',
leaf: true,
cls: 'tree-pdf'
});
}
Ext.create('Ext.tree.Panel', {
renderTo: document.body,
title: 'Simple Tree',
width: 300,
height: 250,
root: {
text: 'Root',
expanded: true,
children: treeObj
},
listeners: {
beforeitemclick: function (view, record, item, index, e, eOpts) {
if (!loggedIn()) {
Ext.Msg.alert('Insufficient Rights', 'You have selected a secure resource.');
return false;
}
}
}
});
}
});
so I am trying to modify the example Cumulative flow chart here so that it has a release dropdown, making it so that it only shows information pertaining to a given release. My problem is that when a new release is selected from the release dropdown, the graph does not reload itself, and so it never actually shows information pertinent to the selected release. I think I have implemented the listeners correctly but I am not sure, so I am wondering if someone could tell me why this is happening and how to fix it. Thanks! My code is below:
<!DOCTYPE html>
<html>
<head>
<title>Historical Summary</title>
<script type="text/javascript" src="/apps/2.0rc3/sdk.js"></script>
<script type="text/javascript">
Rally.onReady(function() {
Ext.define('Rally.example.CFDCalculator', {
extend: 'Rally.data.lookback.calculator.TimeSeriesCalculator',
config: {
stateFieldName: 'ScheduleState',
stateFieldValues: ['Defined', 'In-Progress', 'Completed', 'Accepted']
},
constructor: function(config) {
this.initConfig(config);
this.callParent(arguments);
},
getMetrics: function() {
return _.map(this.getStateFieldValues(), function(stateFieldValue) {
return {
as: stateFieldValue,
groupByField: this.getStateFieldName(),
allowedValues: [stateFieldValue],
f: 'groupByCount',
display: 'area'
};
}, this);
}
});
Ext.define('Rally.example.CFDChart', {
extend: 'Rally.app.App',
requires: [
'Rally.example.CFDCalculator'
],
launch: function() {
this.add({
xtype: 'rallyreleasecombobox',
fieldLabel: 'Filter by Release:',
project: this.getContext().getProject(),
//value: Rally.util.Ref.getRelativeUri(this.getContext().getRelease()),
listeners: {
select: this._onSelect,
ready: this._onLoad,
scope: this
}
});
},
_onLoad: function() {
this.add({
xtype: 'rallychart',
storeType: 'Rally.data.lookback.SnapshotStore',
storeConfig: this._getStoreConfig(),
calculatorType: 'Rally.example.CFDCalculator',
calculatorConfig: {
stateFieldName: 'ScheduleState',
stateFieldValues: ['Defined', 'In-Progress', 'Completed', 'Accepted']
},
chartConfig: this._getChartConfig()
//context: this.getContext();
});
},
_onSelect: function() {
var histChart = this.down('rallychart');
histChart.refresh({
storeConfig: {
filters: [this._getOwnerFilter()]
}
});
},
_getOwnerFilter: function() {
//var userCombo = this.down('rallyusersearchcombobox');
var releaseValue = this.down('rallyreleasecombobox');
return {
property: 'Release',
operator: '=',
value: releaseValue.getValue()
};
},
/**
* Generate the store config to retrieve all snapshots for stories and defects in the current project scope
* within the last 30 days
*/
_getStoreConfig: function() {
return {
find: {
_TypeHierarchy: { '$in' : [ 'HierarchicalRequirement', 'TestSet' ] },
Children: null,
_ProjectHierarchy: this.getContext().getProject().ObjectID,
_ValidFrom: {'$gt': Rally.util.DateTime.toIsoString(Rally.util.DateTime.add(new Date(), 'day', -30)) }
},
fetch: ['ScheduleState'],
hydrate: ['ScheduleState'],
sort: {
_ValidFrom: 1
},
context: this.getContext().getDataContext(),
limit: Infinity
};
},
/**
* Generate a valid Highcharts configuration object to specify the chart
*/
_getChartConfig: function() {
return {
chart: {
zoomType: 'xy'
},
title: {
text: 'Project Cumulative Flow: User Stories & Test Sets'
},
xAxis: {
tickmarkPlacement: 'on',
tickInterval: 1,
title: {
text: 'Date'
}
},
yAxis: [
{
title: {
text: 'Count'
}
}
],
plotOptions: {
series: {
marker: {
enabled: false
}
},
area: {
stacking: 'normal'
}
}
};
}
});
Rally.launchApp('Rally.example.CFDChart', {
name: 'Historical summary: test cases, stories, and defects'
});
});
</script>
<style type="text/css">
</style>
</head>
<body></body>
</html>
Your code errors with "Uncaught TypeError: undefined is not a function" on line
histChart.refresh
I modified example of ProjectCumulativeFlow to filter by Release. Full code is in this github repo.
Instead of extending Rally.app.App, I extended Rally.app.TimeboxScopedApp.
SnapshotStore may filter by Release, but requires ObjectID.
Here is the find:
find: {
_TypeHierarchy: { '$in' : [ 'HierarchicalRequirement', 'Defect' ] },
Release: this.getContext().getTimeboxScope().record.data.ObjectID,
Children: null,
_ProjectHierarchy: this.getContext().getProject().ObjectID
}
To update the app after Release selection check if the chart already exists (if yes, destroy it):
onScopeChange: function() {
if (this.down('#mychart')) {
this.down('#mychart').destroy();
}
this.add({
xtype: 'rallychart',
itemId: 'mychart',
storeType: 'Rally.data.lookback.SnapshotStore',
storeConfig: this._getStoreConfig(),
calculatorType: 'Rally.example.CFDCalculator',
calculatorConfig: {
stateFieldName: 'ScheduleState',
stateFieldValues: ['Defined', 'In-Progress', 'Completed', 'Accepted']
},
chartConfig: this._getChartConfig()
});
},
I have a menu which is defined like that:
Ext.define('MyApp.FileBrowserContextMenu', {
extend: 'Ext.menu.Menu',
initComponent: function() {
var me = this;
Ext.applyIf(me, {
items: [
{
xtype: 'menuitem',
text: 'Edit',
listeners: {
click: {
fn: me.onMenuitemClick,
scope: me
}
}
},
]
});
me.callParent(arguments);
},
onMenuitemClick: function(item, e, options) {
var server = this.record;
var win = Ext.create('widget.ServerWindow', {
record: server
});
win.show();
}
});
I would like to add new items after the definition, so I try like that:
First I defined the new MenuItem:
Ext.define('MyApp.GitMenuItem', {
extend: 'Ext.menu.Item',
alias: 'widget.gitmenuitem',
text: 'Git',
initComponent: function() {
var me = this;
Ext.applyIf(me, {
menu: {
xtype: 'menu',
items: [
{
xtype: 'menuitem',
text: 'Commit',
listeners: {
click: {
fn: me.onMenuitemClick,
scope: me
}
}
},
]
}
});
me.callParent(arguments);
},
onMenuitemClick: function(item, e, options) {
},
});
Then I try to attach the new menu item:
Ext.override(MyApp.FileBrowserContextMenu, {
initComponent: function () {
var me = this;
this.callParent();
me.items.items.push(Ext.create('widget.gitmenuitem'));
}
});
It seems to work, because the new MenuItem appears, but when I go over, Then new Item should appears but I get this error:
Uncaught TypeError: Cannot set property 'activeChild' of undefined
Any ideads ?
The usual way is the add method: menu.add(menuItem).
I just started using Sencha framework 2.x. This is my app:
app/app.js
Ext.Loader.setConfig({
enabled: true
});
Ext.application({
name: 'App',
controllers: ['Generators'],
models: [],
stores: [],
views: ['Main', 'Generator'],
launch: function() {
Ext.create('App.view.Main');
}
});
app/view/Main.js
Ext.define('App.view.Main', {
extend: 'Ext.NavigationView',
requires: [
'App.view.Generator'
],
config: {
fullscreen: true,
items: [
{
xtype: 'generatorview'
}
]
}
});
app/view/Generator.js
Ext.define('App.view.Generator', {
extend: 'Ext.Container',
xtype: 'generatorview',
id: 'generator',
config: {
layout: 'vbox',
items: [
{
xtype: 'panel',
html: 'My message: <a id="xxxSet">Set</a> :: <span id="xxxMsg">...</span>',
flex: 1
},
{
dock: 'bottom',
xtype: 'toolbar',
items: []
}
]
}
});
app/controller/Generator.js
Ext.define('App.controller.Generators', {
extend: 'Ext.app.Controller',
config: {
refs: {}
},
init: function() {
this.control({
'#xxxSet': { // QUESTION1
tap: 'setMyMessage'
}
});
},
setMyMessage: function() {
'#xxxMsg'.html('Set this message'); // QUESTION2
}
});
As you can see I placed questions in the last part (controller).
QUESTION1: How can I set a tap function to the element (#xxxSet)
defined in the view as HTML content.
QUESTION2: How can I set a
message the the element (#xxxMsg) defined in the view as HTML
content.
xxxSet = id of a button
xxxMsg = id of a message holder
Thx!
You can use Ext#get (which accepts a string which is the id) which will return a instance of Ext.dom.Element. With that, you can use the on method to add listeners (much like control) and then the setHtml method to update the contents.
init: function() {
Ext.get('xxxSet').on({
tap: 'setMyMessage',
scope: this
});
},
setMyMessage: function() {
Ext.get('xxxMsg).setHtml('Hello');
}
If you use itemId, you can not access it with Ext.get(). You can try Ext.ComponentQuery.query() instead of that.
init: function() {
Ext.ComponentQuery.query('#xxxSet').on({
tap: 'setMyMessage',
scope: this
});
},
setMyMessage: function() {
Ext.ComponentQuery.query('#xxxMsg).setHtml('Hello');
}