Populate a viewmodel with a newly added database ID - asp.net

This is a follow up to a question that was asked yesterday.
I have a viewmodel, which shows a list of objectives. Using jquery I can add a new objectives line to the screen (the ID is set to 0 for any new objectives listed). When I click on the Save button to Post the objective list back to the controller, the controller loops through the objective list, and checks the ID against the database. If the ID is NOT found, it creates a new objective, adds this to the DB context, and saves te changes. It then retreives the ID, and returns the View(model) to the View.
The problem is, although the ID in the model, is updated to the database ID - when the model is rendered in the View again, it's ID is still 0. So if I click Save again, it again, re-adds the "new objective added previously" to the database again.
My controller is shown below:
//
// POST: /Objective/Edit/model
[HttpPost]
public ActionResult Edit(ObjectivesEdit model)
{
if (model.Objectives != null)
{
foreach (var item in model.Objectives)
{
// find the database row
Objective objective = db.objectives.Find(item.ID);
if (objective != null) // if database row is found...
{
objective.objective = item.objective;
objective.score = item.score;
objective.possscore = item.possscore;
objective.comments = item.comments;
db.SaveChanges();
}
else // database row not found, so create a new objective
{
Objective obj = new Objective();
obj.comments=item.comments;
obj.objective = item.objective;
obj.possscore = item.possscore;
obj.score = item.score;
db.objectives.Add(obj);
db.SaveChanges();
// now get the newly created ID
item.ID = obj.ID;
}
}
}
return View(model);
}
My ID is being set in the controller:
EDIT: Another example here, showing model.Objectives1.ID being updated:
However when the view renders it, it reverts to 0:
The Objectives list is determined as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MvcObjectives2.Models
{
public class ObjectivesEdit
{
public IEnumerable<Objective> Objectives { set; get; }
public ObjectivesEdit()
{
if (Objectives == null)
Objectives = new List<Objective>();
}
}
}
The View has:
#model MvcObjectives2.Models.ObjectivesEdit
#using (Html.BeginForm())
{
#Html.EditorFor(x=>x.Objectives)
<button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i> Save</button>
}
and in my EditorTemplate (objective.cshtml):
#model MvcObjectives2.Models.Objective
<div class="objec">
<div>
#Html.TextBoxFor(x => x.objective})
</div>
<div>
#Html.TextBoxFor(x => x.score})
</div>
<div>
#Html.TextBoxFor(x => x.possscore})
</div>
<div>
#Html.TextBoxFor(x => x.comments})
#Html.HiddenFor(x => x.ID) // This is the ID where it should now show the new ID from the database, but shows 0
</div>
</div>
I suspect the issue is somewhere in my controller - but I would appreciate any advise on how to get my View to render the new ID of the added objective.

After rewording my search, I came across several posts which say this is by design. A Posted form expects to display what it sent to the controller, if the same page is shown again.
However, you can add this, which will flush ModelState, and apparantly show the updated values from the model, updated in the controller:
ModelState.Clear();
return View(model);
I'm not certain if this has any other effect yet - but for now, it appears to work ok.
Thanks, Mark

The Html.HiddenFor has bitten me before in a similar scenario. The problem is when using this Html helper the hidden value is not updated on the re-post.
If you post something from the form and change it inside your controller, when you re-render the page using it will use the value which was originally posted to the action.
Instead use
<input type="hidden" name="ID" id="ID" value="#Html.Encode(Model.ID)" />

Related

Display Successful Message after HttpPost

