How to use Ext.ComponentQuery.query with nested attributes - extjs

How to use Ext.ComponentQuery.query with nested attributes in Sencha Touch?
e.g
var myHardtoGetObj = topLevelView.down('someview[config.categoryCfg.id=1]')[0];
This gets me "uncaught error"
given :
Ext.define('SomeView', {
xtype : 'someview',
config : {
categoryCfg : {
id : 5,
name : 'someName'
}
}
});
Is this possible?
Thanks.

The canonical way of doing things like that is adding a custom pseudo class matcher:
Ext.ComponentQuery.pseudos.hasCategoryId = function(components, selector) {
var result = [],
c, i, len;
for (i = 0, len = components.length; i < len; i++) {
c = components[i];
if (c.config.categoryCfg && c.config.categoryCfg.id == selector) {
result.push(c);
}
}
return result;
}
Then you can use this pseudo class both globally with Ext.ComponentQuery.query, and locally with methods like query, down, etc.:
var allMatched, someComponent;
allMatched = Ext.ComponentQuery.query(':hasCategoryId(1)');
someComponent = myPanel.down(':hasCategoryId(42)');
See more ways to skin the cat in ComponentQuery doc.

This really is an interesting question. There doesn't seem to be an absolutely straightforward solution, however there is a rather quick workaround. You can modify your view code to:
Ext.define('SomeView', {
xtype : 'someview',
config : {
categoryCfg : {
id : 5,
name : 'someName'
}
},
hasCategoryId: function (id) {
return this.getCategoryCfg().id == id;
}
});
Then you can make a query like this:
Ext.ComponentQuery.query('someview{hasCategoryId(1)}');
or
topLevelView.down('someview{hasCategoryId(1)}');
Note: The syntax of the selector is xtype{memberMethod()} without a space in between. This way both selectors must match (the same way as .class1.class2 in CSS). Also the selectors must be in this order, because the result set is filtered by each selector in order and if some of the components don't have the hasCategoryId method it will break with just '{hasCategoryId(1)}'

Although not exactly answering the question but you can do a little work around to get it to work.
you can add update method to your nestedConfig like so
Ext.define('myCoolClass', {
config : {
nestedConfig : {
nestedId : 5
},
nestedId : null
},
updateNestedConfig: function (nestedConfig) {
if (nestedConfig.nestedId) {
this.setNestedId(nestedConfig.nestedId);
}
}
});
By doing that you now have access to normal component query attribute
Ext.ComponentQuery.query('[nestedId=555]')
As an example. If you take a look at Sencha source code they use this quite a lot like in NavigationView and TabPanels

Related

Custom attributes are removed when using custom blots

I created a custom blot for links that requires to be able to set rel and target manually. However when loading content that has those attributes, quill strips them. I'm not sure why.
I created a codepen to illustrate the issue.
This is my custom blot:
const Inline = Quill.import('blots/inline')
class CustomLink extends Inline {
static create(options) {
const node = super.create()
node.setAttribute('href', options.url)
if (options.target) { node.setAttribute('target', '_blank') }
if (options.follow === 'nofollow') { node.setAttribute('rel', 'nofollow') }
return node
}
static formats(node) {
return node.getAttribute('href')
}
}
CustomLink.blotName = 'custom_link'
CustomLink.tagName = 'A'
Quill.register({'formats/custom_link': CustomLink})
Do I have to tell Quill to allow certain atttributes?
Upon initialization from existing HTML, Quill will try to construct the data model from it, which is the symmetry between create(), value() for leaf blots, and formats() for inline blots. Given how create() is implemented, you would need formats() to be something like this:
static formats(node) {
let ret = {
url: node.getAttribute('href'),
};
if (node.getAttribute('target') == '_blank') {
ret.target = true;
}
if (node.getAttribute('rel') == 'nofollow') {
ret.follow = 'nofollow';
}
return ret;
}
Working fork with this change: https://codepen.io/quill/pen/xPxGgw
I would recommend overwriting the default link as well though instead of creating another one, unless there's some reason you need both types.

AngularJS: Apply filter to template

