Select a dropdown element with dynamic ID using Webdriver, C# - c#

I have a situation I'm trying to handle. I am using webdriver, C#. when I tried using the CSSSelector it is just reading the parameter as a string. Kindly look into it.This is the HTML.
<div class="select2-container select2" id="s2id_UserRole" style="width: 100%;">
<a href="javascript:void(0)" class="select2-choice" tabindex="-1">
***<span class="select2-chosen" id="select2-chosen-7"> </span>***
<abbr class="select2-search-choice-close"></abbr>
<span class="select2-arrow" role="presentation">
<b role="presentation"></b></span></a><label for="s2id_autogen7" class="select2-offscreen"></label>
<input class="select2-focusser select2-offscreen" type="text" aria-haspopup="true" role="button" aria-labelledby="select2-chosen-7" id="s2id_autogen7">
<div class="select2-drop select2-display-none select2-with-searchbox">
<div class="select2-search">
<label for="s2id_autogen7_search" class="select2-offscreen"></label>
<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" class="select2-input" role="combobox" aria-expanded="true" aria-autocomplete="list" aria-owns="select2-results-7" id="s2id_autogen7_search" placeholder="">
</div>
<ul class="select2-results" role="listbox" id="select2-results-7"> </ul></div>
</div>
The element I'm trying to get the ID is a dropdown with this kind of ID: id="select2-chosen-7". The "select-chosen-" is static and numeric part is dynamic. After reading forums, all suggestion does not seem to have the answer. At the end this is the code I'm using which is still not working -
IWebElement DropDownPath = driver.FindElement(By.CssSelector("*[id^='select2-chosen-'][id*='select2-chosen-']"));
SelectElement DropDown = new SelectElement(DropDownPath);
DropDown.SelectByText(UserConstants.UserRoleText);

If it isn't a requirement to keep the ValuesAddX separated, you could have an array of arrays, and foreach over that:
public static string[][] ValuesAdd =
{
new [] { "a", "b", "c" },
new [] { "1", "2", "3" },
new [] { "x", "y", "z" },
};
public void NestedForeach()
{
// Note that count isn't required anymore as we're using
// ValuesAdd.Length as the count
NestedForeachRecursive(string.Empty, 0);
}
public void NestedForeachRecursive(string prefix, int depth)
{
foreach (var item in ValuesAdd[depth])
{
var nextDepth = depth + 1;
var nextPrefix = prefix + item;
if (nextDepth < ValuesAdd.Length)
NestedForeachRecursive(nextPrefix, nextDepth);
else
Console.WriteLine(nextPrefix);
}
}
Note that because you're iterating over every item for every other item, the performance of this will scale very poorly.
The output of this example is:
a1x
a1y
a1z
a2x
a2y
a2z
a3x
a3y
a3z
b1x
b1y
b1z
b2x
... and so on

You should use recursion:
public void ConutNumber(int count)
{
...
GoThroughElements(count);
...
}
public void GoThroughElements(int count, List<String> recurseValues = new List<String>())
{
foreach(String value in ValuesAdd1)
{
recurseValues.Add(value);
if(count == 1)
{
// In deepest recursion iterate through the line of values
foreach(String i in recurseValues)
Console.WriteLine(i);
}
else if(count > 1)
{
GoThroughElements(--count, recurseValues);
}
else
{
throw new Exception("Wrong count!");
}
}
}
Don't forget to check your count for invalid values. Use recursion with great care, it may easily cause memory issues if a wrong case goes unnoticed.

Related

Encapsulate a string into a div bubble using html

I want users to be able to enter keywords for a product. Each keyword is separated by a ','. As the user types the keywords they become isolated in a little bubble and shown to the user as such.
<label for="KeywordsInput">Keywords:</label>
<InputText id="KeywordsInput" #bind-Value="product.Keywords" maxlength="3000" onfocus="#ChangeHelpNoteVisibility" onblur="#ChangeHelpNoteVisibility" oninput="#IsKeywordComplete" ></InputText>
<ValidationMessage For="#(() => product.Keywords)" />
<p class="keywordHelpNote" hidden=#keywordHelpNoteHidden>Sepearte keywords with a comma</p>
<div class="bubble">test
<span class="bubbleclosebtn" onclick="#DeleteKeyword">×</span>
</div>
private string[] IsKeywordComplete(string allKeywords)
{
string[] condition = { "," };
string[] keywords = allKeywords.Split(condition, StringSplitOptions.None);
return keywords;
}
private void DeleteKeyword(string keywordToDelete)
{
keywords = keywords.Where(val => val != keywordToDelete).ToArray();
}
This is based off the stack overflow add tags feature when you ask a question.

How to pass multi List values using <a> tag?

