Property values of the same type are updated incorrectly in Blazor - asp.net-core

Background:
I wanted to achieve the following:
Keep a copy of the data context and use the copy for editing
So that I can reset the data context back to its unchanged state using an onclick event by doing copyValue = unchangedValue
Here is my attempt (it's been trimmed down in size to reduce noises but it has the same issue):
**index.razor**
#page "/"
#using SolutionName.Data
#using System.Reflection
<EditForm Model="Items2">
<table class="table">
<thead>
<tr>
<th>Summary</th>
</tr>
</thead>
<tbody>
#foreach (var i in Items2)
{
<tr #key="#i.GetHashCode()">
<InputText #bind-Value="i.Summary"></InputText>
</tr>
}
</tbody>
</table>
</EditForm>
//
//reflections for debuggings
//
#if (Items != null)
{
<p>
#foreach (var item in Items)
{
<span>#($"Items.{typeof(WeatherForecast).GetProperty(nameof(WeatherForecast.Summary)).Name}={typeof(WeatherForecast).GetProperty(nameof(WeatherForecast.Summary)).GetValue(item)}")</span>
}
</p>
}
#if (Items2 != null)
{
<p>
#foreach (var item in Items2)
{
<span>#($"Items2.{typeof(WeatherForecast).GetProperty(nameof(WeatherForecast.Summary)).Name}={typeof(WeatherForecast).GetProperty(nameof(WeatherForecast.Summary)).GetValue(item)}")</span>
}
</p>
}
#code{
List<WeatherForecast> Items = new List<WeatherForecast>();
List<WeatherForecast> Items2 = new List<WeatherForecast>();
protected override void OnInitialized()
{
Items = new List<WeatherForecast>()
{
new WeatherForecast()
{
Date = DateTime.Now,
Summary = "123",
TemperatureC = 1
}
};
Items2 = Items;
}
private void ResetItems2()
{
Items2 = Items;
}
}
As you can see, I am binding Items2, and not Items, to the <EditForm>.
However, updating the summary seems to update both Items2 and Items. I also noticed that this will not happen if Items and Items2 are of two different types (say that they have exactly the same properties, and I cast one to another...)
Two questions:
Why is Item updated in this case?
Is there a way to only update Items2 and not Items, while allowing Items and Items2 to be the same type?
Detailed steps to reproduce the issue:
Step 1. Initialized and render for the first time
Step 2. Change the value to 456 and then tab away
The expected result should be
Items.Summary=123 (not 456)
Items2.Summary=456

The issue is that you're using reference type assignment. When you assign Items to Items2, you actually assign a pointer to Itemss values. Both variable point to the same list of objects.
If it's applicable create a value type instead. Saving data in the local storage and then retrieving it is a viable solution.
This:
List<WeatherForecast> Items = new List<WeatherForecast>();
List<WeatherForecast> Items2 = new List<WeatherForecast>();
is superfluous. Code like this:
List<WeatherForecast> Items;
List<WeatherForecast> Items2;

Related

Editor method of HtmlHelper class doesn't work with collections

