In our ExtJs 5.0.1 project we create components and at runtime we add additional css classes conditional.
We want to find them by a component query but the component query returns nothing for the programatically added css classes.
But when we define them hardcoded in the component config then the query returns the expected result.
How can we get references to the components with programatically added css classes?
Example View:
Ext.define('T.view.Main',{
extend: 'Ext.panel.Panel',
title: 'test',
cls: 'working', // static config
initComponent: function(){
this.callParent(arguments);
this.addCls('notworking'); // added at runtime
}
});
Example Application:
Ext.application({
name : 'T',
autoCreateViewport: 'T.view.Main',
launch: function(){
// the cls "working" assigned as config to the Main View is found by
// the ComponentQuery
var working = Ext.ComponentQuery.query('[cls~=working]');
// the cls "notWorking" assigned by addCls during runtime to the Main View
// is not found by the ComponentQuery
var notWorking = Ext.ComponentQuery.query('[cls~=notworking]');
Ext.Msg.alert('Static vs Dynamic', 'working: ' + working.length + ' notWorking: ' + notWorking.length);
}
});
Update
#Alexander suggested to add the additional cls before the callParent call , which sounds like an obvious solution but now even the .working cls is not found by the component query.
Ext.define('T.view.Main',{
extend: 'Ext.panel.Panel',
title: 'test',
cls: 'working', // static config
initComponent: function(){
this.cls += ' notworking';
this.callParent(arguments);
}
});
See the updated Sencha Fiddle.
Update 2
I may found the problem in the Component.js code, in the constructor the following happens
constructor: function (config) {
...
me.setupProtoEl();
// initComponent, beforeRender, or event handlers may have set the style or `cls` property since the `protoEl` was set up
// so we must apply styles and classes here too.
if (me.cls) {
me.initialCls = me.cls;
me.protoEl.addCls(me.cls);
}
if (me.style) {
me.initialStyle = me.style;
me.protoEl.setStyle(me.style);
}
me.renderData = me.renderData || {};
me.initComponent();
...
the proto el is initialized with some css classes by me.setupProtoEl();
it is checked if cls is set and it is then applied to the proto element and saved to initialCls property
initComponent function is called, when cls is changed in it, it is not noticed by the constructor anymore
In my opinion the steps 2 and 3 needs to be swapped in order to recognize changes of cls in the initComponent function.
cls is a configuration attribute for you to specify the CSS class. But addCls doesn't update it - it simply updates the class attribute on the underlying DOM element.
addCls: function(cls) {
var me = this,
el = me.rendered ? me.el : me.protoEl;
el.addCls.apply(el, arguments);
return me;
},
(source)
Because addCls doesn't update the cls attribute, your ComponentQuery call can't find it that way.
As to how you solve your problem: the simplest way would be to add a property of your own on the class that you update at the same time as you add the class. Then you can do a component query on your custom property. Though I'd include the xtype of your class, so as to avoid potential namespace conflicts.
As #RobertWatkins points out the cls of the component is not changed by addCls, it just updates the DOM element.
A solution to set the cls config during runtime, is to do it in the components constructor. The cls is then applied to the DOM element and to the component aswell. The component query is now able to find it.
cls: 'working',
constructor: function (config) {
this.cls += ' nowworking';
this.callParent(arguments);
},
The corresponding application snippet
// the component is retrieved
var working = Ext.ComponentQuery.query('[cls~=working]');
// the component is retrieved aswell
var nowWorking = Ext.ComponentQuery.query('[cls~=nowworking]');
A working fiddle.
Related
I am looking to use the component query function to find components not by attribute value, but by attribute name where the attribute name itself should be a wildcard.
So I have a component with the following config:
var component = Ext.create({
xtype: 'container',
foobar: 'something',
foobar2: 'something else'
});
I want to be able to do find all elements that have the attribute starting with foobar. Something like
var els = Ext.ComponentQuery.query('[foobar*]');
How would I go about achieving that? Is this an option?
I didn't find a default way in the documentation. What would a possible solution is a custom matcher function that checks each component for properties that start with given parameter. You could then make an override for all ext components to have this matcher function. (If you're already using custom components you could make a mixin)
For Example:
Ext.override(Ext.Component, {
insensetivePropQuery: function(prop) {
var matched = false;
Ext.Object.each(this, function(key, value, myself) {
if(Ext.String.startsWith(key, prop) === true) {
matched = true;
return false;
}
});
return matched;
}
});
Query example:
Ext.ComponentQuery.query('{insensetivePropQuery("foobar")}');
Here is a working sencha fiddle example (using ExtJS 7.3.x Material): example
I want to create a report where where we have capability to reparent our test cases from one test set to other.
in simple language, I need to remove the existing link with the test set and assign a new test set for my existing test case/cases.
I was referring to one existing report at https://github.com/RallyTechServices/test-picker/blob/master/deploy/App.html
listeners: {
scope: this,
artifactChosen: function(items){
Ext.create('Rally.data.WsapiDataStore',{
model:'TestSet',
pageSize: 1,
autoLoad: true,
filters: [{property:'ObjectID',value:record.get('ObjectID')}],
listeners: {
load: function(store,records){
console.dir(records.length);
var ts = records[0];
me._log(["Selected items:",items]);
var tc_store = ts.getCollection('TestCases');
me._log(["Got store:",tc_store]);
tc_store.add(items);
me._log("Added items.");
tc_store.sync({
// callback: function(){
// me._log("inside callback");
// me._getTestSets(me.iteration_selector.getValue());
// }
success: function(batch,options) {
me._getTestSets(me.iteration_selector.getValue());
},
failure: function(batch,options) {
me._getTestSets(me.iteration_selector.getValue());
}
});
}
}
});
}
}
Here If I try to use remove it gives me an error, I am not sure what is causing the issue.
Or How can I approach the issue. I need to change the test set of any user selected test case in my report. Can please some help me on the same.
The error is "Uncaught TypeError: Cannot read property 'getRef' of null". When it can add to the same object why cant it delete. I understand that it has only one value. but I tried removing the filters as well from code snippet mentioned below, but still it gives only current selected test case object id.
Thanks in Advance
I have some data that I'm getting from the server that depending on the situation may bring different fields, so what I have is this:
//This is the way i'm attaching the newly created template to the view
//Still no success
function processDataMethod(response){
//some processing here...
var details = Ext.widget('details');
details.config.itemTpl = new Ext.XTemplate(tplFields);
}
Ext.Ajax.request({
url: '...',
...,
success: function (response, request) {
var combinedData = processDataMethod(response);
operation.setResultSet(Ext.create('Ext.data.ResultSet', {
records: combinedData,
total: combinedData.length
}));
operation.setSuccessful();
operation.setCompleted();
if (typeof callback == "function") {
callback.call(scope || that, operation);
currentList.up().push(Ext.widget('details'));
}
}
});
Any help is appreciated, thanks!!
You have to make a distinction between a number of things:
currentList.up() returns a DOM element (Ext.dom.Element). This has no method push().
With Ext.widget('details', config); you can pass a config like {itemTpl: yourTemplate, data: yourData} to create an instance with a custom template and custom data.
To update your component after creation you can always do someWidget.update(data);.
A component can be rendered to an HTML element with the renderTo option.
A component can be appended to existing components in different ways and you can update the whole layout or parts of it in different ways. This is unnecessary if you are rendering to an HTML element.
Does that help you find your problem?
Can someone help explain / provide an example on how to use the LayoutManager within the Backbone Bolierplate?
Within app.js I can see a useLayout function that extends the main app object. Within here it appears to be setting a base layout element:
// Helper for using layouts.
useLayout: function(name, options) {
// Enable variable arity by allowing the first argument to be the options
// object and omitting the name argument.
if (_.isObject(name)) {
options = name;
}
// Ensure options is an object.
options = options || {};
// If a name property was specified use that as the template.
if (_.isString(name)) {
options.template = name;
}
// Create a new Layout with options.
var layout = new Backbone.Layout(_.extend({
el: "#main"
}, options));
// Cache the refererence.
return this.layout = layout;
}
Is that correct? If so, do I somehow the use the 'UseLayout' function with the applications Router? ...to add different UI elements/nested views to the main view?
Thanks.
I will usually have an "app" object that stores all my settings needed throughout the application. This object then extends some useful functions like the one you listed above. For example:
var app = {
// The root path to run the application.
root: "/",
anotherGlobalValue: "something",
apiUrl: "http://some.url"
};
// Mix Backbone.Events, modules, and layout management into the app object.
return _.extend(app, {
// Create a custom object with a nested Views object.
module: function(additionalProps) {
return _.extend({ Views: {} }, additionalProps);
},
// Helper for using layouts.
useLayout: function(options) {
// Create a new Layout with options.
var layout = new Backbone.Layout(_.extend({
el: "#main"
}, options));
return this.layout = layout;
},
// Helper for using form layouts.
anotherUsefulFunction: function(options) {
// Something useful
}
}, Backbone.Events);
});
Now in my router I would do something like:
app.useLayout({ template: "layout/home" })
.setViews({
".promotional-items": new Promotions.Views.PromotionNavigation(),
".featured-container": new Media.Views.FeaturedSlider({
vehicles: app.vehicles,
collection: featuredCollection
})
}).render().then(function() {
//Do something once the layout has rendered.
});
I have just taken a sample from one of my applications, but I am sure you can get the idea. My main layout is basically just a layout template file which holds the elements so the views can be injected into their respective holders.
You would use it as if you're using a regular Backbone View. Instead of building the View directly, you can use this to create a new instance. The code you posted is a wrapper object on top of the Backbone Layout Manager extension with el: #main set as the default View element which is overridable.
var layout = new useLayout({ template: "#viewElement", ... });
I've completely rewritten my question to hopefully better reflect what I am trying to do here. Thank you guys so much for your help so far.
I have a file called en.js, which holds this code:
Ext.apply(Ext.locale || {}, {
variable: 'great success!'
});
Here's my index.js setup code:
Ext.setup({
tabletStartupScreen: 'tablet_startup.png',
phoneStartupScreen: 'phone_startup.png',
icon: 'icon.png',
glossOnIcon: false,
onReady: function() {
Ext.locale = {};
var headID = document.getElementsByTagName("head")[0],
newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = 'en.js';
headID.appendChild(newScript);
loginPanel = new login.Panel();
}
});
login.Panel is an extension of the Sencha panel class using Ext.extend.
The 'en.js' script is added to the header correctly. I don't have it in the index.html file because once this problem is solved there will be several files that could be loaded, depending on the output of a function. That's why I need to add the script to the header in the onReady function, and not in the index.html file itself.
Once the script has been added it loads "variable: 'great success'" into Ext.locale,
Yet my problem currently lies within login.Panel(), which is an extension of the Sencha panel class using Ext.extend.
Currently, there is a button in the panel.
When I put this in the button's handler:
console.log(Ext.locale.variable)
it returns the string "great success",
yet when I try to set the button's text like this:
text:Ext.locale.variable,
I get the error
Uncaught TypeError: Cannot read property 'variable' of undefined
I'm guessing I have a scope issue here, since console.log() and alert() can both access Ext.locale, but trying to use it to construct the form gives me the undefined error.
Any help would be greatly appreciated.
Thank you!
it sounds like you are defining Ext.locale from your script early on... then later in onReady you are overwriting it as Ext.locale = {}
onReady will run after all your other scripts have been loaded.
Why not move your initialisation code for locale into onReady insted of your = {} line
This will add three properties and their values to the receiving object.
Ext.apply(receivingObject, {
property1: 'value1',
property2: 'value2',
property3: 'value3'
});
Here also is the Sencha documentation on the Ext.apply method:
http://dev.sencha.com/deploy/touch/docs/source/Ext.html#method-Ext-apply
As for accessing the isReady property, you could do something like if(someExtObj.isReady), but you may be more interested in using the onReady method...
Ext.setup({
onReady: function() {
// your setup code
}
});