Basic example link - following link is what I am trying to build. But this is only for single value.
Issue Detail After I select multi colors in <select>, Then i click <a> tag, I want to pass the value of what i select in <select>. Currently after clicking on <a> tag, it wont keep values selcted inside <select>
Step#1 Here I am creating Form with 2 filters. 1 search text box and 2 selectlist
<form asp-page="./index" method="get">
<input type="text" asp-for="SearchString" />
<select asp-for="Colors" asp-items="#Model.Colors_SELECT" class="MultiSelect" multiple>...</select>
...
</form>
Step#2 Display data in table grid. <a> is passing filters to url
<table>
....
<a asp-page="./My_Training"
asp-route-SearchString="#Model.SearchString"
asp-route-Colors="#Model.Colors"
asp-route-SortOrder="#Model.Colors_Sort">
#Html.DisplayNameFor(model => model.MyListData[0].Colors)
</a>
.. // more `<a>` tags. 1 for each column
</table>
Step#3 Back-end code: - mainly bind filter values
[BindProperty(SupportsGet = true)]
public string? SearchString { get; set; }
[BindProperty(SupportsGet = true)]
public List<string>? Colors { get; set; }
public SelectList? Colors_SELECT { get; set; }
public async Task OnGetAsync()
{
// everything is auto bind to properties
}
What I tried: according to google, they recommended using asp-all-route-data with Dictionary. I have tried this code and it doesnt work for multi values. Dictionary doesnt allow same key. for example: if I use asp-all-route-data and pass URL like Colors[0]=Red&Colors[1]=Green than it wont keep values selected inside <select>
I also Tried to do this. This works but the code is a mess and hard to maintain if you have too many filters & columns in data grid
<input type="hidden" asp-for="URL_String" />
...
<a href="/Index? #Model.URL_String&SortOrder=#Model.Colors_Sort">
#Html.DisplayNameFor(model => model.CourseTakenList[0].Colors)
</a>
public string? URL_String { get; set; } = "";
public async Task<IActionResult> OnPostAsync()
{
string? createURL = "?";
createURL += $"SearchString={SearchString}&";
foreach (var p in Colors)
{
createURL += $"Colors={p}&";
}
if (createURL.EndsWith("?") || createURL.EndsWith("&"))
{
createURL = createURL.TrimEnd(createURL[createURL.Length - 1]); //remove last '&'
}
string url = $"{HttpContext.Request.Path}{createURL}";
return Redirect(url);
}
public async Task OnGetAsync()
{
URL_String = Request.QueryString.ToString().Replace("??", "?");
CurrentSort = Request.Query["SortOrder"];
}
Because asp-all-route-data can't pass value with the same key, So you need to add index by yourself, Please refer to this simple demo:
#{
var colors = new Dictionary<string, string>();
var i = 0;
foreach (var item in Model.colors)
{
colors.Add($"colors[{i}]", item);
i++;
}
}
<a asp-page="index" asp-all-route-data="colors">Test</a>
It will pass data like:
?colors[0]=red&colors[1]=green&colors[2]=black
Demo:
=========================Update================
<select multiple id="selectone">
<option value="red">red</option>
<option value="black">black</option>
<option value="white">white</option>
<option value="yellow">yellow</option>
</select>
<button onclick="MySelect()">Select color</button>
<a asp-page="index" asp-route-SearchString="Test" id="color">Test</a>
<script>
function MySelect(){
var result = document.getElementById("selectone").selectedOptions;
var arr=[];
for(let i=0,len = result.length;i<len;i++){
if(result[i].selected){
arr.push("colors["+i+"]=" + result[i].value)
}
}
var str=arr.join("&");
var a = document.getElementById("color").getAttribute('href');
var url = a+ "&" + str;
document.getElementById("color").href = url;
}
</script>
Demo:

C# and HTML Code together in Razor View MVC

I am trying to achieve something like the following with MVC4 Razor Syntax. Here is the Code I am trying to write:
#{
int counter = 1;
#foreach (var item in Model)
{
#if (counter == 1)
{
<div class="section show">
}
else
{
<div class="section">
}
<span>#counter <text> .</text> #Html.DisplayFor(ab => item.Title)</span>
<div class="section-content">
#Html.DisplayFor(ab => item.Description)
</div>
</div>
counter++;
}
}
The above Code doesn't give any Compilation Error. But it causes Run time error. It says the Closing Tag is missing. Is it somehow possible to make the C# Code and HTML Code work together to achieve the result that I am trying to achieve here?
I am trying to get the C# Code and make them work based on Condition. For example, in the if Statement, if Counter is 1, the first div class will be selected and in rest of the times, the second div class will be selected.
Thanks. (I have updated the Question)
Don't use the if to create the div. Use it to determine the value of the class attribute:
#{
int counter = 1;
foreach (var item in Model)
{
result = "section";
if (counter == 1)
{
result = "section show";
}
<div class="#result">
<span>#counter <text> .</text> #Html.DisplayFor(ab => item.Title)</span>
<div class="section-content">
#Html.DisplayFor(ab => item.Description)
</div>
</div>
counter++;
}
}

