Add to list mvc - asp.net-mvc

I'm trying to add string data to list, but have null reference exception and don't have any idea how to fix this.
Here home controller:
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(ChatUser model)
{
if (ModelState.IsValid)
{
**model.chatGroup.Add(model.inputGroups);** - here excepton
}
return View(model);
}
And Index.cshtml:
#model test.Models.ChatUser
#{
ViewBag.Title = "Test";
}
<h2>#ViewBag.Title.</h2>
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
<div class="form-group">
#Html.LabelFor(m => m.inputGroups, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.inputGroups, new { #class = "form-control" })
<input type="submit" value="GO" class="btn btn-default" />
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}

Your form does not include any controls for properties of chatGroup so its null when you post back. You need to either initialize the property in a parameterless constructor
public class ChatUser
{
public ChatUser()
{
chatGroup = new List<string>(); // initialize collection
}
....
public List<string> chatGroup { get; set; }
}
or initialize it in the POST method
[HttpPost]
public ActionResult Index(ChatUser model)
{
if (ModelState.IsValid)
{
model.chatGroup = new List<string>(); // initialize collection
model.chatGroup.Add(model.inputGroups);
}
return View(model);
}

Related

how i can send multivalue to create action

i have a doctor i want add doctor subspecialty to the doctor from sub specialties table many to many relationship
i need to add subspecialties from multiselect list but my controller only add first selection , i want my create controller take all passed subspecialties and create it
my model
public partial class DoctorSubSpecialty
{
public int Id { get; set; }
public Nullable<int> DoctorId { get; set; }
public Nullable<int> SubSpecialtyId { get; set; }
public virtual DoctorProfile DoctorProfile { get; set; }
public virtual SubSpecialty SubSpecialty { get; set; }
}
}
create get action
public ActionResult Create()
{
ViewBag.DoctorId = new SelectList(db.DoctorProfiles, "Id", "FullName");
ViewBag.SubSpecialtyId = new MultiSelectList(db.SubSpecialties, "id", "Name");
return View();
}
create post action
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "Id,DoctorId,SubSpecialtyId")] DoctorSubSpecialty doctorSubSpecialty)
{
DoctorSubSpecialty doctorSub = db.DoctorSubSpecialties.Where(d => d.DoctorId == doctorSubSpecialty.DoctorId & d.SubSpecialtyId == doctorSubSpecialty.SubSpecialtyId).FirstOrDefault();
if (doctorSub == null) {
db.DoctorSubSpecialties.Add(doctorSubSpecialty);
await db.SaveChangesAsync();
}
my view
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DoctorSubSpecialty</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.DoctorId, "DoctorId", htmlAttributes: new { #class = "control-label col-md-2", #id = "DoctorID" })
<div class="col-md-10">
#Html.DropDownList("DoctorId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.DoctorId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SubSpecialtyId, "SubSpecialtyId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("SubSpecialtyId",(MultiSelectList)ViewBag.SubSpecialtyId, htmlAttributes: new { #multiple = "multiple", #class = "form-control" })
#Html.ValidationMessageFor(model => model.SubSpecialtyId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Create a ViewModel specific to your usecase that can actually transport more than one Id.
I.e. you will need an int[] to bind the selection to.
A ViewModel also helps you to get rid of all this ViewBag and [Bind] nonsense.
public class CreateDoctorSubSpecialtyViewModel {
// These are the selected values to be posted back
public int DoctorId { get; set; }
public int[] SubSpecialtyIds { get; set; }
// These are the possible values for the dropdowns
public IEnumerable<SelectListItem> DoctorProfiles { get; set; }
public IEnumerable<SelectListItem> SubSpecialties { get; set; }
}
GET action - initialize the ViewModel and pass it to the View:
[HttpGet]
public ActionResult Create() {
var doctorProfiles = db.DoctorProfiles.Select(d =>
new SelectListItem {
Text = d.FullName,
Value = d.Id
}
).ToArray();
var subSpecialties = db.SubSpecialties.Select(s =>
new SelectListItem {
Text = s.Name,
Value = s.id
}
).ToArray();
var viewModel = new CreateDoctorSubSpecialtyViewModel {
DoctorProfiles = doctorProfiles,
SubSpecialties = subSpecialties
};
return View("Create", viewModel);
}
View "Create.cshtml" (styling removed for clarity) - tell MVC which ViewModel we want to use with #model:
#model CreateDoctorSubSpecialtyViewModel
#using (Html.BeginForm("Create", "YourControllerName", FormMethod.Post)) {
#Html.DropDownListFor(m => m.DoctorId, Model.DoctorProfiles)
#Html.DropDownListFor(m => m.SubSpecialtyIds, Model.SubSpecialties, new { multiple = "multiple" })
<input type="submit" />
}
POST action - use Linq Contains to test against multiple submitted SubSpecialtyIds:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(CreateDoctorSubSpecialtyViewModel postData) {
DoctorSubSpecialty[] allSelectedSubSpecialities = db.DoctorSubSpecialties
.Where(d => d.DoctorId == postData.DoctorId
&& postData.SubSpecialtyIds.Contains(d.SubSpecialtyId))
.ToArray();
// ...
}
EDIT #Html.DropDownListFor requires an IEnumerable<SelectListItem> as second parameter.

MVC model conflict using partial class and Validate

I have a page containing a form and a partial view (containing a form too).
both model have 1 (or more) properties with the same name. when I validate the first form, the value and validation message is duplicate on the second form.
I create a little sample with dummy entities.
person.cs
public partial class Person : IValidatableObject
{
[Required(ErrorMessage = "name required")]
public string Name { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (Name == "admin") //just example
{
results.Add(new ValidationResult("You cant be admin.", new[] { "Title", "Name" }));
}
return results;
}
}
Person/Index.cshtml
#model Person
#{
ViewBag.Title = "Person";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("Index", "Person", FormMethod.Post, new { id = "CreatePersonForm" }))
{
#Html.AntiForgeryToken()
#Html.DisplayNameFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
<input type="submit" value="Save" class="btn btn-default" />
}
#Html.Partial("~/Views/Dog/Index.cshtml", new Dog())
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
PersonController.cs
public class PersonController : Controller
{
// GET: Person
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "Name")] Person person)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
return View(person);
}
}
I made a partial view practically the same.
Dog.cs
public partial class Dog : IValidatableObject
{
[Required(ErrorMessage = "name required")]
public string Name { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (Name == "admin") //just example
{
results.Add(new ValidationResult("You cant be admin.", new[] { "Title", "Name" }));
}
return results;
}
}
Dog/Index.cshtml
#model Dog
#{
ViewBag.Title = "Dog Page";
}
#using (Html.BeginForm("Index", "Dog", FormMethod.Post, new { id = "CreateDogForm" }))
{
#Html.AntiForgeryToken()
#Html.DisplayNameFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
<input type="submit" value="Save" class="btn btn-default" />
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
DogController.cs
public class DogController : Controller
{
// GET: Dog
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "Name")] Dog dog)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
return View(dog);
}
}
if you start /Person/Index, if you write admin in the first textbox (person form), after posting (save) the second form (dog form) have the same text and validation than the first form.
The #Html.EditorFor by default uses the property name as the id and name of the generated HTML, and the validation uses these values to set the error messages! You can pass a value to overwrite that default behavior in your partial view as following:
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger", #data_valmsg_for="partial_name" })
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control", #id="partial_name" } })