I created a simple filter to format a number based on the current locale.
angular.module('myApp').filter('doubleFilter', DoubleFilter);
function DoubleFilter($translate) {
return function(val, decimalPlaces) {
if (val && (typeof val === 'number')) {
val = val.toFixed(decimalPlaces);
if ($translate.use() === 'de_DE') {
val = val.replace('.', ',');
}
}
return val;
}
};
I call this filter in my template like this and it works fine:
{{dog.weight | doubleFilter : 2}}
However when I change the language using $translate.use('en_US') the format of the numbers in my template are not updated. Obviously I am missing out on something here.
How can I update the view when the language changes?
I think your directive needs to listen for 'local changed notification', for example :
scope.$on('$localeChangeSuccess', function () {
// code to execute the filter
});
Making your filter stateful may help ( see https://docs.angularjs.org/guide/filter )

Getting elements from array based on property values (AngularJS)

I have an array of players, each player is an object that has a number of properties, one is "goals".
var players = [
{
"id":"4634",
"name":"A. Turan",
"number":"0",
"age":"28",
"position":"M",
"goals":"1"
},
{
"id":"155410",
"name":"H. Çalhano?lu",
"number":"0",
"age":"21",
"position":"A",
"goals":"0"
},
{
"id":"4788",
"name":"B. Y?lmaz",
"number":"0",
"age":"30",
"position":"A",
"goals":"2",
}
]
I've written a function to cycle through the array and push every element that has more than '0' goals to an array, topScorers. Like so:
$scope.topScorerSearch = function() {
var topScorers = [];
$scope.teamDetails.squad.forEach(function(o) {
if (o.goals > 0) {
topScorers.push(o)
}
});
return topScorers;
}
With the function called as {{topScorerSearch()}}.
This returns only players who have scored. Perfect.
However, I want to run this on other properties, which will result in a lot of repetitious code. How can I make this a general purpose function that can be executed on different properties?
I tried including the 'prop' parameter, but it didn't work:
$scope.topScorerSearch = function(prop) {
var topScorers = [];
$scope.teamDetails.squad.forEach(function(o) {
if (o.prop > 0) {
topScorers.push(o)
}
});
return topScorers;
}
...and called the function like this:
{{topScorerSearch(goals)}}
Why doesn't this work? Where am I going wrong?
I believe the issue is that prop will not resolve to goals because goals is being treated as a variable with a null or undefined value, making prop null or undefined.
If you use the alternative way of accessing object properties object["property"] and use the function {{topScorers("goals")}} it should work out.

Sencha Touch: searching components with classes with ComponentQuery() and down()

I have a problem and don't know if this is a bug or I misunderstood something. I wanted to search for a component with a specific class. Example:
Ext.define('Test', {
xtype: 'Test',
cls: ['cat', 'dog']
});
I wanted to find this component I created in a Ext.Container with this.down('Test[cls~=cat]') (I used ~= because the component has multiple classes). But I got undefined or null (don't know anymore) as a result.
With Ext.ComponentQuery.query('Test[cls~=cat]') I actually could find it.
Why is that? I thought down() is the same as Ext.ComponentQuery.query with the difference that it's search scope is not global.
I am using the current version of Sencha Touch.
No, this doesn't work. But you can use
this.query('Test[cls~=cat]')
or you use something like this:
Ext.ComponentQuery.pseudos.hasCls = function(items, cls) {
var i = 0, l = items.length, c, result = [];
for (; i < l; i++) {
var c = items[i];
if (c._cls && c._cls.indexOf(cls) > -1) {
return c;
}
}
return undefined;
};
and call it like:
this.down(".component:hasCls(cat)")

Extjs Class System statics

I want to define a class with utility functions. I'm using Extjs class system.
I'm doing this in the following way:
Ext.ns('Controls.Plugins.Nzok')
Ext.define('Controls.Plugins.Nzok.XUtility', {
statics : {
getTest : function(test) { return test }
}
})
Now when I want to use getTest method I have to require the class and to write full class name
Ext.define('Controls.Plugins.Nzok', {
requires : ['Controls.Plugins.Nzok.XUtility'],
useTest : function() {
var testResult = Controls.Plugins.Nzok.XUtility.getTest(2);
}
})
My problem is that notation is too long. It's very inconvenient to write down every time Controls.Plugins.Nzok.XUtility. Are there any solution?
The alternateClassName config does the trick.
Ext.define('Controls.Plugins.Nzok.XUtility', {
alternateClassName: 'Controls.XUtil', // <--- this is your shorthand
statics : {
getTest : function(test) { return test }
}
});
As a side note, Ext.define will automatically create namespaces based on your class name, so Ext.define('Controls.Plugins.Nzok.XUtility' will generate the Controls.Plugins.Nzok namespace for you.

Resources