In my view I have a model with a property Details of type List. The collection has 3 elements. Now I need to edit this list in a view.
If I use Html.EditorFor method passing the expression everything works correctly, But if I use Html.Editor method, the binding fails. By "fails" I mean that MVC uses the string editor for all fields (even if they are numbers) passing null as a model.
// this works correctly
#for (var i = 0; i < Model.Details.Count; i++)
{
<li>
#Html.EditorFor(m => m.Details[i].Name)
#Html.EditorFor(m => m.Details[i].Age)
</li>
}
// this doesn't work
#for (var i = 0; i < Model.Details.Count; i++)
{
<li>
#Html.Editor("Details[" + i +"].Name")
#Html.Editor("Details[" + i +"].Age")
</li>
}
I'm using ASP.NET Core 3.0 and didn't test this code against previous versions. For several reasons, I cannot use the EditorFor method so I'm stuck with this problem.
Any ideas?
Editor() HTML Helper method is for simple type view and EditorFor() HTML Helper method is for strongly type view to generate HTML elements based on the data type of the model object’s property.
The definition of Html.Editor:
// Summary:
// Returns HTML markup for the expression, using an editor template. The template
// is found using the expression's Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.
//
// Parameters:
// htmlHelper:
// The Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper instance this method extends.
//
// expression:
// Expression name, relative to the current model. May identify a single property
// or an System.Object that contains the properties to edit.
//
// Returns:
// A new Microsoft.AspNetCore.Html.IHtmlContent containing the <input> element(s).
//
// Remarks:
// For example the default System.Object editor template includes <label> and <input>
// elements for each property in the expression's value.
// Example expressions include string.Empty which identifies the current model and
// "prop" which identifies the current model's "prop" property.
// Custom templates are found under a EditorTemplates folder. The folder name is
// case-sensitive on case-sensitive file systems.
public static IHtmlContent Editor(this IHtmlHelper htmlHelper, string expression);
You could identify a single property for the expression of Editor Tag Helper like below :
#model MVC3_0.Models.Detail
<table>
<tr>
<td>Id</td>
<td>#Html.Editor("Id")</td>
</tr>
<tr>
<td>Name</td>
<td>#Html.Editor("Name")</td>
</tr>
<tr>
<td>Age</td>
<td>#Html.Editor("Age")</td>
</tr>
</table>
public IActionResult Index()
{
var model = new Detail { Id = 1, Name = "jack", Age = 12 };
return View(model);
}
Result:
There is a workaround that you could use TextBox or input instead
#for (var i = 0; i < Model.Details.Count; i++)
{
<li>
#Html.TextBox("Details[" + i + "].Name", Model.Details[i].Name, new { htmlAttributes = new { #class = "text-field" } })
#Html.TextBox("Details[" + i + "].Age", Model.Details[i].Age, new { htmlAttributes = new { #class = "text-field" } })
</li>
}
// input tag helper
#for (var i = 0; i < Model.Details.Count; i++)
{
<li>
<input asp-for="#Model.Details[i].Name" />
<input asp-for="#Model.Details[i].Age" />
</li>
}

Error when converting List to IEnumerable [duplicate]

This question already has answers here:
The model item passed into the dictionary is of type .. but this dictionary requires a model item of type
(7 answers)
Closed 5 years ago.
I am trying to display the default index page for a model. But I get the below error.
The model item passed into the dictionary is of type
'System.Collections.Generic.List 1[System.Boolean]', but this
dictionary requires a model item of type
'System.Collections.Generic.IEnumerable`1[EDIWebsite.Models.Error_Details]'.
Controller
public ActionResult FindRelatedBols(string bolnumber)
{
if (bolnumber == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var error_Summaries = db.Error_Details.Select(r => r.BOL == bolnumber);
if (error_Summaries == null)
{
return HttpNotFound();
}
return PartialView("~/Views/Error_Details/Index.cshtml",error_Summaries.ToList());
}
View
#model IEnumerable<EDIWebsite.Models.Error_Details>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Source_System)
</th>
.
.
.
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</table>
The error is self explanatory. Your view is strongly typed to a collection of Error_Details objects.Your current code is generating IQueryable<bool> as the type of error_Summaries variable and you are later calling the ToList() on that, which will generate a list of boolean values(List<bool>).
Your view is expecting something(IEnumerable<Error_Details>) and your action method is passing something else(List<bool>), hence getting that type mismatch exception!
You need to pass a collection of Error_Details objects to the view. I assume you wanted to pass a filtered list of items which is has the same BOL value as your bolnumber parameter. You can use the LINQ Where method to do the filtering.
var items = db.Error_Details.Where(a=>a.BOL == bolnumber).ToList();
return PartialView("~/Views/Error_Details/Index.cshtml",items);
Assuming the BOL property on Error_Details class is of string type.

Wicket dynamicly add data from database to page

I'm trying to add some data into page from database, after applying "filter"
After submit form, candidate list is update and I want to push this changes into page.
How can I do this in wicket ?
.java file
public class SearchCandidate extends WebPage {
private SearchCandidateModel searchCandidateModel = new SearchCandidateModel();
private List<CandidateEntity> candidate = new ArrayList();
public SearchCandidate(PageParameters p) {
super(p);
final TextField<String> firstName = new TextField<>("first_name", new PropertyModel<String>(searchCandidateModel, "firstName")); //Filter
final DataView dataView = new DataView("simple", new ListDataProvider(candidate)) {
public void populateItem(final Item item) {
final CandidateEntity user = (CandidateEntity) item.getModelObject();
item.add(new Label("firstName", user.getFirstName()));
}
};
Form<?> form = new Form<Void>("step1") {
#Override
protected void onSubmit() {
candidate = databse.findCandidate(searchCandidateModel.getFirstName());
//UPDATE TABLE
}
};
form.add(firstName);
add(form);
add(dataView);
}
}
html file:
<form wicket:id="step1">
<input wicket:id="first_name" type="text" size="30"/>
</form>
<table cellspacing="0" class="dataview">
<tbody>
<tr wicket:id="simple">
<td><span wicket:id="name">Test ID</span></td>
</tr>
</tbody>
</table>
You can make you DataProvider - dynamic:
new ListDataProvider() {
#Override protected List getData() {
if (noFilter) return emptyList
else return database.getList(filter)
}
}
This way the provider will always load the data according to your data filter.
For more information about static vs. dynamic models/providers check:
https://cwiki.apache.org/confluence/display/WICKET/Working+with+Wicket+models#WorkingwithWicketmodels-DynamicModels

