How to have nested select dropdown that will fetch differently for the main list and the dependent list for htmx? - htmx

Was requested by maintainer to repost here
Issue
In https://htmx.org/examples/value-select/
I can tell that the make is directly in the html and when it changes, the hx-get is called to fetch a new list of models.
What if I want to have both the make and the model all rely on ajax?
meaning to say
<select name="makes" id="makes" hx-get="/makes" hx-trigger="load">
<option value="none">original</option>
</select>
<select name="models" id="models">
<option value="none">original</option>
</select>
i like to be able to fetch from /makes on load to get the list of makes option
and then somehow I can also fill up the dependent models as well.
I don't mind doing it as two requests. The reason is that in my situation the makes are also dependent on a 3rd party API

I hope I understand what you want, but something like this should work:
<select name="makes" id="makes" hx-get="/makes" hx-trigger="load">
<option value="none">original</option>
</select>
<select name="models" id="models"
hx-get="/models"
hx-trigger="changed from:#makes"
hx-include="#makes">
<option value="none">original</option>
</select>
So you add a trigger to the second drop down that listens for changed events on the #makes drop down and then fires up a request to the /models URL, including the value of the #makes drop down so you know what models to return.
Does that seem like what you want?

Related

htmx three dependent selects immediate population of child elements on change

Consider this HTML page using the HTML Javascript package
<html>
<head>
<script src="https://unpkg.com/htmx.org#1.8.0" integrity="sha384-cZuAZ+ZbwkNRnrKi05G/fjBX+azI9DNOkNYysZ0I/X5ZFgsmMiBXgDZof30F5ofc" crossorigin="anonymous"></script>
<title>Test</title>
</head>
<body>
<h1>Prova</h1>
<select name="brand" hx-get="/models" hx-target="#models" hx-trigger="change">
<option value='325i'>325i</option>
<option value='325ix'>325ix</option>
<option value='X5'>X5</option>
</select>
<select name="model" id="models" hx-get="/models2" hx-target="#numbers" hx-trigger="change">
</select>
<select name="number" id="numbers">
</select>
</body>
</html>
Consider that endpoints models and models2 return a bunch of options depending on the value of the select being changed.
The response is something like:
<option value='325i'>325i</option>
<option value='325ix'>325ix</option>
<option value='X5'>X5</option>
I wonder how I can make that whenever I change the select brand, that triggers the HTMX GET Request associated with the model select, which itself will populate the numbers select.
What I see happening, right now, is that the change to the brand dropdown triggers the population of the model dropdown.
I would expect the same to happen for the number dropdown as a consequence of the model dropdown value being changed but that is not happening. Can you elucidate me why that is not happening? And in that case how I can fix that using standard HTMX?
Swapping the new content into the models element does not actually trigger the change event on the models element. You'd need to define an additional trigger event to get the models element to issue its hx-get request when the brand changes and therefore the models options are updated. You could do this purely on the client side in HTMX with a delay so that your models selected value is sent. Or you could have a combination of client custom trigger event and a HX-Response Header sent to the client to trigger that event.
Here is an example of a client event. Added a long 3 second delay to illustrate how the trigger from brand needs to wait until the swap has finished. If you just had the from:brand trigger without delay, it would fire instantaneously (before the models content was populated) so that post would not include the preselected model option. Essentially a race condition. Also went ahead and included the brands element in the models2 request. Maybe you didn't want models2 server side logic have to worry about the brand... but if two different brands had the same model name, then you may not know which set of numbers are supposed to be returned... For the following to work correctly, please add id='brand' to your brand select element.
<select name="model" id="models" hx-get="/models2"
hx-target="#numbers" hx-include="#brand"
hx-trigger="change, change from:#brand delay:3s">
</select>
The other way to do this would be to add a custom event into the model hx-trigger and then trigger that client event from the server in a Response Header. Added a short 5 millisecond delay so that HTMX has time to finish the swap before making the call to models2 .. Again, so that the preselected model is included in the request to models2.
Clientside:
<select name="model" id="models" hx-get="?handler=models2"
hx-target="#numbers" hx-include="#brand"
hx-trigger="change, get-numbers from:body delay:5ms">
</select>
Server Side ASP.NET here (your server may need different syntax for adding Response Header)
Response.Headers.Add("HX-Trigger", "get-numbers");

Creating an xpath with a dynamic id

