Getting an error: on line CurrentFilters.Add($"Colors", Colors[i].ToString());
An unhandled exception occurred while processing the request.
ArgumentException: An item with the same key has already been added. Key: Colors
Issue: If Dictionary can't have same key than what are my alternatives? I found few on Google like putting into a List but they don't work with asp-all-route-data.
Also since, it is auto Bind; this mean I can't do something like CurrentFilters.Add($"Colors[i]", Colors[i].ToString()); this will work but it wont bind bc name "Colors[1]" is not same as "Colors".
If you click on <a> tag than it will send URL format: localhost234/Index?SearchString=text%Colors=red&Colors=blue&SortOrder=Colors_asc
back-end code
[BindProperty(SupportsGet = true)]
public Dictionary<string, string> CurrentFilters { get; set; }
[BindProperty(SupportsGet = true)]
public List<string>? Colors { get; set; } //list of colors user selects
public SelectList? Colors_SELECT { get; set; } //fill dropdownlist with colors values
public async Task OnGetAsync()
{
Colors_SELECT = getColors(); //fill dropdown with all colors
//this fill get back into asp-all-route-data
CurrentFilters.Clear();
CurrentFilters.Add("SearchString", SearchString);
for (int i = 0; i < Colors.Count; i++)
{
// ISSUE is here
CurrentFilters.Add($"Colors", Colors[i]);
}
}
Front-End - form has many different filters for my grid-table
<form asp-page="./index" method="get">
<input type="text" asp-for="SearchString" class="form-control" placeholder="Search..." />
<select asp-for="Colors" asp-items="#Model.Colors_SELECT" class="MultiSelect" multiple>...</select>
...
</form>
grid-table - display data from database. Header links are for filtering and sorting. asp-all-route-data has different filters inside that get pass to URL
<Table>
... // header hyper link
<a asp-page="./Index" method="get"
asp-all-route-data="#Model.CurrentFilters"
asp-route-SortOrder="#Model.Colors_Sort" >
#Html.DisplayNameFor(model => model.MyList[0].Colors)
</a>
...
Update: I tried using list inside Dictionary but it gives an error.
[BindProperty(SupportsGet = true)]
public Dictionary<string, List<string>> CurrentFilters { get; set; }
Error:
Error: 'System.Collections.Generic.Dictionary<string,
System.Collections.Generic.List<string>>' to
'System.Collections.Generic.IDictionary<string, string>'.
An explicit conversion exists (are you missing a cast?)
Try using
[BindProperty(SupportsGet = true)]
public Dictionary<string, List<string>> CurrentFilters { get; set; }
instead of public Dictionary<string, string> CurrentFilters { get; set; }
To handle this in your OnGetAsync handler do something like this:
public async Task OnGetAsync()
{
CurrentFilters.Clear();
CurrentFilters.Add("SearchString", new List<string>());
CurrentFilters["SearchString"].Add(SearchString);
CurrentFilters.Add($"Colors", new List<string>());
for (int i = 0; i < Colors.Count; i++)
{
CurrentFilters[$"Colors"].Add(Colors[i].ToString());
}
}
Related
How to select values inside MultiSelect? I just want to select some items inside a dropdownlist from URL Variables
Issue: following line is not selecting items Colors.add(TempArray[i]);
debug: If I remove this line than it works ok and selects values. if (!string.IsNullOrEmpty(result)) ... foreach (var item2 in TempArray) but I need this line to check for null values. Code will get error otherwise
Also If I remove Post Method and it goes to Get directly than it works fine (items get selected). how can I make it work by using post method
Form
<form asp-page="./Index" method="post">
<select asp-for="Colors" asp-items="#Model.Colors_SELECT " class="MultiSelect" multiple>
....
</select>
...
back-end variables
[BindProperty(SupportsGet = true)]
public List<string>? Colors { get; set; }
public SelectList? Colors_SELECT { get; set; }
OnPost() Method - here I am change the URL format. for ex: localhost/index?Colors=red&Colors=Black to localhost/index?Colors=red,Black
public async Task<IActionResult> OnPostAsync()
{
var CurrentFilters = new Dictionary<string, string>();
var ColorsTemp = string.Join(",", Colors);
CurrentFilters.Add("Colors", ColorsTemp);
string query = "";
foreach (var p in CurrentFilters)
{
query += $"{p.Key}={p.Value}&";
}
query = query.TrimEnd(query[query.Length - 1]); //remove last '&'
var url = $"{HttpContext.Request.Path}?{query}"; // create URL
return Redirect(url); // Send new url - call get Method()
}
on OnGet() method
public async Task OnGetAsync()
{
// here i want to get URL values and select those items
string result = Request.Query["Colors"];
if (!string.IsNullOrEmpty(result))
{
string[] TempArray = result.Split(",");
foreach (var item2 in TempArray)
{
Colors.add(TempArray[i]);
}
}
}
I want to create a components by following this steps:
I have a list of items.
I want to loop in this list and create a component like InputNumber.
Add EventCallback to the generic created InputNumber that accept ref of this Inputtext because I want to use this ref to set the focus on this InputNumber.
I have also onblure method that execute some code for me, and I am using the onfocus to return focus to the input after execute this code by onblure
My question How can I get this ref and send it as parameter of EventCallback? The problem here that this components have been generated by loop, so I don't want to create by hand hundred variables to represent ref's.
My concept code like this:
#code{
private void OnFocus(MyInputNumber<double?> obj)
{
if (obj is not null)
{
obj!.Element.Value.FocusAsync();
}
}
}
#foreach(var MyItem in MyList)
{
<EditForm Model="MyItem">
//Some components ..
<label>
Test
<InputNumber #bind-Value="MyItem.MyVal"
#onfocus="#((InputNumber<double?> obj #*wrong*#) =>
OnFocus(obj))"
#onblur=#(() => OnblureHandler(context))
</label>
</EditForm>
}
If you see up the parameter InputNumber<double?> obj, this way is wrong, usually I use #ref=SomeVariable but becasue I created in generic way, I can not do that.
Note:
I don't to adjust my list to be dictionary<MYItemType,InputNumber<double?>>, or create a new class that has InputNumber<double?> as property. I am searching for different way, like go from editcontext to any input has been modified and reset focus on it, I don't know if that possible !
You can add an InputNumber<double?> InputNumberRef { get; set; } property to your model class. Then is the foreach loop bind it to the component reference #ref="MyItem.InputNumberRef" then you can pass it in your callback method #onblur="() => HandleBlur(MyItem.InputNumberRef)".
Here is the demo code that I used. The following code after input onblur event it waits 2 seconds and returns the focus to the input.
#page "/"
#foreach (var item in _items)
{
<EditForm Model="#item">
<InputNumber class="form-control" #ref="#item.InputNumberRef" #bind-Value="#item.Value" #onblur="() => HandleBlur(item.InputNumberRef)" />
</EditForm>
}
#code {
private List<Item> _items = new List<Item>
{
new Item { Value = 10 },
new Item { Value = 30 },
new Item { Value = 20 },
};
private async Task HandleBlur(InputNumber<int> inputNumberRef)
{
if (inputNumberRef.Element.HasValue)
{
await Task.Delay(2000);
await inputNumberRef.Element.Value.FocusAsync();
}
}
public class Item
{
public int Value { get; set; }
public InputNumber<int> InputNumberRef { get; set; }
}
}
Credits to user #enet for suggesting this solution in a different question on stackoverflow.
If your requirement is that you apply some form of complex validation on the content of the input before the user is allowed to leave it, i.e if the handler attached to onBlur fails validation then you want to return focus to the input, then this is how to do that without resorting to dictionaries, ...
I've defined a custom InputText component to demonstrate the principles. You'll need to apply the same principles to any other InputBase component where you want to apply the functionality. The key is defining a delegate Func (which returns a bool) as a parameter which is called when the user tries to leave the control. As everything is contained within the component (a bit of SOLID as pointed out by #BrianParker), we can call the inbuilt Element property to return focus to the component.
#inherits InputText
<input #ref="Element"
#attributes="AdditionalAttributes"
class="#CssClass"
value="#CurrentValue"
#oninput="OnInput"
#onblur="OnBlur" />
#if (validationMessage != string.Empty)
{
<div class="validation-message">
#validationMessage
</div>
}
#code {
private string validationMessage = string.Empty;
[Parameter] public Func<string?, Task<bool>>? BlurValidation { get; set; }
[Parameter] public string ValidationFailMessage { get; set; } = "Failed Validation";
private void OnInput(ChangeEventArgs e)
=> this.CurrentValueAsString = e.Value?.ToString() ?? null;
private async Task OnBlur(FocusEventArgs e)
{
validationMessage = string.Empty;
if (Element is not null && BlurValidation is not null && !await this.BlurValidation.Invoke(this.CurrentValue))
{
await Element.Value.FocusAsync();
validationMessage = ValidationFailMessage;
}
}
}
And a demo page:
#page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
#foreach(var item in model)
{
<EditForm Model=item>
<MyInputText class="form-text" #bind-Value=item.MyCountry BlurValidation=CheckBlur />
</EditForm>
}
#code {
private List<MyData> model = new List<MyData>() { new MyData { MyCountry = "UK" }, new MyData { MyCountry = "Australia" } };
private async Task<bool> CheckBlur(string value)
{
// Emulate some async behaviour to do whatever checking is required
await Task.Delay(100);
// simple test here to demonstrate - I know you could use nornal validation to do this!
return value.Length > 5;
}
public class MyData
{
public string? MyCountry { get; set; }
}
}
I'm not sure I'm happy with the UX using this design, but it's your code.
In the system I'm developing I send a select with multiple options to a string type list in my controller, I now need to link this list that I get from the view to the list with the login class I have in my model. The goal is that each position in the string list becomes a position in the new list.
For example I get the following list with the values that were sent from the view select:
[0] = "147"
[1] = "33"
I need to link this array to my other login list, something like this
Login[0] = "147
Login[1] = "33"
I'll put my codes with comments to explain it better:
View:
<select class="selectpicker" multiple data-live-search="true" asp-for="ListResponsibles">
<option value="147">John</option>
<option value="212">Maria</option>
<option value="33">Luiza</option>
</select>
Model:
//Here I get the options marked in the select
public List<string>ListResponsibles { get; set; }
//I want to pass the list I received from the view to this list
public List<Responsibles> ListResponsiblesData { get; set; }
public class Responsibles
{
public string Login { get; set; }
}
Controller:
public async Task<IActionResult> RegisterTask([FromForm] WebdeskTasks webdeskTasks)
{
//I created this variable to receive data from the other list
var LoginList = webdeskTasks.ListResponsiblesData;
//Here I tried to link each possibility of the view's list array to my list, but it doesn't show value in webdeskTarefas.ListResponsibles [i]
for (int i = 0; i < webdeskTasks.ListResponsibles.Count; i++)
{
LoginList[i].Login = webdeskTasks.ListResponsibles[i];
}
Or the LINQish way:
public async Task<IActionResult> RegisterTask([FromForm] WebdeskTasks webdeskTasks)
{
webdeskTasks.ListResponsiblesData = webdeskTasks.ListResponsibles
.Select(entry => new Responsible { Login = entry })
.ToList();
//....
}
You can try following code:
public async Task<IActionResult> RegisterTask([FromForm] WebdeskTasks webdeskTasks)
{
var LoginList = new List<Responsibles>();
foreach (string i in webdeskTasks.ListResponsibles)
{
Responsibles re = new Responsibles();
re.Login = i;
LoginList.Add(re);
}
webdeskTasks.ListResponsiblesData = LoginList;
//....
}
Hellow,
I'm using ASP.NET CORE with Razor Pages and I'm trying to go through the final step of making my ViewData return a tuple of lists.
I made things work the way I wanted, where I made 2 different ViewDatas return 2 lists, but the order of it wasn't the way I wanted, so I put them both in a tuple to return the 2 lists one after another, subject, text, subject, text, etc.
Creating and adding data to the tuple in my Services Class:
public Tuple<List<string>, List<JToken>> get_data_om_mig_info_data()
{
StreamReader reader = File.OpenText(json_file_name);
JToken data = JToken.Parse(reader.ReadToEnd());
JObject om_mig_info = data["om_mig_info"].Value<JObject>();
List<string> subjects = om_mig_info.Properties().Select(property => property.Name).ToList();
List<JToken> text = om_mig_info.Properties().Select(property => property.Value).ToList();
Tuple<List<string>, List<JToken>> om_mig_data = new Tuple<List<string>, List<JToken>>(subjects, text);
return om_mig_data;
}
Index.cs:
Here Tuuple gets all the items as intended. Item1 Count = 3, and Item2 Count = 3.
But the ViewData remains null.
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
public string Index_title { get; private set; }
public Data_Info_Service Om_mig_service_text { get; set; }
public Tuple<List<string>, List<JToken>> Tuuple { get; set; }
public object aaa;
public IndexModel(ILogger<IndexModel> logger, Data_Info_Service Om_mig_service_text)
{
_logger = logger;
this.Om_mig_service_text = Om_mig_service_text;
}
public void OnGet()
{
Tuuple = Om_mig_service_text.get_data_om_mig_info_data(); // Tuple works
ViewData["data_text"] = Tuuple;
aaa = ViewData["data_text"]; // aaa seems to work as well, has all items
Index_title = "Om mig";
}
}
View:
<div class="data_position">
#foreach (var data in ViewData["data_text"] as IEnumerable<Tuple<List<string>, List<Newtonsoft.Json.Linq.JToken>>>) // Exception here
{
<h5>#data</h5>
}
</div>
Exception:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
(... as System.Collections.Generic.IEnumerable<System.Tuple<System.Collections.Generic.List<string>,
System.Collections.Generic.List<Newtonsoft.Json.Linq.JToken>>>) returned null.
Any help would be very appreciated, thanks.
I think this should be working :
public void OnGet()
{
Var datas = ViewData["data_text"] as Om_mig_service_text.get_data_om_mig_info_data();}
The cast in your View is not valid.
You cannot cast a Tuple containing 2 lists into a single list.
<div class="data_position">
#{
var data = ViewBag.data_text;
var list1 = data.Item1 as List<string>;
var list2 = data.Item2 as List<string>;
}
#foreach (var item in list1)
{
<h5>#item</h5>
}
</div>
So I'm new to ASP and EF and I am wondering how to do this incredibly basic operation, as well as a few questions to go along with doing it.
Currently I have a table we will call Resource;
class Resource
{
int current;
int min;
int max;
};
Right now I have the default CRUD options for this. What I would like is a + / - button on the main list that will manipulate the current value of each resource and update the value in the DB and on screen.
There are also certain operations I'd like to run such as "AddFive" to a selected group of resources.
So my questions;
How do I do this?
Is this scalable? If someone is constantly hitting the buttons this is obviously going to send a lot of requests to my DB. Is there any way to limit the impact of this?
What are my alternatives?
Thanks.
Edit:
To clarify the question; here are my post functions. How / where do I add these in my view to get a button showing for each resource. I just want the action to fire and refresh the value rather than redirect to a new page.
#Html.ActionLink("+", "Increment", new { id = item.ID })
public void Increment(int? id)
{
if (id != null)
{
Movie movie = db.Movies.Find(id);
if (movie != null)
{
Increment(movie);
}
}
}
[HttpPost, ActionName("Increment")]
[ValidateAntiForgeryToken]
public ActionResult Increment([Bind(Include = "ID,Title,ReleaseDate,Genre,Price")] Resource resource)
{
if ((resource.Current + 1) < (resource.Max))
resource.Current++;
return View(resource);
}
It sounds like the main issue you are having is creating a list of movies on the front end and updating the details for a specific one.
The key here is that you will need to either wrap a form around each item and have that posting to your update controller or use ajax / jquery to call the controller instead.
I have given you an example of the first one. Once the update controller is hit it will redirect to the listing page which will then present the updated list of movies.
Below is a minimal working example of how to wire this up. I've not included any data access code for brevity but have included psuedo code in the comments to show you where to place it.
Please let me know if you have any futher questions.
Controller
public class MoviesController : Controller
{
public ViewResult Index()
{
// Data access and mapping of domain to vm entities here.
var movieListModel = new MovieListModel();
return View(movieListModel);
}
public ActionResult Increment(IncrementMovieCountModel model)
{
// Put breakpoint here and you can check the value are correct
var incrementValue = model.IncrementValue;
var movieId = model.MovieId;
// Update movie using entity framework here
// var movie = db.Movies.Find(id);
// movie.Number = movie.Number + model.IncrementValue;
// db.Movies.Save(movie);
// Now we have updated the movie we can go back to the index to list them out with incremented number
return RedirectToAction("Index");
}
}
View
#model WebApplication1.Models.MovieListModel
#{
ViewBag.Title = "Index";
}
<h2>Some Movies</h2>
<table>
<tr>
<td>Id</td>
<td>Name</td>
<td>Increment Value</td>
<td></td>
</tr>
#foreach (var movie in Model.MovieList)
{
using (Html.BeginForm("Increment", "Movies", FormMethod.Post))
{
<tr>
<td>#movie.Id #Html.Hidden("MovieId", movie.Id)</td>
<td>#movie.Name</td>
<td>#Html.TextBox("IncrementValue", movie.IncrementValue)</td>
<td><input type="submit" name="Update Movie"/></td>
</tr>
}
}
</table>
Models
public class MovieListModel
{
public MovieListModel()
{
MovieList = new List<MovieModel> {new MovieModel{Id=1,Name = "Apocalypse Now",IncrementValue = 3}, new MovieModel {Id = 2,Name = "Three Lions", IncrementValue = 7} };
}
public List<MovieModel> MovieList { get; set; }
}
public class MovieModel
{
public int Id { get; set; }
public string Name { get; set; }
public int IncrementValue { get; set; }
}
public class IncrementMovieCountModel
{
public int IncrementValue { get; set; }
public int MovieId { get; set; }
}