Dgrid - how to start row rendering at a specific index - dojo

I'm using Dgrid with an Observable JsonRest store. The JsonRest store is queried for 50 rows at a time. Now I have a function where the user can 'quicksearch' the data and the search is processed on the serverside. This works, and in this case the server returns for example "Content-Range: 210-260/1500". It returns 50 rows of data but Dgrid renders the full grid at the beginning, so the user can't scroll 'up' for previous entries.
How can I make Dgrid behave like that?

Not entirely sure if I'm completely grasping your problem, but if I am, it may require some thinking outside the box or compromising to get what you want. I assume your UI is basically jumping to the first match rather than simply filtering the grid to only show matches.
If your UI is such that the grid is always present and the search is basically intended to scroll the grid, you can use grid.scrollTo({ y: valueInPixels }) to scroll the grid. While this accepts a pixel value (not rows), if your rows are consistent height, you can multiply by grid.rowHeight (which is a property set by OnDemandList) to get the correct offset.
Another option, though probably not what you want, would be to use the Pagination extension and navigate to a specific page.
Of course, if you would rather actually filter the grid to only display matching items, that's possible too (assuming your server behaves as dojo/store/JsonRest expects, anyway). The Using Grids and Stores tutorial has an example towards the end.

Related

Why do two identical looking elements return when using XPath to map elements for Selenium WebDriver?