How to pass records from one view to another view?

I have a table of records and a submit button.
How do I pass those records from one view to another view while clicking the Submit Button?
Thanks in advance!
You have a table of records (from where are you getting those records?)
I'm guessing
In your controller:
public ActionResult Index()
{
ModelTemp = model = new ModelTemp();
model.listRecord = new List<ModelTempB>();
return view(model);
}
In your view:
#for (int x = 0; x < Model.listRecord .Count; x++)
{
<tr>
<td>
#Html.DisplayFor(modelItem => Model.listRecord[x].Someproperty)
<br/>
#Html.HiddenFor(modelItem => Model.listRecord[x].Someproperty)
</td>
</tr>
}
In your controller for Post:
[HttpPost]
public ActionResult Confirmation(ModelTemp model)
{
//now you can access the list of records
return view();
}
easiest way would be put a hidden input within the form same as submit button, serialize your records to json string on client or server depending on your situation. you can access the form collection and deserialize records from string in other controller.

MVC4 No update on selchange DropDownList Postback

After I have changed selected item in my DropDownList everything works fine. But the view doesnt show the newly selected item (value) in the view.
In my code there is #Model.Name What is wrong? I have already spent do many hours on this problem!
My controller looks like this:
//
// GET: /Province/
public ActionResult Test()
{
ModelState.Clear();
var items = _ProvinceService.GetAll();
ViewBag.Lista = new SelectList(items, "Id", "Name", -1);
var province = _LandskapService.FindByName("xyz");
return View("Test", province);
}
// POST /Province/Test
[HttpPost]
public ActionResult Test(int provinceId)
{
if (ModelState.IsValid)
{
ModelState.Clear();
var model = _ProvinceService.GetAll();
var province = _ProvinceService.FindById(provinceId);
ViewBag.Lista = new SelectList(model, "Id", "Name", province.Id);
return View("Test", province);
}
else
{
return View();
}
}
And here is the view:
#Html.DropDownList("dropDownList3", (SelectList)ViewBag.Lista, "-- select province", new { onchange = "UpdateProvince()" })
<table style="border-collapse:collapse">
<tr>
<th style="width:200px">Name</th>
</tr>
<tr>
<td> #Model.Name </td> Here is the problem: #Model.Name is correct but the view dont show it. Old value is displayed. But debugger shows right value (the selected value).
</tr>
function UpdateProvince() {
alert($('#dropDownList3').val());
var SelCat = $("#dropDownList3").val();
var text = $("#dropDownList3 option:selected").text();
var value = $("#dropDownList3 option:selected").val();
if (SelCat != 0) {
var url = '#Url.Action("Test", "Province")';
// $.get(url, {provinceId : value});
$.post(url, { provinceId: value });
}
}