System.Random in ViewBag not displaying as string? - c#

All I am trying to do is create a random number in the Controller and pass it to the View. However when I run the application the View only displays 'System.Random' not a generated value.
Here is my controller:
// GET: /Products/Create
public ActionResult Create()
{
Random randomID = new Random(Guid.NewGuid().GetHashCode());
randomID.Next(20, 5000);
ViewBag.random = randomID.ToString();
ViewData["random"] = randomID.ToString();
TempData["random"] = randomID.ToString();
return View();
}
I tried the ViewBag, ViewData, and TempData and they all display 'System.Random.'
Here is my View:
#model application.Models.Product
#{
ViewBag.Title = "Create Product";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Product_ID, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Product_ID, new { #readonly = "readonly" })
#Html.TextBox("random", (string)#ViewBag.random, new { #readonly = true })
#ViewBag.random
#ViewData["random"]
#TempData["random"]
#Html.ValidationMessageFor(model => model.Product_ID)
</div>
</div>
I'm sorry the View is a little messy but I tried all the approaches I could find. What am I missing? I really don't want to have the change the Model. I tried Googling this for hours and nothing can solve my problem. Also is this the simplest approach to creating a random ID number for a product? Any help would be appreciated THANK YOU!

Random.Next actually returns a value, and doesn't mutate the Random object at all. Simply calling ToString on a Random object will always return "System.Random" (as it will for every other class that doesn't override ToString.
You need to put the generated value in the ViewBag:
public ActionResult Create()
{
Random random = new Random(Guid.NewGuid().GetHashCode());
int randomID = random.Next(20, 5000);
ViewBag.random = randomID.ToString();
return View();
}

randomId.Next() returns an integer, you need something more like this:
// GET: /Products/Create
public ActionResult Create()
{
Random randomID = new Random(Guid.NewGuid().GetHashCode());
int randomNumber = randomID.Next(20, 5000);
ViewBag.random = randomNumber.ToString();
return View();
}

Related

ASP.NET Core view only partially post back the ViewModel

I have a ViewModel that includes 4 different classes with a lot of properties.
Now, if I make a submit in my view, only the properties that are bound to an input field are posted back, which is bad, since I have a table based on one of those classes (which are gone after the submit).
I know I could handle it like this:
#Html.HiddenFor(m => m.class.property)
But with the amount of properties, this seems like a very inconvenient approach.
I could also just get my model via query again, but this also seems like an approach that's not right.
Is there a better approach than those I mentioned above?
Edit:
And I tried it this way too:
#foreach (var property in Model.Mandant.GetType().GetProperties())
{
#Html.HiddenFor(p => property)
}
But for some reasons this doesn't work, sadly.
Edit2 for clarification:
I have an viewModel like this:
public class ValidationViewModel
{
public M_IV_INVOICE Invoice { get; set; }
public List<M_IP_INVOICE_POS> Positions { get;}
public S_KR_KREDITOR_ Kreditor { get; set; }
public S_MD_MANDANTEN Mandant { get; set; }
public ValidationViewModel() { }
public ValidationViewModel(int invoiceId)
{
Invoice = CRUD.GetFirstOrDefault(new M_IV_INVOICE(), string.Format(#"WHERE M_IV_ID IN ({0})", invoiceId));
Positions = Invoice != null ? CRUD.GetList(new M_IP_INVOICE_POS(), string.Format(#"WHERE M_IP_INVOICEID IN ({0})", Invoice.M_IV_ID)) : null;
Kreditor = Invoice?.M_IV_KREDITOR != null ? CRUD.GetFirstOrDefault(new S_KR_KREDITOR_(), string.Format(#"WHERE S_KR_KREDITOR IN ({0})", Invoice.M_IV_KREDITOR), "S_KR_KREDITOR") : null;
Mandant = Invoice?.M_IV_MANDANT != null ? CRUD.GetFirstOrDefault(new S_MD_MANDANTEN(), string.Format(#"WHERE S_MD_FIR IN ({0})", Invoice.M_IV_MANDANT)) : null;
}
}
I have a view which looks like this:
#using (Html.BeginForm("Update", "Home"))
{
#Html.ValidationSummary();
<div class="container">
<div class="row">
<div class="form-group col-2">
#*#Html.LabelFor(m => m.Mandant.S_MD_FIR)
#Html.TextBoxFor(m => m.Mandant.S_MD_FIR, string.Format("{0} - {1}", Model.Mandant?.S_MD_FIR, Model.Mandant.S_MD_BEZEICHNUNG), new { #class = "form-control", placeholder = "0" })
#Html.ValidationMessageFor(m => m.Mandant.S_MD_FIR)*#
#Html.Label("Mandant")
<input readonly value="#string.Format("{0} - {1}", Model.Mandant?.S_MD_FIR, Model.Mandant?.S_MD_BEZEICHNUNG)" class="form-control" placeholder="" id="Mandant" />
</div>
<div class="form-group col-2">
#Html.LabelFor(m => m.Invoice.M_IV_KREDITOR)
#Html.TextBoxFor(m => m.Invoice.M_IV_KREDITOR, new { #class = "form-control", placeholder = "0" })
#Html.ValidationMessageFor(m => m.Invoice.M_IV_KREDITOR)
</div>
<div class="form-group col-2">
#Html.LabelFor(m => m.Invoice.M_IV_INVOICEDATE)
#Html.TextBoxFor(m => m.Invoice.M_IV_INVOICEDATE, new { #class = "form-control", placeholder = "0" })
#Html.ValidationMessageFor(m => m.Invoice.M_IV_INVOICEDATE)
</div>
</div>
.....
<button type="submit" class="btn btn-primary">Update</button>
}
Now, if I press the button and call my update method:
[HttpPost]
public IActionResult Update(ValidationViewModel validationViewModel)
{
if (ModelState.IsValid)
{
validationViewModel.Invoice.Update();
// TODO additional logic
}
return View("Index", validationViewModel);
}
Everything that's not bound to an input field or listed with:
#Html.HiddenFor(m => m.Invoice.M_IV_ID)
is nulled.
And now I'm locking for a more convenient way to circumvent this, if even possible.
ASP.NET does not provide the behavior that you're looking for, as far as I know, there is no #Html.HiddenForModel(model => model.child). I guess it's because if you have that level of data complexity on your view, you might want to simplify it and to present only what's necessary for the view. But you can try to "hack" it with #Html.EditorForModel(), wrapped within an invisible container on your view, something like the snippet below:
<div style="display: none">
#Html.EditorForModel()
#Html.EditorFor(model => model.child1)
#Html.EditorFor(model => model.child2)
#Html.EditorFor(model => model.child3)
</div>
To further elaborate on my problem with the List object:
If I do it the way Dorin Raba showed in the answer:
#Html.EditorFor(model => model.child1)
it won't work because, all the textfields are named without an index, so it's not possible to match the values back.
But I can simply do it this way:
#for (int i = 0; i < Model.M_IP_INVOICE_POS.Count; i++)
{
#Html.EditorFor(model => model.M_IP_INVOICE_POS[i])
}
and now every editor field has the correct name and I get the positions back to the controller.
So no need to query my Positions every time I want to Update something.

How do I make the date field #Html.EditorFor receive date coming from a ViewBag

How do I make the date field #Html.EditorFor receive date coming from a ViewBag.
Controller
var searchDateBD = db.Sequence.ToList().Select(l => l.DateFlow).Max();
ViewBag.date = searchDateBD;
View
#Html.EditorFor(model => model.Sequence.DateFlow,
ViewBag.date,
new { htmlAttributes = new { #class = "form-control input-lg" } })
In this way above developed by me is not correct.
How to do so that the field receives the date coming from the viewbag of the controller
You don't need ViewBag here. You can set the model property in the controller action and pass the model object to view back in the line returning View like:
model.Sequence.DateFlow = db.Sequence.Select(l => l.DateFlow).Max();
return View(model);
You also do not need to materialize the result by calling ToList() and leave the Max item to be calculates on the database server.
and in your view it would be like:
#Html.EditorFor(model => model.Sequence.DateFlow,
new { htmlAttributes = new { #class = "form-control input-lg" } })
You can overwrite the value using attribute #Value:
htmlAttributes = new { #class = "form-control input-lg", #Value = ViewBag.date }
Note that #Value is written with an uppercase letter V.

Cannot submit two dimension `List` from view to controller

Class CreateActivityViewModelwill be passed to View.
public class CreateActivityViewModel
{
public List<List<int>> SelectedDepartmentIds { get; set; }
...
}
In the View, using these code to generate html code:
<div class="form-group">
#Html.Label("报名范围", htmlAttributes: new {#class = "col-xs-12"})
<div>
<button id="repeat" type="button">增加单位范围</button>
</div>
#for (var i = 0; i < Model.MaxDepartmentLevel; i++)
{
<div class="col-xs-6">
#if (i == 0)
{
#Html.Label((i + 1) + "级单位", htmlAttributes: new {#class = "control-label col-md-2"})
#Html.DropDownListFor(x => x.SelectedDepartmentIds[0][i], Model.Departments,
"请选择单位", new {#class = "form-control department"})
}
else
{
#Html.Label((1 + i) + "级单位", htmlAttributes: new {#class = "control-label col-md-2"})
#Html.DropDownListFor(x => x.SelectedDepartmentIds[0][i], Enumerable.Empty<SelectListItem>(),
"所有单位", new {#class = "form-control department"})
}
</div>
}
</div>
Here x => x.SelectedDepartmentIds[0][i] is the two dimention List that cannot be passed back to server. When debuging, createActivityViewModel.SelectedDepartmentIds=null. However, Using Chrome Dev Tools to see what is passing to server, I saw SelectedDepartmentIds do submit:
In fact, I have tried almost the same work in other place, the only difference I think is that the success work is passing one dimension List and now two dimension. Does ASP.NET not support binding two dimension List or what?
This should work as expected. From the comments it seems that you have forgotten to include the property to the [Bind(Include=...)] attribute that your Create action is using. This being said, since you are using a view model you don't need any Bind attributes. The properties that are part of the view model will simply be bound. It is much less error prone and explicit.

How do I resolve ModelState.IsValid = false while binding a List type?

I'm getting an ModelState.IsValid = false from a List that contains a class object that has its own id's.
I've seen some examples of how to exclude class properties from the [HttpPost] method while binding that look like this:
[Bind(Exclude="Id,SomeOtherProperty")]
My Question:
How do you exclude the Id that belongs to a property as it does with List? Or, if there's a better way of handling this, please shed some light on the subject.
Here's my PostController.cs:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create([Bind(Include = "Title,URL,IntroText,Body,Created,Modified,Author,Tags")] Post post)
{
if (ModelState.IsValid) /*ModelState.IsValid except for its not... */
{
// this is failing so I unwrapped the code below temporarily
}
using (UnitOfWork uwork = new UnitOfWork())
{
var newPost = new Post
{
Title = post.Title,
URL = post.URL,
IntroText = post.IntroText,
Body = replace,
Author = post.Author,
Tags = post.Tags
};
uwork.PostRepository.Insert(newPost);
uwork.Commit();
return RedirectToAction("Index", "Dashboard");
}
return RedirectToAction("Index", "Dashboard");
}
Update: Relevant excerpt from my Create.cshtml (This turned out to be the problem.)
<div class="form-group">
#Html.LabelFor(model => model.Tags, htmlAttributes: new { #class = "control-label col-md-2 col-md-offet-3" })
<div class="col-md-7">
#for (var i = 0; i < 4; i++)
{
#Html.HiddenFor(m => m.Tags[i].Id)
#Html.EditorFor(model => model.Tags[i].Name)
}
</div>
</div>
Gists: Post.cs | Tag.cs
I wanted to include this picture so you could see, visually, what was failing. Each Tag[i].Id tag is causing the invalid state.
To restate my question, how do I omit the List<Tag> Id from my POST method and achieve valid state?
As #StephenMuecke pointed out in the comments of the OP. I just needed to remove the field from the View:
#Html.HiddenFor(m => m.Tags[i].Id)
Now the ModelState.IsValid returns true.

How to properly use SelectListItem for HTML.DropDownList instead of SelectList?

I've been digging through other posts trying to figure out how to go from using a SelectList in my controller to fill an #Html.DropDownList in my View to instead use what seems to be the commonly suggested SelectListItem, but I'm completely lost?
I have a main INV_Assets model that when I go to the Edit View, I include Drop Down Lists for other Model Properties (Location, Manufacturer, Model, Status, Type, Vendor, etc.) My current code below adequately fills the lists and allows me on Edit() to change the chosen entity value to any other value stored in that relevant table.
CURRENT CODE:
Controller:
// GET: INV_Assets/Edit/5
public async Task<ActionResult> Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
INV_Assets iNV_Assets = await db.INV_Assets.FindAsync(id);
if (iNV_Assets == null)
{
return HttpNotFound();
}
ViewBag.Location_Id = new SelectList(db.INV_Locations, "Id", "location_dept", iNV_Assets.Location_Id);
ViewBag.Manufacturer_Id = new SelectList(db.INV_Manufacturers, "Id", "manufacturer_description", iNV_Assets.Manufacturer_Id);
ViewBag.Model_Id = new SelectList(db.INV_Models, "Id", "model_description", iNV_Assets.Model_Id);
ViewBag.Status_Id = new SelectList(db.INV_Statuses, "Id", "status_description", iNV_Assets.Status_Id);
ViewBag.Type_Id = new SelectList(db.INV_Types, "Id", "type_description", iNV_Assets.Type_Id);
ViewBag.Vendor_Id = new SelectList(db.INV_Vendors, "Id", "vendor_name", iNV_Assets.Vendor_Id);
return View(iNV_Assets);
}
// POST: INV_Assets/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "Id,Model_Id,Manufacturer_Id,Type_Id,Location_Id,Vendor_Id,Status_Id,ip_address,mac_address,note,owner,cost,po_number,description,invoice_number,serial_number,asset_tag_number,acquired_date,disposed_date,created_date,created_by,modified_date,modified_by")] INV_Assets iNV_Assets)
{
if (ModelState.IsValid)
{
db.Entry(iNV_Assets).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.Location_Id = new SelectList(db.INV_Locations, "Id", "location_dept", iNV_Assets.Location_Id);
ViewBag.Manufacturer_Id = new SelectList(db.INV_Manufacturers, "Id", "manufacturer_description", iNV_Assets.Manufacturer_Id);
ViewBag.Model_Id = new SelectList(db.INV_Models, "Id", "model_description", iNV_Assets.Model_Id);
ViewBag.Status_Id = new SelectList(db.INV_Statuses, "Id", "status_description", iNV_Assets.Status_Id);
ViewBag.Type_Id = new SelectList(db.INV_Types, "Id", "type_description", iNV_Assets.Type_Id);
ViewBag.Vendor_Id = new SelectList(db.INV_Vendors, "Id", "vendor_name", iNV_Assets.Vendor_Id);
return View(iNV_Assets);
}
View - Just [Locations] for example:
<div class="form-group">
#*#Html.LabelFor(model => model.Location_Id, "Location_Id", htmlAttributes: new { #class = "control-label col-md-2" })*#
<span class="control-label col-md-2">Location:</span>
<div class="col-md-10">
#Html.DropDownList("Location_Id", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Location_Id, "", new { #class = "text-danger" })
</div>
</div>
What I'm trying to do now is add a value to each list stating "Add New", which I want to allow users to click on and have a (partial view?) popup for them to immediately add a new relevant record (Ex. New [Location] of "Warehouse 2") and then be able to choose that from the [Locations] list for the particular Asset being Edited.
Can anyone walk me through this?
A lot of the suggestions are to add a SelectList or IEnumerable<SelectListItem> to my relevant Model properties, but from there I am lost on what to tweak in my controller/view? Currently I am using Code-First Migrations with an InventoryTrackerContext.cs inside my DAL folder for the project.
You're confusing two very different aspects of this. First, Html.DropDownList only requires an IEnumerable<SelectListItem>. Passing a full SelectList object satisfies this parameter merely because a SelectList is an IEnumerable<SelectListItem>. The advice to not use SelectList is simply to save yourself the work of having to construct a full SelectList object (and remembering to do things like set the selectedValue to the right item), when Razor will handle this for you. Whether you use SelectList or IEnumerable<SelectListItem> has no bearing on the remainder of your question.
As far as adding items to an existing drop down list goes, you have to use JavaScript for that. At a basic level, it's as simple as just just selecting the select element in the DOM and appending a new option node.

Categories

Resources