C# MVC3 Razor alternating items in a #foreach list?

In MVC3, how do you create alternating row colors on a #foreach list when using the Razor view engine?
#foreach (var item in Model) {
<tr>
<td>#item.DisplayName</td>
<td>#item.Currency</td>
<td>#String.Format("{0:dd/MM/yyyy}", item.CreatedOn)</td>
<td>#String.Format("{0:g}", item.CreatedBy)</td>
<td>#Html.ActionLink("Edit", "Edit", new { id = item.Id })</td>
</tr>
}
Assuming you would rather not use CSS (i.e. :nth-child(odd)) you can either:
use a normal for loop:
#for (int i = 0; i < Model.Count; i++)
{
...
}
use .Select:
#foreach (var item in Model.Select((x, i) => new { Data = x, Index = i }))
{
...
}
Either way, you'd have access to the current index and could then use i % 2 == 1 as the condition for your background-color. Something like:
<tr style="background-color:#(i % 2 == 1 ? "red" : "white")">...</tr>
This is what CSS is for (changing style rather than content). Save the server the effort: Do it on the client.
Since you're using Razor, you can use JQuery. Here's how I do it in my projects:
$(document).ready(function () {
$("table > tbody tr:odd").css("background-color", "#F7F7F7");
}
With ASP.NET MVC 3 and the new #helper syntax, there is a neater way to handle this.
First add this #helper method:
#helper AlternateBackground(string color) {
if (ViewBag.count == null) { ViewBag.count = 0; }
<text>style="background-color:#(ViewBag.count % 2 == 1 ? color : "none")"</text>
ViewBag.count++;
}
Then just add the call to the helper in your <TR> element
#foreach (var item in Model) {
<tr #AlternateBackground("Red")>
<td>#item.DisplayName</td>
<td>#item.Currency</td>
<td>#String.Format("{0:dd/MM/yyyy}", item.CreatedOn)</td>
<td>#String.Format("{0:g}", item.CreatedBy)</td>
<td>#Html.ActionLink("Edit", "Edit", new { id = item.Id })</td>
</tr>
}
You could always do it in pure css using:
TABLE.test tr:nth-child(even)
{
background-color: #EFEFEF;
}
How about something like this?
#for (int i = 0; i < Model.length; i++) {
<tr #(i % 2 != 0 ? class="odd" : "")>
<td>#Model[i].DisplayName</td>
<td>#Model[i].Currency</td>
<td>#String.Format("{0:dd/MM/yyyy}", Model[i].CreatedOn)</td>
<td>#String.Format("{0:g}", Model[i].CreatedBy)</td>
<td>#Html.ActionLink("Edit", "Edit", new { id = Model[i].Id })</td>
</tr>
#{
int i = 0;
foreach (Account acct in Model)
{
<div class="row #(i%2==0?"even":"odd")">
#acct.name
</div>
i++;
}
}
Original: http://15daysofjquery.com/table-striping-made-easy/5/
Author: Jack Born
jQuery Zebra_Striping_Made_Easy
=============== Java script ===================
$(document).ready(function () {
$('.stripeMe tr:even').addClass('alt');
$('.stripeMe tr').hover(
function () {
$(this).addClass("highlight");
},
function () {
$(this).removeClass("highlight");
});
});
================= css =================
tr.alt td {
background-color : #F7F7F7;
}
tr.highlight td {
background-color : #bcd4ec;
}
=============== HTML ===============
<table class="stripeMe">
There isn't much documentation on it, but the Loop Helper (http://nuget.org/Packages/Packages/Details/Loop-Helper-for-WebMatrix-0-1) gives you support for detecting Even/Odd/etc. items.
What you can do is to set a variable odd outside of the foreach()
#{
var odd = false;
}
And then, inside your foreach loop, you'd change the value of it and then use it in an if condition to set the alternating classes.
#foreach (var item in Model) {
odd = !odd;
<tr class="#(odd ? "odd" : "even")">
<td>#item.DisplayName</td>
<td>#item.Currency</td>
<td>#String.Format("{0:dd/MM/yyyy}", item.CreatedOn)</td>
<td>#String.Format("{0:g}", item.CreatedBy)</td>
<td>#Html.ActionLink("Edit", "Edit", new { id = item.Id })</td>
</tr>
}
An old post, but none of the answers covered this approach, so I will.
Since you are using MVC Razor utilizing the #helper function is the simplest, cleanest and best approach.
In the App_Code folder of your project add new item or modify your existing CustomeHelpers.cshtml file with the following code:
#helper AlternateBackground(string color, Int32 iViewBagCount) {
if (iViewBagCount == null) { iViewBagCount = 0; }
<text>style="background-color:#(iViewBagCount % 2 == 1 ? color : "none")"</text>
iViewBagCount++;
}
Then on your view your foreach loop would look like this:
#foreach (var item in Model) {
<tr #CustomHelpers.AlternateBackground("#ECEDEE", Model.Count())>
<td>#item.DisplayName</td>
<td>#item.Currency</td>
<td>#String.Format("{0:dd/MM/yyyy}", item.CreatedOn)</td>
<td>#String.Format("{0:g}", item.CreatedBy)</td>
<td>#Html.ActionLink("Edit", "Edit", new { id = item.Id })</td>
</tr>
}
You can pass a color identifier like "#ECEDEE" or the named color "Blue".
This way you only have to add the #Helper function once and it propagates throughout your application and it can be called on each view as needed by referencing the #CustomHelpers function.
what about using the jQuery DataTable plugin. i used it on an MVC2 application i developed.
http://www.datatables.net/
A solution i use to support IE8 (corporate browser, not by choice) was to combine the_lotus's solution with a jquery solution
Since IE8 doesnt support nth-child() use this css
.tableclass tr.even{
background:#E6EDF5;
}
And use jQuery to do this:
$(document).ready(function() {
$(".table tr:nth-child(even)").addClass("even");
});
You could let the framework decide how best to render it, presumably using a bit of browser detection logic and whatever other goodness it has built-in, something like the following, and get on with your life.
:-)
My point being that with this approach the WebGrid will control the alternating grid colors using the best technology it can (best that it is designed to use, at least) for the detected browser. It might not use "nth" CSS syntax, but that might not work for all of your intended audience, anyway, so you'd have to detect the browser and emit different content on your own. Of course everybody should be using a CSS 3.x-compliant browser by now, but mileage varies.
#myWebGrid.GetHtml
(
tableStyle: "some-style",
headerStyle: "some-head-style",
alternatingRowStyle: "some-fancy-alt-row-style",
etc ...
)
The System.Web.Helpers.WebGrid's GetHtml method signature looks like this here:
public IHtmlString GetHtml
(
string tableStyle = null,
string headerStyle = null,
string footerStyle = null,
string rowStyle = null,
string alternatingRowStyle = null,
string selectedRowStyle = null,
string caption = null,
bool displayHeader = true,
bool fillEmptyRows = false,
string emptyRowCellValue = null,
IEnumerable<WebGridColumn> columns = null,
IEnumerable<string> exclusions = null,
WebGridPagerModes mode = WebGridPagerModes.Numeric | WebGridPagerModes.NextPrevious,
string firstText = null,
string previousText = null,
string nextText = null,
string lastText = null,
int numericLinksCount = 5,
object htmlAttributes = null
);
#helper Prop(List prop)
{
foreach (var p in prop)
{
p
}
}
format: #Prop(#item.Prop)

