I need two models in my view, so I created ViewModel that combines them. That is on getting method. But on the Post, my input type=submit is returning ViewModel, but values of properties inside are null. Any advice?
These are my actions for Get and Post
[HttpGet]
public ActionResult Report(int id)
{
using (SkolskaBibliotekaDBModel db = new SkolskaBibliotekaDBModel())
{
ReportZaPrikaz RZP = new ReportZaPrikaz();
var komentar = db.Komentar.FirstOrDefault(x => x.KomentarID == id);
RZP.kzp = new KomentarZaPrikaz()
{
KomentarID = komentar.KomentarID,
KomentarText = komentar.KomentarText,
KorisnikID = komentar.KorisnikID,
Reported = komentar.Reported,
VremeKomentara = komentar.VremeKomentara,
Ime = komentar.Korisnik.Ime,
Prezime = komentar.Korisnik.Prezime,
PicPath = komentar.Korisnik.PicPath,
VoteUp = komentar.VoteUp,
VoteDown = komentar.VoteDown
};
RZP.report = db.PrijavaKomentara.Create();
return View("Report",RZP);
}
}
[HttpPost]
public void Report(ReportZaPrikaz RZP)
{
using (SkolskaBibliotekaDBModel db = new SkolskaBibliotekaDBModel())
{
db.PrijavaKomentara.Add(RZP.report);
db.SaveChanges();
}
}
this is my viewmodel:
namespace SkolskaBiblioteka.ViewModels
{
public class ReportZaPrikaz
{
public KomentarZaPrikaz kzp;
public PrijavaKomentara report;
}
}
and this is my view:
#model SkolskaBiblioteka.ViewModels.ReportZaPrikaz
#using SkolskaBiblioteka.ViewModels
#{
ViewBag.Title = "Пријава коментара";
}
<div class="container">
#Html.Partial("_Komentar", Model.kzp)
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>PrijavaKomentara</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#{
Model.report.KorisnikId = ((KorisnikZaPrikaz)Session["Korisnik"]).KorisnikId;
}
#Html.HiddenFor(x => x.report.KorisnikId)
#{
Model.report.KomentarId = Model.kzp.KomentarID;
}
#Html.HiddenFor(x => x.report.KomentarId)
#{
Model.report.VremePrijave = DateTime.Now;
}
#Html.HiddenFor(x => x.report.VremePrijave)
<div class="form-group">
#Html.LabelFor(model => model.report.Tekst, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.report.Tekst, new { htmlAttributes = new { #class = "form-control" } })
</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>
These conditions show up when I hit submit button.
The default model-binder would not bind fields. Try to use properties instead:
public class ReportZaPrikaz
{
public KomentarZaPrikaz kzp { get; set; }
public PrijavaKomentara report { get; set; }
}
Related
I have two objects that I pass to View, and than back to controller.
public class Category1Dictionary
{
public int Id { get; set; }
public IList<Category2Dictionary> SubCategories {get; set;}
public string UserName { get; set; }
}
public class Category2Dictionary
{
public int Id { get; set; }
public string Category2Item { get; set; }
}
My view looks like this:
#model Budget.Models.Category1Dictionary
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.UserName)
#Html.HiddenFor(m => m.SubCategories)
#Html.LabelFor(m => m.Category1Item, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(m => m.Category1Item, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m.Category1Item, "", new { #class = "text-danger" })
</div>
#for (int i = 0; i < Model.SubCategories.Count; i++)
{
<div class="col-md-10" style="padding-left:40px">
#Html.EditorFor(m => m.SubCategories[i].Category2Item, new { htmlAttributes = new { #class = "form-control" } })
#Html.HiddenFor(m => m.SubCategories[i].Id, new { htmlAttributes = new { #class = "form-control" } })
</div>
}
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
And action in the controller:
[HttpPost]
public ActionResult Edit(Category1Dictionary category1Dictionary)
{
return View(category1Dictionary);
}
With this layout in action I get object Category1Dictionary with all items but SubCategories is NULL. I saw some similar posts but I have spent 6 hours in total on this and still can make it work...
Get Method:
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Category1Dictionary category1Dictionary = db.Category1Dictionaries.Include(p=>p.SubCategories).First(c=>c.Id == id);
if (category1Dictionary == null)
{
return HttpNotFound();
}
return View(category1Dictionary);
}
EDIT:
input field generated by EditorFor
Post message from fiddler:
I want to create a register site that will take user information and save it to a text file (it needs to be a text file, not a database).
This is my controller:
public IActionResult Register()
{
UserRLModel cos = new UserRLModel();
string root = #"D:\dotNET\RiderProjects\WebApplication3\UserInfo\Users.txt";
using (StreamWriter sw = new StreamWriter(root))
{
sw.WriteLine(cos.Name);
};
return View(cos.Name);
}
This is my model:
using System;
namespace WebApplication3.Models
{
public class UserRLModel
{
public string Name { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public DateTime Date { get; set; }
}
}
This is my view:
#model WebApplication3.Models.UserRLModel
#{
ViewBag.Title = "Register";
Layout = "_Layout";
}
#using (Html.BeginForm("Register", "UserRL", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Wprowadź dane użytkownika</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => Model.Name, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(model => Model.Name, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Dodaj" class="btn btn-default" />
#Html.ActionLink("Anuluj", "Index", null, htmlAttributes: new { #class = "btn btn-danger" })
</div>
</div>
}
Found a solution, but a very ugly one, see at the bottom, can someone improve that solution?
So I have a dummy Model like this one
public class TestModel
{
public int TestModelID { get; set; }
public string Name { get; set; }
}
And another one like this one
public class Collector
{
public int CollectorID { get; set; }
public string CollectorString { get; set; }
public ICollection<TestModel> MyList { get; set; }
}
I would like to (in simple CRUD style) create a new object Collector and populate (later with dynamic addition of new fields, for now only one) the ICollection.
This is my view
#model TestApplication.Models.Collector
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Collector</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.CollectorString, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CollectorString, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CollectorString, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => Model.MyList.ToList()[0].Name)
<div class="col-md-10">
#Html.EditorFor(model => model.MyList.ToList()[0].Name, new { htmlAttributes = new { #class = "form-control" } })
</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>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
And the controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Collector collector)
{
if (ModelState.IsValid)
{
db.Collectors.Add(collector);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(collector);
}
This is the resulting HTML code (only relevant part I hope)
<div class="form-group">
<label for="">Name</label>
<div class="col-md-10">
<input class="form-control text-box single-line" name="[0].Name" type="text" value="" />
</div>
</div>
However, the MyList in the controller when creating is always null, why? (Yes I know Haackeds Blog entry, still can't figure out why it doesn't work)
So the problem is, that this line
#Html.EditorFor(model => model.MyList.ToList()[0].Name, new { htmlAttributes = new { #class = "form-control" } })
although very recommended to use in MVC, generates this here
<input class="form-control text-box single-line" name="[0].Name" type="text" value="" />
which is obviously not working. How do I get razor to change [0].Name to MyList[0].Name?
**Update: **
So I found a solution, if a hard-code this here
<input class="form-control text-box single-line" name="MyList[0].Name" type="text" value="" />
The controller understands it and I don't get null. How to solve it using razor?
ICollection<T> does not have an indexer. IList<T> does
public class Collector {
public Collector() {
MyList = new List<TestModel>();
}
public int CollectorID { get; set; }
public string CollectorString { get; set; }
public IList<TestModel> MyList { get; set; }
}
This would allow
#Html.EditorFor(model => model.MyList[0].Name, new { htmlAttributes = new { #class = "form-control" } })
in the view to generate the desired markup
<input class="form-control text-box single-line" name="MyList[0].Name" type="text" value="" />
Which can also be used in a loop for multiple items
<div class="form-group">
#for(int i = 0; i < Model.MyList.Count; i++) {
#Html.LabelFor(model => Model.MyList[i].Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.MyList.ToList()[i].Name, new { htmlAttributes = new { #class = "form-control" } })
</div>
}
</div>
When returning the model from the controllers initial call, just make sure that calls to the model's collection index has an item.
[HttpGet]
public ActionResult Create() {
var model = new Collector();
model.MyList.Add(new TestModel());
return View(model);
}
Try changing your collector class to include initialization.
public class Collector
{
public Collector()
{
set MyList = new Collection<TestModel>();
}
public int CollectorID { get; set; }
public string CollectorString { get; set; }
public ICollection<TestModel> MyList { get; set; }
}
You can do by this using razor syntax
<div class="form-group">
#for(int i = 0; i < Model.MyList.Count; i++) {
#Html.LabelFor(model => Model.MyList[i].Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.MyList[i].Name, new { htmlAttributes = new { #class = "form-control" } })
</div>
}
</div>
I've come up with problem, when trying to fill data to my model. I have an "Resource" entity, which can have no-to-many "attributes". I have templates set up, which holds names for those attributes. When Resource is created, user chooses on of templates, then Attributes are created(empty) and program generates form for those attributes.
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(Model => Model.Resource)
#for (int i = 0; i < Model.Attributes.Count(); i++)
{
<div class="form-group">
#*Html.LabelFor(d => d.Attributes.ToArray()[i].Name, htmlAttributes: new { #class = "control-label col-md-2" })*#
<h4>#Html.Raw(Model.Attributes.ToList()[i].Name)</h4>
<div class="col-md-10">
#Html.TextBoxFor(Model => Model.Attributes.ToList()[i].Value, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(Model => Model.Attributes.ToList()[i].Value, "", new { #class = "text-danger" })
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
This form uses this View model:
public class ResourceAttributesViewModel
{
public virtual Resource Resource { get; set; }
public virtual ICollection<_Attribute> Attributes { get; set; }
}
problem is that when i hit "submit" button, it gives me view model with null Resource and Attributes properties
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Fill(ResourceAttributesViewModel AttributeSet)
{
if(ModelState.IsValid)
{
foreach (var attr in AttributeSet.Attributes)
{
db.Entry(attr).State = EntityState.Modified;
db.SaveChanges();
}
return RedirectToAction("Index");
}
return View(AttributeSet);
}
if it helps, there is POST string sent by browser
__RequestVerificationToken=XoVM9h_3njX5x2m35b_vKKHY3m5UDaYm9_2ZMfNkglouqHJCSw2NO56Tv2Sb3kXy8qC8XBLXawoQv0ft0xc-LxYmQGfi4EAqroq2b63Wb9Q1&Resource=System.Data.Entity.DynamicProxies.Resource_7639327FA0332BEBC7FB6836F70C3D62C3D744D76F2C3F8DDFCE679AA8CA31DC&%5B0%5D.Value=100&%5B1%5D.Value=200
I would like to create multiple objects from one view (e.g. 5 Items in one View). My View looks like:
#model IEnumerable<WerehouseProject.Models.ItemRentList>
#{
ViewBag.Title = "Create";
}
<h2>Dodaj</h2>
#{Html.RenderPartial("_PartialError");}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#for (int i = 0; i < Model.Count; i++)
{
<div class="form-group">
#Html.LabelFor(model => model[i].ItemID, "Przedmiot", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("ItemID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model[i].ItemID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model[i].Ilosc, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model[i].Ilosc, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model[i].Ilosc, "", new { #class = "text-danger" })
</div>
</div>
#Html.HiddenFor(model => model[i].ItemRentID)
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Dodaj" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
<button type="button" name="ID" value="#ViewBag.ItemRentID" onclick="location.href='#Url.Action("Details", "ItemRents", new { ID = #ViewBag.ItemRentID })'">Wróć</button>
</div>
Create GET method:
public ActionResult Create(int ID, int quantity)
{
var model = new List<ItemRentList>();
for(int i = 0; i < quantity; i++)
{
model.Add(new ItemRentList { ItemRentID = ID } );
}
ViewBag.ItemRentID = ID;
ViewBag.ItemID = new SelectList(db.Items, "ItemID", "Nazwa");
return View(model);
}
And my POST Create method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "IRListID,Ilosc,ItemRentID,ItemID")] IEnumerable<ItemRentList> itemRentList)
{
...
}
My itemRentList.ItemID is always 0, can someone tell me how should I code View or Controller to get list of ItemRentList in POST Method?
This is my Model of ItemRentList
public class ItemRentList
{
[Key]
public int IRListID { get; set; }
[Display(Name = "Ilość")]
public double Ilosc { get; set; }
[Display(Name = "Nazwa przedmiotu")]
public int ItemID { get; set; }
public virtual Item Item { get; set; }
public int ItemRentID { get; set; }
public virtual ItemShopping ItemRent { get; set; }
}
If I edit View and Controller to create only one and always one ItemRentList (not one object in list, just simple ItemRentList) then the value of ItemID is correctly bind to object, the problem is when I want to create a list of ItemRentList, then ItemID is always 0, no mater what I choose from DropDownList.