I have a view which has a registration form. If the registration form is submitted I want to return to the same view and display a temporary Bootstrap Well and then fade it out. Check my idea out in my controller
Controller
// Insert User
[HttpPost]
public void AddUser(ResourceViewModel resourceInfo)
{
// Fetch data from ViewModel as parameters Execute Stored Procedure
db_RIRO.sp_InsertNewUser(resourceInfo.Username, resourceInfo.Password);
db_RIRO.SaveChanges()
// My Idea
if (storedProcedure succesful)
{ // display success ViewBag in view }
else
{
// display failed ViewBag in view
}
}
View
<div class="form-group">
<label class="col-sm-3 control-label lb-sm" for="textinput">Password</label>
<div class="col-sm-5">
#Html.TextBoxFor(a => a.Password, new { #class = "form-control input-sm" })
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label lb-sm" for="textinput">Username</label>
<div class="col-sm-5">
#Html.TextBoxFor(a => a.Username, new { #class = "form-control input-sm" })
</div>
</div>
How would I achieve this using ViewBag?
You haven't said whether you return to the same view or not, but in whatever view you do return to, you can print the TempData out.
Here's a basic example:
Controller:
[HttpPost]
public void AddUser(ResourceViewModel resourceInfo)
{
// Fetch data from ViewModel as parameters Execute Stored Procedure
db_RIRO.sp_InsertNewUser(resourceInfo.Username, resourceInfo.Password);
db_RIRO.SaveChanges()
// My Idea
if (storedProcedure succesful)
{
// display success tempdata in view
TempData["Message"] = "Data saved successfully";
}
else
{
// display failed tempdata in view
TempData["Message"] = "Sorry, an error has occurred";
}
//...etc
}
View (place this anywhere you like in the view):
#if (TempData["Message"] != null)
{
#Html.Raw(TempData["Message"].ToString())
}
This example just uses a simple string, but you can use a more complex data structure if required (e.g. I imagine you might want to set colour schemes / CSS classes for success / failure, for instance, or add Javascript to get things like fade effects - you can place that script within your if statement. Maybe consider creating a re-usable partial view and a "Message" object to use as the model for it which can convey all that kind thing, and you can use it throughout your application.
N.B. If you're returning to the same view, you can always just use the ViewBag instead of TempData - TempData can be useful because will persist across requests, e.g. if you redirect to another action at the end of your current action, instead of directly returning a view.

How to call a other action method and create a list in my view

I have a registration form and a grid(table)for viewing all records..
but i dont know how to display from different action method
My code shown below
#using (Html.BeginForm("Create", "Bathrooms", FormMethod.Post))
{
#Html.LabelFor(model => model.BathRoomDetails)
#Html.TextBoxFor(model => model.BathRoomDetails)
<br/>
#Html.LabelFor(model => model.shortstring)
#Html.TextBoxFor(model => model.shortstring)
<br/>
<button type="submit" class="btn btn-primary"/> Create</button>
<button type="submit"> View All</button>
}
and my grid code
<table>
#foreach (var a in ViewBag.data as List<RealEstate.Models.BathRoomVM>)
{
<tr>
<td>#a.BathRoomDetails</td>
<td>#a.BActive</td>
</tr>
}
</table>
and my controller
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(BathRoomVM Bathrooms)
{
ModelState.Clear();
var realContext = new RealEstateDBContext();
Bathroom Bathroomobj = new Bathroom();
Bathroomobj.BathRoomDetails = Bathrooms.BathRoomDetails;
Bathroomobj.shortstring = Bathrooms.shortstring;
realContext.Bathrooms.Add(Bathroomobj);
realContext.SaveChanges();
ModelState.Clear();
return View();
}
public ActionResult Get()
{
var realContext = new RealEstateDBContext();
var rslt = (from bathroom in realContext.Bathrooms
select new BathRoomVM { BathRoomDetails = bathroom.BathRoomDetails, BActive = bathroom.BActive, Bcancelled = bathroom.Bcancelled }).Where(m => m.BActive == true && m.Bcancelled == false).ToList();
ViewBag.data = rslt;
return View();
}
I need To Load or Display Grid Only after click ViewAll Button .. I already try different ways but none of them worked .. Please help me how to implement , i have stuck in this problem from last 2 days..
I have use
public ActionResult Create()
{
var realContext = new RealEstateDBContext();
var rslt = (from bathroom in realContext.Bathrooms
select new BathRoomVM { BathRoomDetails = bathroom.BathRoomDetails, BActive = bathroom.BActive, Bcancelled = bathroom.Bcancelled }).Where(m => m.BActive == true && m.Bcancelled == false).ToList();
ViewBag.data = rslt;
return View();
}
I got result in this way , too slow (more time required to search data from db ) ..
[ I need to call a other action method and create a list in my view ]
You really need to get away from using viewbag. It's a clear sign that you not comfortable using view models. With that said, your going to need to use jQuery, a view model and a partial view.'
Here are the pieces:
1. Replace your table area is a div that will then hold your table that will be returned from a partial view.
Create a partial view .cshtml file that contains your table logic and uses a view model that will also be returned from your partial view controller action.
You'll need to assign a click event with a prevent default action on your submit button to prevent it from submitting the page but instead to call the partial view controller action to update the table area.
If I had some clarity I might be able to help a little more. When you click Create, do you want it to update the table or only when you click view all?
A couple other thoughts. You could use jQuery to add a new row in your table each time you create and then use an jQuery ajax call to send the bathroom data to the server.
Another feature that might be helpful is to use a MemoryCache. It's fairly easy to implement but you would need to reset it each time you add a new bathroom otherwise you'll not get an updated list.
If you need something specific, let me know.

Getting values of check-box from formcollection in asp.net mvc

I viewed some topics here but I still have a problem with getting values from checkboxes.
Part of Model :
public Dictionary<Language, bool> TargetLanguages { get; set; }
Part of View :
<div class="editor-label">
<label for="TargetLanguages">select target languages</label>
</div>
<div class="editor-field">
<form>
#foreach (var item in Model.TargetLanguages)
{
#Html.CheckBox("TargetLanguages["+item.Key.Name+"]", item.Value)
#item.Key.Name
}
</form>
</div>
Part of Controller :
[HttpPost, ActionName("AddDictionary")]
public ActionResult AddDictionary(FormCollection collection)
{
...
}
And the problem is I don't get any trace of TargetLanguages in my FormCollection. I tried CheckBoxFor but it wasn't help. I tried write check-box manually also.
EDITED : Okay, I just noticed where the problem was. I've got messed up markers and that was the reason why I can't get data from FormCollection.
Create all the checkboxes with the same name. In this sample I'm using 'SelectedTargetLanguages'.
#using (Html.BeginForm())
{
foreach (var item in Model.TargetLanguages)
{
<label>
#Html.CheckBoxFor(m => m.SelectedTargetLanguages, item.value)
#item.KeyName
</label>
}
<br/>
#Html.SubmitButton("Actualizar listado")
}
Then, in your action the parameter must be an array of strings like this:
public ActionResult AddDictionary(string[] selectedTargetLanguages)
Note that the name of the argument is the same name of the checkboxes. (It works even with the different casing).
You should use explicit arguments like this, rather than the generic FormCollection. Anyway, if you use FormCollection, you shpuld also receive the array.
I have asked same type of question previously. Please check the following links
MVC3 #Html.RadioButtonfor Pass data from view to controller
MVC3 #html.radiobuttonfor
I think this might helps you.

Asp.Net MVC Return to page on error with fields populated

I am starting a new project in Asp.net MVC 2.
I have been mostly a webforms developer and have limited exposure to Asp.Net MVC and hence this is probably a noob question.
My situation is as follows:
I have a create page for saving some data to the DB.
The view for this page is not strongly bound / typed - so the way I am extracting the data from the view is by looking at the POST parameters.
Incase there is an error (data validation, etc), I need to send the user back to the previous page with everything filled in the way it was and displaying the message.
On webforms, this got handled automatically due to the view state - but how can I go about doing the same here?
A code example can be as follows:
View:
<% using (Html.BeginForm("Create", "Question", FormMethod.Post)) { %>
<div>
Title: <%: Html.TextBox("Title", "", new { #style="width:700px" })%>
</div>
<div>
Question: <%: Html.TextBox("Question", "", new { #style="width:700px" })%>
</div>
<input type="submit" value="Submit" />
<% } %>
Controller:
[HttpPost]
[ValidateInput(false)]
public ActionResult Create() {
Question q = new Question();
q.Title = Request.Form["Title"];
q.Text = Request.Form["Question"];
if(q.Save()) {
return RedirectToAction("Details", new { id = q.Id });
}
else {
// Need to send back to Create page with data filled in
// Help needed here
}
}
Thanks.
You could simply return the View in case of error. This will preserve the context.
[HttpPost]
[ValidateInput(false)]
public ActionResult Create(Question q) {
if(q.Save()) {
return RedirectToAction("Details", new { id = q.Id });
}
else {
// Need to send back to Create page with data filled in
// Help needed here
return View();
// If the view is located on some other controller you could
// specify its location:
// return View("~/Views/Question/Create.aspx");
}
}
Also I would recommend you to use strongly typed views along with the strongly typed helpers. Notice how I used a Question object directly as action parameter. This is equivalent to the code you have written in which you were manually extracting and building this object. The model binder does this job automatically for you.

Retrieving data from Html.DropDownList() in controller (ASP MVC) | string returned?

I have the following problem:
I have a form in site/banen (currently local running webserver) which is using a SQL database. The link is made using ADO.net and is instantiated in the controller in the following way:
DBModelEntities _entities;
_entities = new DBModelEntities(); // this part is in the constructor of the controller.
Next, I use this database to fill a Html.DropDownList() in my view. This is done in two steps. At the controller side we have in the constructor:
ViewData["EducationLevels"] = this.GetAllEducationLevels();
and a helper method:
public SelectList GetAllEducationLevels()
{
List<EducationLevels> lstEducationLevels = _entities.EducationLevels.ToList();
SelectList slist = new SelectList(lstEducationLevels, "ID", "Name");
return slist;
}
In the view I have the following:
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<!-- various textfields here -->
<p>
<label for="EducationLevels">EducationLevels:</label>
<!-- <%= Html.DropDownList("EducationLevels", ViewData["EducationLevels"] as SelectList)%> -->
<%= Html.DropDownList("EducationLevels", "..select option..")%>
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
Now, the form is rendered correctly when I browse to the create page. I can select etc. But when selected I have to use that value to save in my new model to upload to the database. This is where it goes wrong. I have the following code to do this in my controller:
//
// POST: /Banen/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection form)
{
// set rest of information which has to be set automatically
var vacatureToAdd = new Vacatures();
//vacatureToAdd.EducationLevels = form["EducationLevels"];
// Deserialize (Include white list!)
TryUpdateModel(vacatureToAdd);
// Validate
if (String.IsNullOrEmpty(vacatureToAdd.Title))
ModelState.AddModelError("Title", "Title is required!");
if (String.IsNullOrEmpty(vacatureToAdd.Content))
ModelState.AddModelError("Content", "Content is required!");
// Update the variables not set in the form
vacatureToAdd.CreatedAt = DateTime.Now; // Just created.
vacatureToAdd.UpdatedAt = DateTime.Now; // Just created, so also modified now.
vacatureToAdd.ViewCount = 0; // We have just created it, so no views
vacatureToAdd.ID = GetGuid(); // Generate uniqueidentifier
try
{
// TODO: Add insert logic here
_entities.AddToVacatures(vacatureToAdd);
_entities.SaveChanges();
// Return to listing page if succesful
return RedirectToAction("Index");
}
catch (Exception e)
{
return View();
}
}
#endregion
It gives the error:
alt text http://www.bastijn.nl/zooi/error_dropdown.png
I have found various topics on this but all say you can retrieve by just using:
vacatureToAdd.EducationLevels = form["EducationLevels"];
Though this returns a string for me. Since I'm new to ASP.net I think I am forgetting to tell to select the object to return and not a string. Maybe this is the selectedValue in the part where I make my SelectList but I can't figure out how to set this correctly. Of course I can also be complete on a sidetrack.
Sidenote: currently I'm thinking about having a seperate model like here.
Any help is appreciated.
You can't return an object from usual <SELECT> tag wich is rendered by Html.DropDownList() method, but only string variable could be returned. In your case ID of EducationLevels object will be send to the server. You should define and use one more custom helper method to reconstruct this object by ID.

Resources