How do I get the children of an Element in Watin?

Given the following html:
<ul id="search-results">
<li>
<h3>Result 1</h3>
<span class="some-info">Tag line</span>
</li>
<li>
<h3>Result 2</h3>
<span class="some-info">Another bit</span>
</li>
<li>
<h3>Result 2</h3>
<span class="some-info">Another bit</span>
</li>
</ul>
I can get the ul element with:
Element ul = ie.Element(Find.ById("search-results"));
How do I iterate over the children of the search-results?
I've managed to get as far as:
var allListItems = ie.Elements.Filter(e => e.Parent != null && e.Parent.Id == "search-results");
But this doesn't help me assert things about the H3 or span contained within the li's.
*Option1:*Based on your code, you can try something like:
ie.Spans.Filter(Find.ByClass("some-info"))[0...2].PreviousSibling;
you will need to loop over each span.
*Option2:*Based on some suggestions from the Watin mail list (similar to your own answer as well):
IElementContainer elem = (IElementContainer)ie.Element(Find.ById("search-results"));
Then you parse through elem.Element
Current best hack I can think of is:
public static class WatinExtensions
{
public static ElementCollection Children(this Element self)
{
return self.DomContainer.Elements.Filter(e => self.Equals(e.Parent));
}
}
ElementCollection results = ie.Element(Find.ById("search-results")).Children();
// and
foreach(Element li in results)
{
Element info = li.Children().First(e => e.ClassName.Contains("some-info"));
}
It works but surely there is a proper why to do this?
And there is a correct way to do this from the watin mailing list:
In my example:
var searchResults = (IElementContainer) ie.Element(Find.ById("search-results"));
foreach (Element element in searchResults.Elements) {
// can do more here
}

Categories

Resources