I am having difficulty creating an xpath. I can write an xpath and map it directly to the id and it works fine. The problem arises when I need to select a different product. Most of the id is static and does not change, but the rest of it does change. I've tried writing an xpath with the contains clause and it still does not work.
Here is what I have tried:
//*[contains(#id, 'j_id0:j_id1:j_id6:section2:j_id34:j_id35:j_id67:j_id68:0:j_id138:j_id139:1:j_id219')]
//*[#id='j_id0:j_id1:j_id6:section2:j_id34:j_id35:j_id67:j_id68:']/option[5]
Here is the code I'm writing the xpath from:
<select id="j_id0:j_id1:j_id6:section2:j_id34:j_id35:j_id67:j_id68:0:j_id138:j_id139:0:j_id222" name="j_id0:j_id1:j_id6:section2:j_id34:j_id35:j_id67:j_id68:0:j_id138:j_id139:0:j_id222" class="user-success">
<option value="">--None--</option>
<option value="Budget">Budget</option>
<option value="Drop/New">Drop/New</option>
<option value="Management Change">Management Change</option>
<option value="Never Fulfilled">Never Fulfilled</option>
<option value="Product Swap">Product Swap</option><option value="Renewal">Renewal</option><option value="Stacked">Stacked</option></select>
Try using following:
//div[#class='user-success']/option[5]
This way you don't have to worry about changing xpath.

Selenium XPath not found for IEDriver when I add URL/Domain in compatibility view

When I execute my script using IEDriver without compatibility view, my test script is running without any problem.
But, If I execute the same script after adding domain in compatibility view, then some elements are not found and I'm getting exceptions.
e.g.
I want to get text of selected item from this DOM:
<select id="selectNumber" name="selectNumber" style="width:180px;">
<option selected="selected" value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
and I'm using XPath .//*[#id='selectNumber']/option[#selected='selected']
to get text but it does not work.
I just checked that in IE DOM selected="selected" is not displayed for selected option until I change Document version manually.
You can use the Select class that works with every browser. Here's some code
Select sel = new Select(driver.findElement(By.id("selectNumber")));
WebElement selectOption = sel.getFirstSelectedOption();
String text = selectOption.getText();
I think you should consider to change from using XPath to use cssSelector. It's much safer to find the elements, not depending on the whole "path". There's a big chance it would not break when using cssSelector running with IEDriver (as you stated in your question).
You can achieve the same goal with both, but when you use XPath, your DOM is much more sensible to changes, and you have bigger chances to see tests broken after page changes.
For your case, you could use it in two ways:
XPath (as you must have it)
driver.findElement(By.xpath(".//*[#id='selectNumber']/option[#selected='selected']"))
Selector
driver.findElement(By.cssSelector("#selectNumber[selected]"))
When you have more complex "paths" you can combine more things like CSS classes in cssSelectors. For example (when you don't have ID):
<select class"nice-dropdown" name="selectNumber" style="width:180px;">
<option selected="selected" value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
driver.findElement(By.cssSelector("select.nice-dropdown")) (return the select element)
driver.findElement(By.cssSelector("select.nice-dropdown option[value='3']")) (return the option with value=3)
With selectors you have shorter "paths". They work in the same way as selectors are used in CSS.
As reference:
Selectors = w3schools
Selectors = W3C
Hope this information is somehow helpful.

How can I changed the selected <select> tag element in scrapy?

I'm trying to crawl a website using Scrapy.
I need to query the list of all existing items of a board-like function on my target site.
The problem is that it only searches within a target year, designated with a HTML tag option.
So I need to find a way to changed to location "selected" attribute within the s.
I think I'm not really doing my job at describing my troubles, so I'll append a simplified HTML code of the site:
<select name="Search_FIS_YEAR" id="Search_FIS_YEAR" title="fiscal_year">
<option value="2014" selected>2014</option>
<option value="2013">2013</option>
<option value="2012">2012</option>
...
<option value="2007">2011</option>
</select>
So the default value of my target website is 2014, but I want to programmatically change its value to 2013, 2012, etc.
The search query is sent through a large that makes a POST request method to the server.
Fortunately I found a way to send the query using FormRequest.from_response, but I wasn't really successful in modifying the above part of the code.
I already knew the answer.
All I did was to just change the value of option within the form data:
yield FormRequest.from_response(
response,
formname=NAME_OF_FORM_WRAPPING_SELECT_HTML_ELEMENT
formdata={
'Search_FIS_YEAR': '2013', # or any other year value
}
callback=self.other_parse_function
)
This did all the tricks for me.

Filtering Select - How to add a blank option '-' that allows submission in Dojo

Been trying to fix this for a while now. I have loads of Dojo Filtering Select elements in my form. I need them to have a blank option which should be selected by default. Here is an example (of a normal <select> structure) that I want to emulate:
<select>
<option value="">-</option>
<option value="foo">Bar</option>
</select>
At present when I load up my filtering selects that have the options set as above, there is no element selected. This in turn disables form submission. This is totally unusable for my end users as they would have no idea why the form is not working. The selects are not even required fields.
The problem lies in the way the FilteringSelect stores it's data. It keeps them in a data store that requires an identifier and a label. You can't quite emulate the functionality of a plain select because of this.
You can 'get around' this by putting a fake item in your options with a value of 'XXXX' (or another fake value).
<select>
<option value="XXXX">-</option>
<option value="foo">Bar</option>
</select>
One downside of this kludge is that you need to change your validation functions to look for this fake value (instead of an empty value).
I set up a test on jsbin.com where you can see it in action.