Encapsulate a string into a div bubble using html - c#

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.

Related

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:

Defining attribute ID at runtime

I'm creating DIVs on runtime based on user input.
#foreach (var item in Model.BUM_List)
{
<div class="shadow col-sm-2 col-xs-4">
<input class="Columndiv" value="#item.Columns" id="" readonly/>
<input type="checkbox"/><label id="lblSelectColumn">Include this Column</label>
</div>
}
My question is, how do I define ID for each of these newly created DIVs, after creation? It can be names as Column1, Column2, etc.
I tried using the following function, but it's only useful after the DIVs have been created.
$("#Submit_Table").click(function () {
var i = 0;
$('.Columndiv').each(function () {
i++;
var newID = 'menu' + i;
$(this).attr('id', newID);
$(this).val(i);
});
});
Try adding dynamic id by getting the value from item
<div class="shadow col-sm-2 col-xs-4" id="topDiv#item.code">
I am assuming your item will have some unique id which you can use.

Form fields in array uniquely identified with item Id - how to access their values in the controller?

Let's say I have this dynamically created form:
<form>
Item #1 <input type="text" name="qty" id="Item_3223" value="3">
Item #2 <input type="text" name="qty" id="Item_2243" value="1">
Item #3 <input type="text" name="qty" id="Item_5328" value="1">
Item #4 <input type="text" name="qty" id="Item_3250" value="5">
<!--... and so on...-->
<button type="submit">Update quantities</button>
</form>
The fields are generated server side like this:
<input type="text" name="qty" id="Item_#item.Id" value="#item.Qty">
In my controller, how can I receive the array and perform an update on each of the items in it?
Simplified, my Update-method looks like this now:
public async Task<IActionResult> Update(IFormCollection form)
{
var updateItem = new ShoppingCartItem();
foreach (string item in form.Keys)
{
updateItem = (from c in _context.ShoppingCartItems
where (c.ShoppingCartId == ShoppingCartItem.ShoppingCartId &&
c.ProductId == form.[what goes here?])
select c).FirstOrDefault();
updateItem.Quantity = form.[what goes here?]
}
_context.SaveChangesAsync();
return RedirectToAction("Index", "Cart");
}
Since I can't know the name of the form.Keys, how can I extract the values from them? I seem to remember this was quite easy in classic ASP back in the day ...
Edit
With help from #David, I put this together:
<input asp-for="#item.Quantity" name="Item_#item.ProductId" id="Item_#item.ProductId" />
... and:
var updateItem = new ShoppingCartItem();
int qty = 0;
int pId = 0;
foreach (string item in form.Keys.Where(k => k.StartsWith("Item_")))
{
pId = int.Parse(item.Replace("Item_", ""));
updateItem = (from c in _context.ShoppingCartItems
where (c.ShoppingCartId == ShoppingCartItem.ShoppingCartId && c.ProductId == pId)
select c).FirstOrDefault();
qty = int.Parse(form[item]);
updateItem.Quantity = qty;
}
await _context.SaveChangesAsync();
Works like a charm! Thanks! :)
You're not passing an array to the server-side code. You're passing a single value with the name "qty". This would likely be the last instance of that input in your form.
An array needs brackets in the name:
<input type="text" name="qty[]" id="Item_#item.Id" value="#item.Qty">
Then in the server-side code the "key" for the array is "qty[]". You can pull that array from the form object and iterate over it for your needs:
var inputArray = form["qty[]"];
Basically, all you need is the brackets in the name to tell the browser that these multiple elements constitute an array of values. The id has nothing to do with form posting.
Edit: If you do need that "id" value, then you can put it in the name:
<input type="text" name="Item_#item.Id" id="Item_#item.Id" value="#item.Qty">
Then you would have multiple unique key/value pairs in the form post, which you can check as you're currently attempting to (foreach (string item in form.Keys)) and then parse out the "id" from that item string as needed.
It sounds like what you need is a dictionary. For that, you'd need inputs like the following:
<input type="hidden" name="qty[0].Key" value="#item.Id" />
<input type="text" name="qty[0].Value" value="#item.Qty" />
You'd bind that to a param like Dictionary<int, int> qty. Then, you can access the quantities for each item via something like:
item.Qty = qty[item.Id];
That said, it's usually better to just use view models for this kind of thing, so you can bind to properties in a strongly-typed way. For example, you could use something like:
public class ItemQuantityViewModel
{
public int Id { get; set; }
public int Qty { get; set; }
}
Then, you can have as the model for your form (and the param you bind to on your action) be List<ItemQuantityViewModel>. Finally, you'd just have input like:
#for (var i = 0; i < Model.Count; i++)
{
<input type="hidden" asp-for="#Model[i].Id">
<input asp-for="#Model[i].Qty">
}

