I have created two models, Student and School and a composite model, StudentSchool. I am using two different partiel views to create one the Student and the other the School. However, in my controller, I am getting null values for both the Student and School. Below is my code:
Student:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace LayoutProject.Models
{
public class Student
{
public int studentID { get; set; }
public String firstname { get; set; }
public String lastname { get; set; }
public int year { get; set; }
}
}
School:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace LayoutProject.Models
{
public class School
{
public int schoolID { get; set; }
public String name { get; set; }
public String add { get; set; }
}
}
Composite model, StudentSchool:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace LayoutProject.Models
{
public class StudentSchool
{
public Student student { get; set; }
public School school { get; set; }
}
}
PartielViewStudent:
#model LayoutProject.Models.Student
<h4>Student</h4>
#Html.LabelFor(d=>d.studentID) : #Html.TextBoxFor(d=>d.studentID)
<br />
#Html.LabelFor(d=>d.firstname) : #Html.TextBoxFor(d=>d.firstname)
<br />
#Html.LabelFor(d=>d.lastname) : #Html.TextBoxFor(d=>d.lastname)
<br />
#Html.LabelFor(d=>d.year) : #Html.TextBoxFor(d=>d.year)
<br />
PartialViewSchool:
#model LayoutProject.Models.School
<h4>School</h4>
#Html.LabelFor(d=>d.schoolID) : #Html.TextBoxFor(d=>d.schoolID)
<br />
#Html.LabelFor(d=>d.name) : #Html.TextBoxFor(d=>d.name)
<br />
#Html.LabelFor(d=>d.add) : #Html.TextBoxFor(d=>d.add)
<br />
The View:
#{
ViewBag.Title = "StudentSchool";
}
<h2>StudentSchool</h2>
<form action="/Home/CreateStudentSchools" method="post">
#using (Html.BeginForm())
{
#Html.Partial("_PartialViewStudent")
#Html.Partial("_PartialViewSchool")
}
<input id="createSD" type="submit" value="Submit"/>
</form>
The controller:
[HttpPost]
public ActionResult CreateStudentSchools(StudentSchool ss)
{
return View("CreateStudentSchool");
}
Any idea what I might be missing here? Been with this since 4 days.
Move you 'partial' views into the /Views/Shared/EditorTemplates folder and rename them to Student.cshtml and School.cshtml respectively (to match the names of the classes). Then in the main view use
#using (Html.BeginForm())
{
#Html.EditorFor(m => m.Student)
#Html.EditorFor(m => m.School)
}
The inputs your generating will now contain the correct name attribute for binding to your view model, for example name="Student.studentID" instead of the current name="studentID"
Note: As an alternative, you can pass the HtmlFieldPrefix to a Partial as indicated in this answer.
Its not a good idea if you provide the two partial views with different models and combining in one model.You have to pass the reference of the composite model to your partial views.
#using (Html.BeginForm())
{
#Html.Partial("_PartialViewStudent",Model.student)
#Html.Partial("_PartialViewSchool",Model.school)
}
This will work fine as with this approach as everything in the form is strictly typed.
In partial views also you have to specify models as
#model LayoutProject.Models.StudentSchool.student
#model LayoutProject.Models.StudentSchool.school
I am not sure about your [httpget] create method but it should be like
[HttpGet]
public ActionResult CreateStudentSchools(StudentSchool ss)
{
var studentsc=new StudentSchool()
{
student=new Student(),
school=new School()
}
return View("CreateStudentSchool",studentsc);
}
Related
I am using Ajax and rendering a Partial View. The data is being passed fine into the controller. There is nothing null. However for some reason in my Partial View the OwnerUser and the AssignToUser are null but the Hours and the Body are coming through with the correct information. I have this same implementation in another part of my project and it's working fine, even with the user coming in. I can't figure out why in this Partial View the ApplicationUser data is null.
[HttpPost]
public ActionResult ChatAjax([Bind(Include = "Body")] Chat chat, string ChatData)
{
chat.Created = DateTimeOffset.Now;
var user = User.Identity.GetUserId();
chat.OwnerUserId = user;
chat.AssignToUserId = ChatData;
db.Chats.Add(chat);
db.SaveChanges();
if (Request.IsAjaxRequest())
{
return PartialView("_Chats", chat);
}
return RedirectToAction("LandingPage", "Admin");
}
This is my Partial View:
#model BugTracker.Models.Chat
#{
var day = DateTimeOffset.Now.Offset.Hours;
}
<div class="direct-chat-msg">
<div class="direct-chat-info clearfix">
<span class="direct-chat-name pull-left">
#Model.OwnerUser.FirstName</span>
<span class="direct-chat-timestamp pull-right">
#Model.Created.AddHours(day)
</span>
<br />
<span class="direct-chat-timestamp pull-right">Sent to
#Model.AssignToUser.DisplayName
</span>
</div>
<!-- /.direct-chat-info -->
<div class="direct-chat-text" style="background:lightblue">
#Model.Body
</div>
<!-- /.direct-chat-text -->
</div>
Here is my Chat Model
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace BugTracker.Models
{
public class Chat
{
public int Id { get; set; }
public string Body { get; set; }
public DateTimeOffset Created { get; set; }
[Display(Name = "Owner")]
public string OwnerUserId { get; set; }
[Display(Name = "Assign")]
public string AssignToUserId { get; set; }
public virtual ApplicationUser OwnerUser { get; set; }
public virtual ApplicationUser AssignToUser { get; set; }
}
}
I am creating a MVC project. I try to add a Edit page of a model. Here is my model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;
namespace SportsStore.Domain.Entities
{
public class Product
{
[HiddenInput(DisplayValue = false)]
public int ProductID { get; set; }
public string ProductName { get; set; }
[DataType(DataType.MultilineText)]
public string ProductDescription { get; set; }
public decimal ProductPrice { get; set; }
public string ProductCategory { get; set; }
}
}
As you can see, ProductId and ProductCategory property has proper metadata information. As per these metadata information, I shall not see ProductID in edit page and see ProductCategory in multi line.
Here is my edit page. (edit.cshtml)
#model SportsStore.Domain.Entities.Product
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<h1>Edit #Model.ProductName</h1>
#using (Html.BeginForm())
{
#Html.EditorForModel()
<input type="submit" value="Save" />
#Html.ActionLink("Cancel and return to List", "Index")
}
However, I can not see the page in accordance. Here is the view of my page:
As you can see, ProductID has appeared on the page, and description is not multine. What I am missing?
Thanks in advance.
Thanks for person whom interest with the topic.
The problem is that there are multiple Entity Framework classes in the project mistakenly. Each entity framework has its own Product class. I unite them.
Until some days ago it was quite easy to manage model binding in my application. I had a view model, called PersonOfferDTO, containing a collection of PersonProductOfferDTO. (yes, I'm using the DTO as a view model because a view model in this case would be equal to the DTO). Here below a simplified version of PersonOfferDTO
public class PersonOfferDTO
{
[DataMember]
public Guid PersonOfferId { get; private set; }
[DataMember]
public ICollection<PersonProductOfferDTO> Offers { get; set; }
}
And here below a simplified version of PersonProductOfferDTO
public class PersonProductOfferDTO
{
[DataMember]
public Guid PersonProductOfferId { get; private set; }
[DataMember]
public Guid PersonOfferId { get; set; }
[DataMember]
public int Quantity { get; set; }
[DataMember]
public decimal UnitPrice { get; set; }
}
I was able to populate the ICollection thanks to the method shown below (HTML code).
<form method="POST" action="/Offers/AddNewPersonOffer">
<input name="PersonProductOffers.Index" value="myKey1" hidden>
<input name="PersonProductOffers[myKey1].Quantity">
<input name="PersonProductOffers[myKey1].UnitPrice">
<input name="PersonProductOffers.Index" value="myKey2" hidden>
<input name="PersonProductOffers[myKey2].Quantity">
<input name="PersonProductOffers[myKey2].UnitPrice">
</form>
But during the last days I have increased the depth of my objects tree, so now I have the following code.
public class PersonOfferDTO
{
[DataMember]
public Guid PersonOfferId { get; private set; }
[DataMember]
public ICollection<PersonOfferParagraphDTO> Paragraphs { get; set; }
}
[DataContract]
public class PersonOfferParagraphDTO
{
[DataMember]
public Guid PersonOfferParagraphId { get; set; }
[DataMember]
public ICollection<PersonProductOfferDTO> PersonProductOffers { get; set; }
}
As you can see there is now one further level between PersonOfferDTO and PersonProductOfferDTO, and I can't figure out how to perform a "multilevel binding": create a PersonOfferDTO with more PersonOfferParagraphDTO each one containing more PersonProductOfferDTO.
NOTE: I don't want to use an incremental index ([0] , [1], ....)... but a string (["myKey"])
EDIT
By request, I add the controller here below
public ActionResult AddNewPersonOffer(PersonOfferDTO offer)
{
if (!UserHasPermissions())
{
return PartialView("_forbidden");
}
var errors = OffersCRUD.AddNewPersonOffer(offer);
if(errors.Count() == 0)
{
return RedirectToAction("Index");
}
return PartialView("_errors", new ErrorsViewModel(errors));
}
If you want to populate them with your own keys, you can define your collections within your view model as a Dictionary<string, YOURCLASS>it accepts a non-integer index value.
Example view model with Dictionary:
public class ViewModelTest
{
public Dictionary<string, Class1> Values { get; set; }
}
Example class to be used in the dictionary collection:
public class Class1
{
public int MyProperty { get; set; }
public Dictionary <string, Class2> MoreValues { get; set; }
}
public class Class2
{
public int AnotherProperty { get; set; }
}
Here's a form that populates the values:
#using (Html.BeginForm())
{
<input type="text" name="Values[yourkey1].MyProperty" />
<input type="text" name="Values[yourkey1].MoreValues[anotherKey1].AnotherProperty" />
<input type="text" name="Values[yourkey2].MyProperty" />
<input type="text" name="Values[yourkey2].MoreValues[anotherKey2].AnotherProperty" />
<input type="submit" />
}
Instead of writing your input tags yourself, you can use the helper methods and enjoy intellisense, assuming that you have your view model defined within the view with the same structure defined in your action method:
#model ViewModelTest
#using (Html.BeginForm())
{
#Html.TextBoxFor(x => x.Values[yourkey1].MyProperty)
#Html.TextBoxFor(x => x.Values[yourkey1].MoreValues[anotherKey1].AnotherProperty)
#Html.TextBoxFor(x => x.Values[yourkey2].MyProperty)
#Html.TextBoxFor(x => x.Values[yourkey2].MoreValues[anotherKey2].AnotherProperty)
<input type="submit" />
}
You'll have to introduce a view model for this of course and not just get away with using your DTO ;).
PS: A DTO shouldn't be used as a domain model either, it's for transporting information around your layers.
I need to add the following field at my form
<input type="file" class="input-file" />
I create model and describe this field (the last field)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace CorePartners_Site2.Models
{
public class FeedbackForm
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Company { get; set; }
public string AdditionalInformation { get; set; }
public HttpPostedFileBase ProjectInformation { get; set; }
}
}
and create
#Html.TextBox(null, null, new { type="file", #class="input-file" })
but it doesnt work, I get some exception.
What's wrong?
Model
public class FeedbackForm
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Company { get; set; }
public string AdditionalInformation { get; set; }
public HttpPostedFileBase ProjectInformation { get; set; }
}
View
#model FeedbackForm
#Html.TextBox("Name")
#Html.TextBox("Email")
...
#Html.TextBox("ProjectInformation", null, new { type="file", #class="input-file" })
// submit button
My recommended view (strongly - typed)
#model FeedbackForm
#Html.TextBoxFor(model=>model.Name)
#Html.TextBoxFor(model=>model.Email)
...
#Html.TextBoxFor(model=>model.ProjectInformation, null, new { type="file", #class="input-file" })
// submit button
Controller
[HttpPost]
public ActionResult FeedbackForm(FeedbackForm model)
{
// this is your uploaded file
var file = model.ProjectInformation;
...
return View();
}
MVC is using name convention, so if your textbox and model names match, then MVC will bind your inputs to your model.
I think you are getting a null because you have not specified the enctype in your form tag.
#using (Html.BeginForm("ActionMethodName", "Controller", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
}
A working example always help.
Visit http://www.mindstick.com/Articles/cf1e1dd9-fdba-4617-94f0-407223574447/?Upload%20File%20in%20Asp.Net%20Mvc%204
You can use the below syntax
#Html.TextBoxFor(model=>model.Email, new { #type="file", #class="input-file" })
I solved this problem using enctype="multipart/form-data"
#using (Html.BeginForm("SalvarEvidencia", "Evidencia", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
}
There's nothing wrong with just using the input tag directly in your view. You aren't required to use a helper.
<input type="file" class="input-file" />
Just make sure it's within your BeginForm declaration block.
You need to specify the name of the field. If you don't want a name, nor a value, it's better to just include the field as is in your form.
It doesn't make sense to use a helper, if there's nothing dynamic about it.
#using (Html.BeginForm("Action_Name", "Controller_Name",FormMethod.Post))
{
#Html.TextBoxFor(m => m.Email, new {#class = "text_field"})
#Html.ValidationMessageFor(m => m.Email)
}
I have a dictionary being used. And when the "submit" is hit, how do I also pass the dictionary with the values in it to the [HttpPost] controller method?
Currently in the [HttpPost] method, the DummyDictionary is empty, how would I fix this?
Thanks!
MODEL
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using DummyMVC.Objects;
namespace DummyMVC.Models
{
public class TheModel
{
public TheObject Obj { get; set; }
public Dictionary<string, TheObject> DummyDictionary { get; set; }
}
}
CONTROLLER
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using DummyMVC.Models;
using DummyMVC.Objects;
namespace DummyMVC.Controllers
{
public class DummyController : Controller
{
//
// GET: /Dummy/
[HttpGet]
public ActionResult Index()
{
TheModel m = new TheModel();
m.Obj = new Objects.TheObject();
TheObject a_obj = new TheObject();
a_obj.Name = "Joe";
m.DummyDictionary = new Dictionary<string, Objects.TheObject>();
m.DummyDictionary.Add("VT", a_obj);
return View(m);
}
[HttpPost]
public ActionResult Index(TheModel model)
{
// HERE THE DICTIONARY is EMPTY, where all the form values should exist.
string test = "";
return View(model);
}
}
}
VIEW
#model DummyMVC.Models.TheModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.DummyDictionary);
#Html.LabelFor(m => m.DummyDictionary["VT"].Name)
#Html.TextBoxFor(m => m.DummyDictionary["VT"].Value)<br />
#Html.TextBoxFor(m => m.DummyDictionary["VT"].Token, new { #Value = Model.DummyDictionary["VT"].Token, style = "display:none;" })
<input type="submit" class="submit_button" /><br />
}
In the view I am using #Html.HiddenFor(m => m.DummyDictionary) to try to pass the dictionary over to the post method, but the dictionary is just empty. Without this #Html.HiddenFor the Dictionary in the post method is null.
Thank you so much for the assistance I appreciate it!
UPDATE
TheObject
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DummyMVC.Objects
{
public class TheObject
{
public string Name { get; set; }
public string Value { get; set; }
public string Token { get; set; }
}
}
It's not pretty but it works. Remember to include all properties of TheObject, if not visible then hidden so they get posted. Would probably be a good idea to make a helper for this.
#model MvcApplication1.Models.TheModel
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.DummyDictionary["VT"].Name)
#Html.Hidden("DummyDictionary[VT].Name",Model.DummyDictionary["VT"].Name)
#Html.Hidden("DummyDictionary[VT].Token", Model.DummyDictionary["VT"].Token)
#Html.TextBox("DummyDictionary[VT].Value", Model.DummyDictionary["VT"].Value)
<input type="submit" class="submit_button" /><br />
}
Controller
[HttpGet]
public ActionResult Index()
{
TheModel m = new TheModel();
m.Obj = new TheObject();
TheObject a_obj = new TheObject();
a_obj.Name = "Joe";
m.DummyDictionary = new Dictionary<string, TheObject>();
m.DummyDictionary.Add("VT", a_obj);
return View(m);
}
[HttpPost]
public ActionResult Index(TheModel model)
{
// HERE THE DICTIONARY is EMPTY, where all the form values should exist.
string test = "";
return View(model);
}