ExtJS 4 - Get all components that are descendants of a given Element - dom

What would be the most efficient way to find all ExtJS components that are rendered as descendants of a given HTML Element? Note that this Element is not part of a component itself and is not managed by Ext in any way, it is just hardcoded raw HTML. And this Element may not only contain just ext components, it may have other non-ext managed html in it as well, and that html can also contain ext components. So that means that the solution must traverse all the way down the DOM, not just look at direct children.

My suggestion would be to walk the dom, checking the id of each element against Ext.getCmp, which is a hash-map lookup. You could then switch to walking through the Component methods, but I think it would be basically the same speed, and if you're already walking the dom to begin with you might as well keep at it:
var dom = ...;
var components = [];
Ext.Array.each(Ext.get(dom).query('*'), function(dom) {
var cmp = Ext.getCmp(dom.id);
if (cmp)
components.push(cmp);
});
Ext.get(dom).query('*') may do more work than you'd like, it might be more efficient to have your own walker, like this:
function walk(dom, callback) {
if (dom.nodeType === 1) {
callback(dom);
Ext.Array.each(dom.childNodes, function(child) {
walk(child, callback);
});
}
}
var dom = ...;
var components = [];
walk(dom, function(dom) {
var cmp = Ext.getCmp(dom.id);
if (cmp)
components.push(cmp);
});
All this assumes that the dom ids match the component ids, which I don't know if that is something you can rely on in future versions of Ext.

Related

Combining two element locators [duplicate]

I am trying to keep my pageObjects in Protractor as clean as possible, but have run up against some behavior in selecting sub-elements.
This works:
var parent = element(by.css('.parent-class'));
parent.element(by.css('.child-class')).getText();
However this does not:
var parent = element(by.css('.parent-class'));
var child = element(by.css('.child-class'));
parent.child.getText();
Is there someway to do something like the second example? I'd rather not have the element locators spread throughout the methods on my pageObjects, but it seems thats the only way to locate subelements?
In actual application I have a long list of cards, from which I filter down to just the one I am looking for. I then want to do things with subelements of the card.
You could use the locator() function to get the locator of the child element and use it to find a child of the parent. This is similar to the solution you provided in your comment, but allows you to define all properties on your page object as web elements instead of a mix of elements and locators:
var parent = element(by.css('.parent-class'));
var child = element(by.css('.child-class'));
parent.element(child.locator()).getText();
I have a lot of the following code:
var parent = element(by.css('.parent-class'));
var child = parent.element(by.css('.child-class'));
child.getText();
But as far as I understood you have a lot of children and you don't want to list all the variants.
Additionally to Nathan Thompson answer you can have a helper function in page object to find subelement:
function getCard(parent, child) { // Or getChild()
return element(by.css(parent)).element(by.css(child));
}
function getCardText(parent, child) { // Or getChildText
return getCard(parent, child).getText();
}
So then you can just write in spec:
expect(cardPage.getCardText('.parent-class', '.child-class')).toBe('...');
I wanted to mention that using 5.2.2 version this implementation is bit different.
To get actual selector value you must use following code
let selector = child.locator().value
This is because locator returns an object which contains selector and other properties, but in this case, you only need selector.
here is what is returned by method locator()
name(name) {
return By.css('*[name="' + escapeCss(name) + '"]');
}
{ using: 'css selector', value: '.child-class' }
Here is how it should be implemented now.
var parent = element(by.css('.parent-class'));
var child = element(by.css('.child-class'));
parent.element(child.locator().value).getText();
//short hand
var parent = $('.parent-class');
var child = $('.child-class')
parent.$(child.locator().value).getText();

Protractor Chained Elements by Using Variables?