Select a dropdown element with dynamic ID using Webdriver, 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.

javascript to dynamically add a textbox

I am trying to dynamically add a text box based on the selection of dropbox. When the user selects 'other' a text box gets generated asking them to explain the other. The user can dynamically add mutiple dropboxes, resulting in multiple text boxes if other is selected value in dropbox. Every generated dropbox has a unique name which gets read and placed in id of dynamically generated textbox.
The problem i am facing is that when there are multiple dropboxes and the first dropbox selection is something other that 'Other' and second dropbox value is other, the text box generated is placed in front of the first dropbox where it should be placed in front of the dropbox relevant to it.
The html code is as follows for the dropbox:
<div id="container">
<label id="rightlabel"for="dropbox1"></label>
<span>
<select id="frequency" onclick="getData(this, name)" name="dropbox[4fb103e3-06e7-4c88-8836-73b855968478]"></select>
<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="dropbox[4fb103e3-06e7-4c88-8836-73b855968478]"></span>
</span>
<div id="hiddenothertexbox"></div>
</div>
Javascript is as follows:
function getData(title, i) {
var value = title.options[title.selectedIndex].value;
var y = i.replace(/-/g, '')
$('#hiddenother').attr('id', y);
if (value == 'Other') {
str = '<label id="leftlabel">if other, please specify</label><span><input id="textboxid" class="text-box single-line" type="text" value="" name="textbox ></input></span>';
$('#'+y).html(str);
}
else {
str = '';
$('#'+y).html(str);
}
}
teh javascript gets the names of the dropbox and replaces the id of 'hiddenanothertextbox' with that id so its unique. I have an idea of the problem, i think its because when the user does not click teh first dropbox the id of 'hiddenanothertextbox' does not change for the first dropbox and when another dropbox is added and the value is changed, the hiddenanothertextbox for first dropox value changes adding it in front of first not second. I am struggling to achieve teh required result.
UPDATED JAVASCRIPt
function getData(title, i) {
var value = $(title).val();
var y = i.replace(/-/g, '');
$('#hiddenother').attr('id', y);
if (value == 'Other') {
str = '<label id="leftlabel">if other, please specify</label><span><input id="textboxid" class="text-box single-line" type="text" value="" name="textbox"" ></input></span>';
$(title).after(str);
}
else {
$(title).nextUntil('#textboxid').remove();
}
}
Working Demo
Use this code snippet.
var flag = 0;
function getData(title, i) {
var value = $(title).val();
var y = i.replace(/-/g, '');
$('#hiddenother').attr('id', y);
if (value == 'Other' ) {
if(flag == 0){
flag=1;
str = '<label id="leftlabel">if other, please specify</label><span><input id="textboxid" class="text-box single-line" type="text" value="" name="textbox"></input></span>';
$(title).after(str);
}
}
else {
flag=0;
$(title).nextUntil('#textboxid').remove();
}
}
Html :
<select id="frequency" onchange="getData(this, name)" name="dropbox[4fb103e3-06e7-4c88-8836-73b855968478]">
Changes :
var value = $(title).val() gives you the correct value
Use onchange="getData(this, name)" instead of onclick=".."
In variable str : name="textbox" did not have ending "
use .after() or .append() instead of .html()

Categories

Resources