Model binding doesn't work for complex object

Here's the view I'm going to post:
#model WelcomeViewModel
#using (Html.BeginForm("SignUp", "Member", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post))
{
....
<div class="form-group">
#Html.EditorFor(model => model.SignUp.CompanyName, new {htmlAttributes = new {#class = "form-control" }})
</div>
<div class="form-group">
#Html.EditorFor(model => model.SignUp.RegisteredNo, new {htmlAttributes = new {#class = "form-control" } })
</div>
....
<button type="submit" name="signup" class="btn">Register</button>
}
ViewModel:
public class WelcomeViewModel
{
public SignInViewModel LogOn { get; set; }
public SignUpViewModel SignUp { get; set; }
}
Action method:
[HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public virtual async Task<ActionResult> SignUp(SignUpViewModel model)
{
if (!ModelState.IsValid)
return View("SignIn", new WelcomeViewModel { SignUp = model });
// other code
return View();
}
When I post the data, the model gets null. I know the inputs will be generated like:
<input id="SignUp_CompanyName" name="SignUp.CompanyName">
But the model binder accepts this:
<input id="SignUp_CompanyName" name="CompanyName">
Now I want to know how can I remove that prefix? I know I can explicitly add name for each input:
#Html.TextBoxFor(model => model.SignUp.CompanyName, new { Name = "CompanyName" })
but I want to do it in a strongly type way.
Perhaps the easiest way would be to apply the [Bind] attribute with its Prefix set to "SignUp":
public async Task<ActionResult> SignUp([Bind(Prefix="SignUp")] SignUpViewModel model)
See MSDN

How to add existing view with own controller as a partial view to another view?

i am using visual studio 2013 community edition.In my ASP.NET MVC application I had a view with model and controller as follows. I created the view from scafolding. It is database first approach. I added edmx file, then added controller with scafolding, which created views.
Model:
using System;
using System.Collections.Generic;
public partial class request
{
public request()
{
this.stocks = new HashSet<stock>();
this.transactions = new HashSet<transaction>();
}
public int request_id { get; set; }
public Nullable<long> request_no { get; set; }
public int request_status { get; set; }
public virtual ICollection<stock> stocks { get; set; }
public virtual ICollection<transaction> transactions { get; set; }
}
View:
#model MaterialManagement2.request
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>request</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.request_no, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.request_no, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.request_no, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.request_status, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.request_status, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.request_status, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Controller:
namespace MaterialManagement2.Controllers
{
public class requestsController : Controller
{
private MaterialManagement2Entities db = new MaterialManagement2Entities();
// GET: requests
public ActionResult Index()
{
return View(db.requests.ToList());
}
// GET: requests/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
request request = db.requests.Find(id);
if (request == null)
{
return HttpNotFound();
}
return View(request);
}
// GET: requests/Create
public ActionResult Create()
{
return View();
}
// POST: requests/Create
// 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 ActionResult Create([Bind(Include = "request_id,request_no,request_status")] request request)
{
if (ModelState.IsValid)
{
db.requests.Add(request);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(request);
}
// GET: requests/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
request request = db.requests.Find(id);
if (request == null)
{
return HttpNotFound();
}
return View(request);
}
// POST: requests/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 ActionResult Edit([Bind(Include = "request_id,request_no,request_status")] request request)
{
if (ModelState.IsValid)
{
db.Entry(request).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(request);
}
// GET: requests/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
request request = db.requests.Find(id);
if (request == null)
{
return HttpNotFound();
}
return View(request);
}
// POST: requests/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
request request = db.requests.Find(id);
db.requests.Remove(request);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
Now i need this view to be rendered as partial view with another view who has separate controller and model. I have created another view(partial) with above mentioned model with create template. This partial view called "_request" resides in shared folder. Now i used
#Html.Partial("_request", new MaterialManagement2.request())
in my another view so i can use them both is single view but the create button of the new view with partial view does not work. How do i make that button work?
Below is a screen shot of the app. The view renders correctly and looks exactly like i wanted. The button does not works though.
You have to specify the first controller name in this line :
#using (Html.BeginForm("Create", "requestsController"))
and you should drop this in the submit button:
value="Create"

MVC4 Nested ViewModel Binding does not work

i have an outerViewModel and inside of it two ViewModels,
when i try to bind innermodel i get null for all the properties...
here is the code:
**Models.cs**
public class OuterModel
{
public FirstInnerModel firstInnerModel;
public SecondInnerModel secondInnerModel;
}
public class FirstInnerModel
{
public string Title;
}
public class SecondInnerModel
{
public string Title;
}
Index.cshtml
#using (Html.BeginForm("ActivateFirst", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => model.firstInnerModel.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.firstInnerModel.Title)
#Html.ValidationMessageFor(model => model.firstInnerModel.Title)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
HomeController.cs
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
var model = new OuterModel()
{
firstInnerModel = new FirstInnerModel(),
secondInnerModel = new SecondInnerModel()
};
return View(model);
}
[HttpPost]
public void ActivateFirst(FirstInnerModel ggg)
{
}
ggg.Title returns null...
Anyone? help!
When you submit the form it will be posting the OuterModel to the controller so you would need to do something like:
[HttpPost]
public void ActivateFirst(OuterModel ggg)
{
var whatever = ggg.FirstInnerModel.Title;
}

Resources