I want the user to be able to construct a list of items client-side in a form and then send the entire list to the server when the form is submitted.
Each item has several properties that can be set. For example, here's an Order model:
class Order
{
int Id;
string Description;
Guid UserId;
string Product;
}
The user should be able to add and configure as many orders as they want before submitting the form.
I thought I could do this with the Telerik grid using InCell editing. Unfortunately, the list of available options for each field will change based on the value chosen for other fields. I couldn't make this work with InCell editing because I couldn't access the other ui elements from my javascript event handlers.
I also tried to do this with normal InLine editing, but the only item returned is the new Order to be added. I couldn't access the GridModel to add the new Order. I don't want to persist the Order list until the user is done editing.
Any help would be greatly appreciated.
EDIT: If I go with the solution of making my own list, I have this problem with cascading comboboxes. Each combobox has a name like "UserId_0" based on what index it is in the list.
But now how do I get the downstream list?
Here's my two comboboxes:
<td>
#(Html.Telerik().ComboBox()
.Name("UserId_" + i.ToString())
.BindTo(new SelectList(Model.UserIds, "Id", "Name"))
.Placeholder("Select UserId...")
.AutoFill(true)
.CascadeTo("Product_" + i.ToString())
.SelectedIndex(0))
</td>
<td>
#(Html.Telerik().ComboBox()
.Name("Product_" + i.ToString())
.AutoFill(true)
.DataBinding(binding => binding.Ajax().Select("GetProducts", "Order"))
.Placeholder("Select Product...")
.SelectedIndex(0))
</td>
And my Controller method looks like this. It's hacky as hell, but how do I access the value otherwise?
public JsonResult GetProducts(Guid? UserId)
{
// Hack!
ValueProviderResult userResult = ValueProvider.GetValue("UserId");
for (int i=0; i < 10; i++)
{
userResult = ValueProvider.GetValue("UserId_" + i);
if (userResult != null)
{
break;
}
}
if (userResult != null)
{
// Get list of products based on value
return Json(new SelectList(productList, "Id", "Name"));
}
else
{
return Json(null);
}
}
Do not waste your time to achieve this with the some widget - better check how to send collection of items to the server and implement your own form with checkboxes or so.
If you want to go with some of the widgets - you can try the Grid in a way similar to this demo.
Related
I'm new to asp.net MVC core
trying to build a page where you choose multiple search options
I need a get and post actions for that, the results should be in a partial view that is paginated, the code works fine until I click next or previous, I lose the whole search result object cuz the model doesn't bind them back
[HttpGet]
public ViewResult SearchOutbox(DocumentSearchViewModel doc)
{
var documentSearchViewModel = PopulateDocumentSearchViewModel(doc);//this method fills the dropdowns
return View(documentSearchViewModel);
}
[HttpPost]
public async Task<IActionResult> SearchOutbox(DocumentSearchViewModel doc, int? page)
{
var documentSearchViewModel = PopulateDocumentSearchViewModel(doc);
if (ModelState.IsValid)
{
IQueryable<Document> documents = _documentRepository.SearchDocument(documentSearchViewModel);
documentSearchViewModel.Documents = await PaginatedList<Document>.CreateAsync(documents.AsNoTracking(), page ?? 1, 1);
return View("SearchOutbox",documentSearchViewModel);
}
return View();
}
partial view
....
<a asp-action="SearchOutbox"
asp-route-page="#(Model.PageIndex - 1)"
class="btn btn-default #prevDisabled">
Previous
</a>
<a asp-action="SearchOutbox"
asp-route-page="#(Model.PageIndex + 1)"
class="btn btn-default #nextDisabled">
Next
</a>
main view calling partial
<partial name="_ListDocument" model="#Model.Documents">
Before diving into the details, it is worth to mention that for pagination you can use GET only, no need to use POST.
Back yo your question; if the result set is based on some filters, you have to pass all those filters parameters along with the pagination link.
e.g. if you have a URL like below one you can survive by sending only page parameter to the relevant action:
http://example.com/products/?page=1
But whenever you add some filters to the URL, you have to include all of them in the paging buttons, e.g. in below URL you have to send all paramters after ? to the paging action so it can select the next page from the filtered results using the same filtering options:
http://example.com/products/?page=1&category=mobile&brand=xyz
You can add all parameters manually, or you can use a function that will read the query string values then only increase the page number and generate new URL. Below is a function that do the page number increase and replace it via regex:
Previous
Next
#{
string CreateUrl(int newPage)
{
var index = int.Parse(Request.QueryString["page"].ToString());
var input = Request.QueryString.Value;
var replacement = $"page={index + newPage}";
var pattern = #"page=\d+";
return System.Text.RegularExpressions.Regex.Replace(input, pattern, replacement);
}
}
There is some nuget packages that can handle advanced paging functionalities like LazZiya.TagHelpers, install from nuget :
Install-Package LazZiya.TagHelpers -Version 3.0.2
Add paging tag helper to _ViewImports :
#addTagHelper *, LazZiya.TagHelpers
Then use it where you need a paging control:
<paging
total-records="Model.TotalRecords"
page-no="Model.PageNo"
query-string-key-page-no="page"
query-string-value="#(Request.QueryString.Value)">
</paging>
Notice : in the latest preview version (v3.1.0-preview1) no need to add query-string-value so the tag helper will work like below:
<paging
total-records="Model.TotalRecords"
page-no="Model.PageNo"
query-string-key-page-no="page"
</paging>
See tutorial, live demo and docs
What do I have - I'm automating one website where there is a big list of elements but not loaded all at once. Say for example I have 200 elements in the list, but only 10 elements are loaded at the moment, and out of these 10 only 5 are visible on the screen.
What I want to do - Now I want to select all of these elements 1 by 1, by clicking on them, because clicking on element selects check box in front of each (Basically i want to tick checkbox). So i will 1st select 5 elements which are visible on page, then i will scroll down to select another visible group, like wise i want to select all 200 elements.
What Problem I'm facing - webdriver.findElements(..) method is returning list of 10 elements which are loaded. But it is not returning list in the order in which the elements are displayed on the page. I'm using for loop to iterate over the list and clicking on elements one by one.
As a result if 6th element is clicked which is not displayed in page, page scrolls till that element and click it, now after that if 2nd element got chance to be clicked, then page should scrolls up to click it, but this time since DOM has loaded again due to scrolling, I get StaleElementReferenceException. If I handle this exception within try catch and get element list again by finding elements, it is not guaranteed to be in the correct order and does not solve my problem.
Solution?? - Is there any way in selenium to get list of elements in order it is displayed on page? Or please let me know what should be the approach to achieve above scenario?
Any suggestions are much appreciated.
Note - JAVA is being used as a programming laungage.
If you also post the HTML that would be more helpful to explain the solution. I am trying to give you a solution by making some assumptions.
Taking a sample HTML code
<div id "checkbox_container>
<input type="checkbox " id="checkbox1 ">
<input type="checkbox " id="checkbox1 ">
<input type="checkbox " id="checkbox1 ">
<input type="checkbox " id="checkbox1 ">
.
.
.
<input type="checkbox " id="checkbox1 ">
</div>
Now write the locators that will give the count of the available checkbox elements and a locator that will select the specific checkbox.
public class SamplePageObjects {
//This locator will select all available checkboxes
public static By ALL_CHECK_BOX = By.xpath("//div[#id='checkbox_container']//input");
//This locator will select the particular element for the given index
public static getMeExpectedCheckBox(int index, int maxCount) {
if (index < 0 || index > maxCount) {
throw new IlleagalArgumentException(index+" is not a valid index");
}
return By.xpath("//div[#id='checkbox_container']//input[" + index + "];
}
}
Now, write your page class
public class SamplePage{
//Gives the count of available elements
public int getCountOfAvailableCheckBox(WebDriver driver){
return driver.findElements(SamplePageObjects.ALL_CHECK_BOX).size();
}
//Selects the specific element
public void selectWebElement(WebDriver driver,int index){
driver.findElement(SamplePageObjects.getMeExpectedCheckBox(index).click();
}
//Selects all available element one by one
public void click(){
int totalCount = getCountOfAvailableCheckBox();
for(int i=0; i< totalCount-1; i++){
try{
selectWebElement(i,totalCount);
} catch(StaleElementReferenceException e){
selectWebElement(i,totalCount);
}
}
}
So first get the list of all available checkboxes. After getting the count, just pass the index value to the locator and perform the action.
Hope it will help you.
I need to select all the checkboxes from here one-by-one after every 3 second. I tried couple of xpaths with list,none of them have worked
Tried xpaths:
//div/div[#class='filters-list sdCheckbox ']
Using input and type. But none of them worked. Can you please help me out?
Reference website: https://www.snapdeal.com/products/storage-devices?sort=plrty
->Capacity at the left hand corner
By.xpath("//a[#class='filter-name']") this one listed out all the filters of page.
The xPath "//div[#data-name='Capacity_s']/div[#class='filters-list sdCheckbox ']/input" will fetch you the list of all input elements that you need to check.
There is a container DIV that holds all the filters of a certain type, e.g. Brand, Capacity, etc. The one for Brand is shown below.
<div class="filter-inner " data-name="Brand">
Under that container, all the LABEL tags are what you need to click to check the boxes. So, we can craft a CSS selector using the grouping as a filter to reach only the checkboxes we want.
"div[data-name='Brand'] label"
Since this is something I assume you will reuse, I would write this as a function.
public static void CheckFilters(String filterGroup)
{
WebDriverWait wait = new WebDriverWait(driver, 10);
List<WebElement> filters = driver.findElements(By.cssSelector("div[data-name='" + filterGroup + "'] label"));
// System.out.println(filters.size()); // for debugging
for (int i = 0; i < filters.size(); i++)
{
filters.get(i).click();
// wait for the two overlays to disappear
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.cssSelector("div.searcharea-overlay")));
wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector("div.filterLoader.hidden")));
// reload the element list after the refresh so you don't get StaleElementExceptions
filters = driver.findElements(By.cssSelector("div[data-name='" + filterGroup + "'] label"));
}
}
and you would call it like
driver.get("https://www.snapdeal.com/products/storage-devices?sort=plrty");
CheckFilters("Brand");
I am generating a html table dynamically and after that using datatable 1.9.4 plugin to enhance my html table's features. My table looks like below
the "x" are checkboxes, I am trying to select checkboxes with respect to a particular row and column combination. I have values in pairs like (row1,col3) then I need to select that particular cell. I tried reading checkbox text from header row and selecting corresponding checkbox but no luck.
How should I proceed with this. I know there are some methods from dataTable but not very well aware of those.
You lack to tell how exactly you generate those checkboxes. So the following is just how what I would do it. There isnt really difference if the checkboxes is inside a dataTable or anything else.
Example of creating a lot of checkboxes inside a dataTable, all with unique ID's :
for (var row=1;row<=10;row++) {
dataTable.fnAddData([
'<input type="checkbox" id="check_'+row+'_1">',
'<input type="checkbox" id="check_'+row+'_2">',
'<input type="checkbox" id="check_'+row+'_3">',
'<input type="checkbox" id="check_'+row+'_4">',
]);
}
A shorthand function for retrieving the right checkbox, based on row, col pairs :
function dtCheckBox(row, col) {
return document.getElementById('check_'+row+'_'+col);
}
Some examples to show the concept is working :
//setter examples, check 20 random checkboxes
for (var i=0;i<20;i++) {
var row=Math.floor(Math.random()*10)+1,
col=Math.floor(Math.random()*4)+1;
dtCheckBox(row, col).checked=true;
}
//getter example, console out the checked status of all checkboxes
for (col=1;col<=4;col++) {
for (row=1;row<=10;row++) {
console.log(dtCheckBox(row, col).checked);
}
}
See the above in action in this fiddle -> http://jsfiddle.net/EpLA7/
I have a somewhat odd situation where I have dynamically generated fields on a form--all dropdown lists. The selections correspond to binary values that I want to sum together to form a bitmask. I'm generating the dropdowns this way:
<table class="center">
#foreach (var field in Model.Fields)
{
<tr>
<td>#field.DisplayText:</td>
<td>
#Html.DropDownList(field.FieldName, new SelectList(field.Options, "FlagValue", "Text", field.SelectedValue), "(doesn't matter)")
</td>
</tr>
}
</table>
This seems to work--as far as rendering the proper HTML in the view. But my controller is not receiving the selections in the fields. I tried this to loop through the dynamic fields.
In the code below, PatientSelectorEditor is my ViewModel.
private void GetFlagInfo(PatientSelectorEditor pse, out string description, out long flags)
{
description = null;
flags = 0;
// get list of all possible fields that could be in the view.
pse.Fields = InitPatientSelectorFields(0);
foreach (PriceFlagField field in pse.Fields)
{
foreach (var option in field.Options)
{
// was something selected here?
if (Request[field.FieldName].Equals(option.FlagValue))
{
description += ", " + option.Text;
flags += option.FlagValue;
}
}
}
}
The line that goes
Request[field.Name]
is not finding the dynamically generated fields in my view.
What am I doing wrong?
I figured out what I was doing wrong here. This line....
if (Request[field.FieldName].Equals(option.FlagValue))
needed an explicit string comparison like this
if (Request[field.FieldName].Equals(option.FlagValue.ToString()))