I have had this question a while, but I had never done anything about it. When mapping some elements for UI tests I sometimes come across elements that return two identical results.
We have got around this in the past by using findelements and then using an index [1].
But I still don't understand why it returns two elements when I can only see one in the code that should be located.
An example would be the following. You can see this username field box below.
And if I use some XPath expression like,
//input[#name='username']
I'm expecting only to get one element in return, but using the tool Chropath I can see that I get two elements in return.
These elements look identical, one is not hidden, etc.
I have never understood why this is happening, because if I use a findelement, I get an element, not interactable error, as I guess the driver can’t decide which one to use? Or they are in the way of one another.
So the workaround I have always used is:
return self.browser.find_elements(by=By.XPATH, value="//input[#name='username']")[1]
when I realisticly should be able to use:
return self.browser.find_element(by=By.XPATH, value="//input[#name='username']")
Why is this?
Some excellent response and it has made me understand what’s going on now. Moving forward, I will use the following:
for e in self.browser.find_elements(by=By.XPATH, value="//input[#name='username']"):
if e.is_displayed():
return e
This seems to work for me.
I see this often when a website has both the "desktop" version and a mobile or smaller screen version. At full (or near full) screen, the desktop elements are visible while the small screen elements are hidden. Once you resize the browser small enough, the desktop elements are hidden and the small screen elements become visible.
To get around this in a generic way, filter the returned two elements based on visibility, e.g.
return [e in self.browser.find_elements(by=By.XPATH, value="//input[#name='username']") if e.is_displayed()]
That should always return the visible element of the two.
The answer is within the snapshot:
The following xpath
//input[#name='username']
Identifies 2 different elements within the HTML DOM. Among the two matching elements, the first matching element is for mobile displays which remains hidden while you access the DOM Tree in Desktop mode. In the given snapshot of the Chropath the classname as modal-content-mobile is the best hint.
Solution
In these cases there are different approaches to identify the desired element. While some users tends to use an index and some users tends to probe the displayedness, from a personal perspective I find it quite easier and handy to traverse up the DOM to find the difference in attribute values in any of their ancestors and then finally follow down till the desired element.
It is possible you can have more than one same element on the page with same name attribute. One must be hidden.
If you want to access the first one use following xpath.
return self.browser.find_element(by=By.XPATH, value="(//input[#name='username'])[1]")
If you want to access last one use following
return self.browser.find_element(by=By.XPATH, value="(//input[#name='username'])[last()]")
It's quite often occurs that multiple elements will match the same locator.
For example, several code blocks may be implemented for login: one for a computer browser, another for a mobile browser, etc. The proper elements will be presented according to what you use to browse that page.
Selenium find_element always returns the first element found matching the passed locator on the page.
So, in case the first matching element is hidden return self.browser.find_element(by=By.XPATH, value="//input[#name='username']") will always retunt that hidden element.
You will need to make your locator more precise to match the desired web element.
A locator like "(//input[#name='username'])[2]" may be good, but it's better to use a unique parent element here, something like "//div[#class='pc_modal']//input[#name='username']", so your code would be something like this:
return self.browser.find_element(By.XPATH, "//div[#class='pc_modal']//input[#name='username']")
Well, in the strictest sense of way, no two elements have the same XPath expression. If you look at the absolute path, you will find the difference. The key is to find a path which is unique. In many case, you will find a web page where you find many textboxes/labels/dropdowns that have the same ids but are only differentiated by their absolute path.
Most of the times, such things depend on the framework used to develop the webpage and also developer's preference. An application developed in React will have a different DOM structure than one developed using Angular, for instance.
Yes, you are correct that it becomes difficult to find out which is the element of interest in such situations. In such cases, do not only depend on the particular element but add either a parent/sibling or ancestor to access the element. Although it might take some time and will jot be straightforward but it will be possible to find a unique XPath most of the times.
There are some test automation tools, like Ranorex, that have an object browser (objext spy as it is called) that can be used to pin on any web element and access its properties like hidden, visible. enabled, etc. But such tools are not free :(

Offset SwiperJS pagination by one slide

This should be fairly simple, probably too simple, given I cannot find the API reference as of how to control it.
I am using https://swiperjs.com/ to build a simple slider of images.
Specifically, this one https://codesandbox.io/p/sandbox/5nclhv?file=%2Findex.html
Now, I want the slider to start at Slide Item 2, not item 1.
Yet, I cannot find any single option or API control to offset the initial pagination by one.
What am I missing?
You can use https://swiperjs.com/swiper-api#param-initialSlide:
initialSlide
This parameter has default value 0 and defines the index number of initial slide.
Thus, setting it to 1 will make the slider start at the second slide item.

Aurelia: router without losing state

Here is my problem:
I typically have a paginated datagrid with a lot of rows. I want to be able to edit the data for each row of course but I have 2 constraints on this:
I need the edition form to replace the content of the page (I don't want a popup, modal dialog or side panel)
I don't want to lose the state of the datagrid: maybe I navigated down 5 pages in the datagrid and I don't want to be reloaded on the first page. And actually, I'd rather not reload the data I already had (the edited data will be updated automatically by my persistence layer anyway).
Ideally, I would have liked to have some kind of subrouter but I'm not sure how it would fit the first requirement. Otherwise, I could have a component that would be hidden by default and positions itself on top of the datagrid view when necessary but that feels quite hacky and forces me to have everything in the same template. And I will have to handle a stack of these components if I have several different 'full-screen panel'...
Any idea on a correct way to implement this?
Thanks!
I tried different solutions to no avail unfortunately. I had a long discussion with #Kukks on gitter and we agreed that using subrouters and viewports might be a bit overkill for my use case.
I reverted to my original idea of using absolutely positioned components to hide the previous one in a kind of "deck layout". This is not ideal as I would have liked completely separated views and using components forces me to declare them in the main view but it works well and is very easy to implement...
So: not as clean as I would have liked but much easier to implement and less convoluted.
Consider using Router View Ports
http://aurelia.io/hub.html#/doc/article/aurelia/router/latest/router-configuration/9

Skrollr: Multiple instances

I have two columns that I would like to animate separately with a trigger. As I understand it: Skrollr only allows one instantiation on a page. Does anyone know if it's possible to have multiple instances that can be turned off and on?
I've started a working example here:
The grey column will activate the Skrollr instance when clicking on its "Activate!" button. (The "Destroy!" button will remove its instance.)
I would like to isolate the Skrollr animation to just the grey column, but as you can see in this example, the yellow/orange column is also being activated.
Three ways
Remove/add the data attributes between the destroy/init calls and only add them to the elements you want
Use two constants, defined as a function and toggle them between 0 and 1e6 (or something really large). Now the elements with the large one will effectively not be rendered (given you're using edgeStrategy reset)
Monkey patch the refresh method (without touching the skrollr code itself). Skrollr uses it internally when using init. Now you can patch it to use leftColumn.getElementsByTagName('*') or all elements in the right column when no parameter is passed. This way initializing will only affect elements inside one of the columns.

Dojox.grid.DataGrid - in a widget - only renders on visible tab

I am using a Widget that contains a DataGrid object. The Widget works fine when included in the first tab (this is the visible tab), but not when I use the same code on a second tab.
The code is the same I have done several checks to make sure there are no other problems - and non Grid code is rendering fine - only the grid that has a problem. I have tried setting the height and width manually and this just results in a large grey rectangle on the second tab.
Do I need to tell the Grid to refresh in some way - or is it a property for the TabContainer?
Help - this is driving me mad!
Yeah, that's a big problem with the grid. If you use it declaritively in a tab container, it won't render properly on the non-visible tabs. It needs to calculate height/width (even though you specify them)...as you have seen.
The way I got around it was to create the grids programatically on tab select. I posted about my solution on the dojo forums. My code sample is over on github. It's a bit too large to post here methinks. Let me know if you want it, and i'll edit my answer.
There's also a discussion on nabble with a different solution.
"resize" works like a charm! Been looking for this for a long time (didn't know what I had to search for), thanks.
I use this routine to dynamically determine if the tab has more than one datagrid, as I may not know the ID of one single grid, maybe someone else might use that, too:
dojo.query('div#container div[id^="gridNode_"]').forEach(function(node, index, arr) {
dijit.byId(node.id).resize();
});
This will check the div with id="container" (skip that part if you want to search the whole DOM) for divs with an id starting with "gridNode_" and apply "resize" to those widgets.
An alternate approach is to resize the grid upon tab element selection. Sample code
dojo.connect(dijit.byId('my_tab_container'), "selectChild", function(child){
// if second tab (could be any...) selected
if(child.id == 'mySecondTabId'){
var myGrid = dijit.byId('myGridInsideTabId');
if(myGrid != null) myGrid.resize();
}
});