I am trying to instantiate an object from inside of my view:
In my model I have an entity object called Listing that has a Property object inside of it:
public class Listing
{
...
public Property ListingProperty { get; set; }
...
}
Inside of my view I am referencing the listing model:
#model Realintory.Models.Listing
...
Inside of a form in this view I am trying to set up the properties of the "Property" object like so:
<div class="form-group">
#Html.LabelFor(model => model.ListingProperty.Address, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ListingProperty.Address)
#Html.ValidationMessageFor(model => model.ListingProperty.Address)
</div>
</div>
This throws a null value exception because it hasn't been instantiated. My question is how do I set this up, I know this must be easy but no matter what I try it's not working on the view side.
Things like this fail badly:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
Property toCreate = new Property();
#Model.ListingProperty = toCreate;
...
}
I realize this is probably a newb question but I cant seem to find the answer anywhere so I didn't want to waste anymore time and figured I would ask.
Your controller is supposed to provide a model that represents the data required by the view. If the view needs that property populated in the model then it's the responsibility of the controller to do it. The view should be using the model, not building it.
To add an arbitrary block of code in a Razor view you can do this:
#{
// ...
}
In this case, maybe something like this is appropriate:
#if (Model.ListingProperty == null)
{
Model.ListingProperty = new Property();
}
Related
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.
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.
I'm a little new to ASP.Net MVC, I have a complex model.
public class BuildingPermit
{
public int ApplicationID { get; set; }
public virtual Person Applicant { get; set; }
public virtual Area ApplicantArea { get; set; }
public virtual ICollection<Owner> Owners { get; set; }
/...
}
Using scaffolding, I created the controller and all the views. However, I want to register all the details in the same page, meaning in the BuildingPermit's Create view, creating the details for Applicant of type Person, the ApplicationArea of type Area and so on. Is there any way I can accomplish this?
If it's not possible, I think it's possible to add a link to create the object. When the user clicks on it, the page goes to that view, creates it, get its information back and shows it in the BuildingPermit's view.
I'd appreciate your help.
You could achieve this by creating an editor template for Person, Area, Owner etc in:
~/Views/Shared/EditorTemplates/Person.cshtml
~/Views/Shared/EditorTemplates/Area.cshtml
~/Views/Shared/EditorTemplates/Owner.cshtml
The editor template will want to be strongly typed and should give the editor layout for the type:
#model Models.Person
<h2>Person</h2>
<p>
#Html.LabelFor(model => model.Name)
#Html.EditorFor(model => model.Name)
</p>
<p>
#Html.LabelFor(model => model.Address)
#Html.EditorFor(model => model.Address)
</p>
// And so on
Once you've done this calling #Html.EditorFor(model => model.Applicant) will pick up your template and display within your Edit view.
If you are wanting to display all of this information together then you will probably want to also create display templates for these types. These work just like the editor templates except you keep your templates in a DisplayTemplates folder.
~/Views/Shared/DisplayTemplates/Person.cshtml
~/Views/Shared/DisplayTemplates/Area.cshtml
~/Views/Shared/DisplayTemplates/Owner.cshtml
That's no problem, just make sure you initialise your complex object somehow to avoid null reference exceptions:
public BuildingPermit()
{
this.Applicant = new Person();
this.ApplicantArea = new Area();
...
}
Then in your controller action method create an instance of the model and pass it to your view:
public ActionResult Create()
{
BuildingPermit model = new BuildingPermit();
View(model);
}
For the view:
#model MyNamespace.BuildingPermit
#Html.LabelFor(m => m.Applicant.FirstName)<br />
#Html.TextBoxFor(m => m.Applicant.FirstName)<br />
...
<input type="submit" value="Create new building permit" />
Then look into examples online on how to handle a HttpPost in your MVC controller.
If you want to create specific UI partials for each object type, then you can looking into EditorFor and DisplayFor templates. From what you mention in your original post, this might be what you're looking for also.
Hope this helps.
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)" />
Here is a portion of a partial:
#model IEnumerable<BLL.DomainModel.Jerk>
#foreach (var jerk in Model)
{
using (Html.BeginForm("AddJerk", "Jerk", FormMethod.Post, new { #class = "jerkListForm" }))
{
#Html.HiddenFor(jerk => )
#jerk.Name
...
}
}
The type that the HiddenFor lambda is looking for is the same as the #model (IEnumerable), whereas I'm looking for a single object within that IEnumerable.
What am I missing? Why is it still looking for a collection inside of the foreach loop?
#model IEnumerable<Type>
#foreach(var item in Model)
{
#Html.HiddenFor(model => item)
}
Don't forget that Type must be de/serializable in order for this to work.
All Html For Helpers provided by Asp.Net MVC use the model of the defined model for the page. What you can do is create an EditorTemplate. Create a subdirectory in your View folder called EditorTemplates such as in \Home\EditorTemplates\ and add a new view called Jerk.cshtml and assign your model that way.
Then you can use the Html.HiddenFor(f => f.Name) and so forth for each property. This will give you your typed access that you're expecting.
In your main view you would do the following:
#foreach(var jerk in Model) {
#Html.EditorFor(m => jerk)
}
or you can call EditorForModel where it will automatically loop through and look for the EditorTemplate.
#Html.EditorForModel()
The EditorFor will look for the EditorTemplate you created first then it will go from there until it either finds an override or it will output one MVC thinks you might need.
I would use a for loop instead. That way your hidden field will have the correct name. If you use the helpers like in your example, all the hidden fields will have a name of jerk, which won't post back correctly.
#foreach (var i = 0; i < Model.Count; i++)
{
var jerk = Model[i];
using (Html.BeginForm("AddJerk", "Jerk", FormMethod.Post, new { #class = "jerkListForm" }))
{
#Html.HiddenFor(model => model[i])
#jerk.Name
...
}
}
If binding doesn't matter to you on the postback, you can simply do
#Html.HiddenFor(jerk => jerk)