I am trying to keep my pageObjects in Protractor as clean as possible, but have run up against some behavior in selecting sub-elements.
This works:
var parent = element(by.css('.parent-class'));
parent.element(by.css('.child-class')).getText();
However this does not:
var parent = element(by.css('.parent-class'));
var child = element(by.css('.child-class'));
parent.child.getText();
Is there someway to do something like the second example? I'd rather not have the element locators spread throughout the methods on my pageObjects, but it seems thats the only way to locate subelements?
In actual application I have a long list of cards, from which I filter down to just the one I am looking for. I then want to do things with subelements of the card.
You could use the locator() function to get the locator of the child element and use it to find a child of the parent. This is similar to the solution you provided in your comment, but allows you to define all properties on your page object as web elements instead of a mix of elements and locators:
var parent = element(by.css('.parent-class'));
var child = element(by.css('.child-class'));
parent.element(child.locator()).getText();
I have a lot of the following code:
var parent = element(by.css('.parent-class'));
var child = parent.element(by.css('.child-class'));
child.getText();
But as far as I understood you have a lot of children and you don't want to list all the variants.
Additionally to Nathan Thompson answer you can have a helper function in page object to find subelement:
function getCard(parent, child) { // Or getChild()
return element(by.css(parent)).element(by.css(child));
}
function getCardText(parent, child) { // Or getChildText
return getCard(parent, child).getText();
}
So then you can just write in spec:
expect(cardPage.getCardText('.parent-class', '.child-class')).toBe('...');
I wanted to mention that using 5.2.2 version this implementation is bit different.
To get actual selector value you must use following code
let selector = child.locator().value
This is because locator returns an object which contains selector and other properties, but in this case, you only need selector.
here is what is returned by method locator()
name(name) {
return By.css('*[name="' + escapeCss(name) + '"]');
}
{ using: 'css selector', value: '.child-class' }
Here is how it should be implemented now.
var parent = element(by.css('.parent-class'));
var child = element(by.css('.child-class'));
parent.element(child.locator().value).getText();
//short hand
var parent = $('.parent-class');
var child = $('.child-class')
parent.$(child.locator().value).getText();

Prevent "with" binding from removing DOM elements (Knockout.js)

Knockouters,
I have come to rely on the with binding for establishing context nesting. while I like the way KO manipulates the DOM based on the state of the bound elements in some instances, sometime I just want the binding implications without the DOM removal.
Does anyone know if it is possible way to prevent the DOM manipulation on an individual element binding level?
Thanks,
Vinney
Version 2.2+ of Knockout won't clear the DOM element when with is bound initially to an object (or other truthy value). Alternatively, you can use the withlight binding that I put together some time ago. It only will bind to an object (not an observable).
ko.bindingHandlers['withlight'] = {
'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var bindingValue = valueAccessor();
if (typeof bindingValue != 'object' || bindingValue === null)
throw new Error('withlight must be used with an object');
var innerContext = bindingContext['createChildContext'](bindingValue);
ko.applyBindingsToDescendants(innerContext, element);
return { 'controlsDescendantBindings': true };
}
};

Querying class on Raphael object

I have this array of rects using jQuery and Raphael:
squares = [];
for (i = 0; i < 2; ++i) {
var square = paper.rect(0 + 100*i, 0, 70, 70);
square.node.idx = i;
square.node.setAttribute('class', 'foo');
squares.push(square);
}
I can successfully query various attributes, like:
alert(squares[0].attr('x'));
or
alert(squares[0].attr('width'));
but not:
alert(squares[0].attr('class'));
Is there a special reason for which this is not valid?
Is there an (other) way to query the class attribute?
Thanks,
Adrian
Classes in SVG aren't quite the same as classes in everything else - and in Raphael, which deals with SVG and IE's VML, things get even more hairy.
First of all, they're on the page's DOM element (Raphael's output) not in the Raphael JS object itself. You'd use Raphael's .node to get the actual DOM path (e.g. with jQuery, $(squares[0].node).someJqueryFunction();) but this sort of thing is best avoided where possible for the above reasons. This related question has answers with more info.
If you want to use classes to store data (e.g. like using 'active', 'inactive' classes as switches), you're best off using Raphael's .data function which apparently is for storing arbitrary values. This related question has answers with more info.

What exactly is DOM Extension / Wrapping?

I have 2 main questions.
Does extending things like Object count?
What is DOM wrapping?
http://perfectionkills.com/whats-wrong-with-extending-the-dom/
After reading that article I couldn't find anything about DOM wrapping, and no specification and what exactly is and isn't DOM extension.
No, Object is specified as part of the Javascript language, while the DOM is an API only relevant in a browser environment and is used to "access and update the content, structure and style of documents" (W3C).
However, one of the reasons provided in that article arguing against the extension of DOM objects still applies to extending native types such as Object - namely the chance of collisions.
Wrapping an object refers to creating a new object that references the original, but providing additional functionality through the new, wrapper object.
For example, rather than extending a DOM Element object with a cross-browser addClass function like this:
var element = document.getElementById('someId');
element.addClass = function (className) {
...
};
You can instead define a wrapper function:
var ElementWrapper = function (element) {
this.element = element;
};
And add the function to its prototype:
ElementWrapper.prototype.addClass = function (className) {
...
};
And "wrap" elements like this:
var element = document.getElementById('someId');
var wrapped = new ElementWrapper(element);
wrapped.addClass('someClass');