ViewBag does not work - asp.net-mvc

I'm trying to create an MVC site and I have some trubble with the ViewBag.
It's like it can't contain elements.
I'm using MVC 4.
This is my function in the controller:
public ActionResult Create()
{
ViewBag.DiscID = new SelectList(entities.Disc, "ID", "ID");
ViewBag.StarID = new SelectList(entities.Star, "ID", "Name");
ViewBag.Num = 7;
return View();
}
This is my Create view:
#model Movies.Models.StarAndRole
#{
ViewBag.Title = "Create";
int num;
int.TryParse(ViewBag.Num, out num);
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>StarAndRole</h4>
<h3>num is = #num</h3>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.StarID, "Star", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("StarID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.StarID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DiscID, "Disc Num", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("DiscID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.DiscID, "", 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>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
It appears to me that everything I put in the ViewBag is NULL.
What am i doing wrong?
Thank you in advance!

Int.TryParse method accepts a string (representation of the int value). But you are passing a dynamic type to the method. It should give you an error about that.
If you know you are setting an Int value from your action method, in your view, you can simply read it like
int num = ViewBag.Num;
A better solution is to add the data you want to pass to the view to your view model.
public class YourCreateVm
{
public string Description { set;get;}
public int Num { set;get;}
public List<SelectListItem> Disks { set;get;}
public int SelectedDiskId { set;get;}
}
and in your GET view
public ActionResult Create()
{
var vm = new YourCreateVm { Num = 7 };
vm.Disks = entities.Disc.Select(s=> new SelectListItem
{ Value=s.ID.ToString(), Text =s.Name}).ToList();
return View(vm);
}
and in your view
#model YourCreateVm
#using(Html.BeginForm())
{
<p>My num is : #Model.Num <p>
#Html.TextBoxFor(s=>s.Description)
#Html.DropDownListFor(s=>s.SelectedDiskId, Model.Disks)
<input type="submit" />
}

Related

ASP.NET MVC - Compound Model

I have a trivial web app with the following model:
public class SillyModel
{
public SillyModel()
{ Id = Guid.NewGuid(); Children = new List<SillyModel>(); }
[Key]
public virtual Guid Id { get; set; }
public virtual string Value { get; set; }
public virtual List<SillyModel> Children { get; set; }
}
}
I have an Edit View of:
#model WebApplication1.Models.SillyModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.Partial("EditPartial", Model)
<div class="form-horizontal">
<h4>SillyModel</h4>
<hr />
<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>
}
With the Partial:
#model WebApplication1.Models.SillyModel
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Value, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Value, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Value, "", new { #class = "text-danger" })
</div>
#foreach (var item in Model.Children)
{
#Html.Partial("EditPartial", item)
}
</div>
The rendering is Fine! But for the life of me (well at least 3 days of struggle) I can not get it so that the returned model is properly bound! [No children are returned]
I am at my wits end.
You have to re-structure the way you are coding a little
In the Views/YourController, add a folder called EditorTemplates if not yet exists and add a view named SillyModel and copy the code from EditPartial to this new view
You change the foreach to for loop to decorate the controls with index
The code
~/Views/YourController/EditorTemplates/SillyModel.cshtml
#model WebApplication1.Models.SillyModel
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Value, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Value, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Value, "", new { #class = "text-danger" })
</div>
#for (var index=0; index<Model.Children.Count;index++)
{
#Html.EditorFor(model=>Model.Children[index])
}
</div>
~/Views/YourController/Edit
instead of #Html.Partial("EditPartial", Model), use #Html.EditorFor(m=>m)
Explanation
By adding the EditorTemplates/SillyModel , now you can call #Html.EditorFor(model=>Model.Children[index]) and your custom editor will be rendered
You need to use indexed controls in order Binding to the Model succeeded
Hope this will help you

How could I store an "id" (urlparameter.optional) through a textboxfor()

I'm trying to store an id from url in a textboxfor(), but I don't obtain the result. When i put "id" in a textbox, I obtain the correct result. Instead, in a textboxfor(model=> model.IDCantiere,new{#value="id"}). It doesn't run; please help me, I've attached the code fragments:
Controller.cs
// GET: CantiereOres/Create
public ActionResult Create(int id)
{
ViewBag.id = id;
//ViewBag.IdCantiere = new SelectList(db.Cantieres, "IdCantiere", "IdCantiere");
ViewBag.IdPersona = new SelectList(db.CantierePersones, "IdPersona", "Persona");
return View();
}
// POST: CantiereOres/Create
// Per proteggere da attacchi di overposting, abilitare le proprietà a cui eseguire il binding.
// Per ulteriori dettagli, vedere https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "IdOre,IdCantiere,IdPersona,Ore,datetime")] CantiereOre cantiereOre)
{
if (ModelState.IsValid)
{
db.CantiereOres.Add(cantiereOre);
db.SaveChanges();
return RedirectToAction("details", "Cantieres", new { id = cantiereOre.IdCantiere });
}
ViewBag.IdCantiere = new SelectList(db.Cantieres, "IdCantiere", "IdCantiere", cantiereOre.IdCantiere);
ViewBag.IdPersona = new SelectList(db.CantierePersones, "IdPersona", "Persona", cantiereOre.IdPersona);
return View(cantiereOre);
}
View.cshtml:
#model archeagroup.Models.CantiereOre
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.TextBox("id")
<div class="form-horizontal">
<h4>CantiereOre</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.IdCantiere, "IdCantiere", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model=>model.IdCantiere, new {#value ="id", #class = "form-control" } )
#*#Html.EditorFor(model => model.IdCantiere, new { htmlAttributes = new { #class = "form-control" } })*#
#Html.ValidationMessageFor(model => model.IdCantiere, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.IdPersona, "IdPersona", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("IdPersona", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.IdPersona, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Ore, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Ore, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Ore, "", 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", "Details", "Cantieres")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
If you want 2441 to be in this textbox right?
#Html.TextBoxFor(model=>model.IdCantiere, new {#value ="id", #class = "form-control" } )
Then in your controller, instead of this:
ViewBag.id = id;
Do this:
Model.IdCantiere = id;
And change your textbox to:
#Html.TextBoxFor(model=>model.IdCantiere, new { #class = "form-control" })
Which will put (from your screenshot) 2441 in the textbox you indicated.
EDIT: So you need to return an instance of archeagroup.Models.CantiereOre from your controller Create method:
public ActionResult Create(int id)
{
var model = new acheagroup.Models.CantiereOre();
model.IdCantiere = id;
model... //other assignments - use this instead of ViewBag
return View(model);
}
With this the above code will work since it will be fed from the model.

How do I create a new db entry and populate the foreign key based on autocomplete selection of that foreign key object?

I have a ASP.NET MVC application i am developing, and have a repair model that includes a foreign key to a serial number model. In my view i have a typeahead jquery doing a autocomplete search through database to allow the user to select a serial number already in the database. I then have fields to fill out new information that will populate the repair table. I cannot seem to get the selected SerialNumber.Id to populate in the Repair.SerialNumber_Id from the selected serial number.
my view:
#model EntityTestApp.Models.Repair
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Repair</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.RepairDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.RepairDate, new {htmlAttributes = new { #class = "form-control", #Value = DateTime.Now.ToShortDateString() } })
#Html.ValidationMessageFor(model => model.RepairDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SerialNumber, htmlAttributes: new { #class = "control-label col-md-2"})
<div class="col-md-10">
<input id="serialNumberTextBox" type="text" value="" class="form-control" />
#Html.ValidationMessageFor(model => model.SerialNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CustomerComplaint, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.CustomerComplaint, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerComplaint, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.IssueFound, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.IssueFound, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.IssueFound, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.RepairActionTaken, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.RepairActionTaken, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.RepairActionTaken, "", 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>
#section scripts{
<script>
$(document).ready(function () {
var SerialNumbers = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('Number'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/api/serialnumbers?query=%QUERY',
wildcard: '%QUERY'
}
});
$('#serialNumberTextBox').typeahead({
highlight: true
}, {
name: 'SerialNumbers',
display: 'Number',
source: SerialNumbers
}).on("typeahead:select", function (e, SerialNumber) {
//alert(serialNumberTextBox.value)
});
});
</script>
}
my Controller action to create a new repair entry:
// GET: Repairs/Create
public ActionResult Create()
{
return View();
}
// POST: Repairs/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(Repair repair)
{
if (ModelState.IsValid)
{
db.Repairs.Add(repair);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(repair);
}
And my repair model:
public class Repair
{
public int Id { get; set; }
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime RepairDate { get; set; }
public SerialNumber SerialNumber { get; set; }
public string CustomerComplaint { get; set; }
public string IssueFound { get; set; }
public string RepairActionTaken { get; set; }
}
Ok, several hair pulling nights later i figured it out. I had to add a hidden field to hold the value for the foreign key and assign that value based on the 'typeahead' javascript autocomplete selection with "document.getElementById('').
Works like a charm. Thanks for the help!
-Chris

Submitting image path to the database

Im looking enter in some data and then save it into a DB table,The other fields work just fine when submitted to the database. but when it comes to saving the image path it gives null. Ive searched online and everything i find is out of date and doesn't work for the most part.
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Insert([Bind(Include ="id, ImageName, ImageSize")] ImageInfo info, HttpPostedFileBase ImagePath)
{
if(ModelState.IsValid)
{
if (ImagePath != null)
{
var filename = Path.GetFileName(ImagePath.FileName);
var path = Path.Combine(Server.MapPath("~/Uploads"), filename);
ImagePath.SaveAs(path);
ImagePath.SaveAs(HttpContext.Server.MapPath("~/Uploads") + ImagePath.FileName);
info.ImagePath = ImagePath.FileName;
}
db.ImageInfoes.Add(info);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(info);
}
View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Image</legend>
<div class="editor-label">
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.id, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ImageName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ImageName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ImageName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ImageSize, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ImageSize, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ImageSize, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#*#Html.TextBoxFor(model => model.ImagePath, new { type = "file" })*#
<input type="file" name="ImagePath" id="ImagePath" />
#Html.ValidationMessageFor(model => model.ImagePath, "", new { #class = "text-danger" })
</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>
</fieldset>
DB
Any help would be great!.
You want to add enctype="multipart/form-data" to form tag.
I personally like to rename HttpPostedFileBase parameter name to file not to confuse with ImageInfo.ImagePath.
View
#using (Html.BeginForm("Index","Start",FormMethod.Post,new {enctype="multipart/form-data"}))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Image</legend>
<div class="editor-label">
<div class="form-horizontal">
<hr/>
...
<div class="form-group">
#* Notice name and id are named as file *#
<input type="file" name="file" id="file"/>
#Html.ValidationMessageFor(model => model.ImagePath, "", new {#class = "text-danger"})
</div>
...
</div>
</div>
</fieldset>
}
Model
public class ImageInfo
{
public string Id { get; set; }
public string ImageName { get; set; }
public string ImageSize { get; set; }
public string ImagePath { get; set; }
}
Action Method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "id, ImageName, ImageSize")] ImageInfo info,
HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
if (file != null)
{
file.SaveAs(Server.MapPath("~/Uploads") + file.FileName);
info.ImagePath = file.FileName;
}
/*db.ImageInfoes.Add(info);
db.SaveChanges();*/
return RedirectToAction("Index");
}
return View(info);
}

The current request for action ... on controller type ... is ambiguous between the following action methods

When I run the application on browser with ~/Person/New the result is
The current request for action 'New' on controller type 'PersonController' is ambiguous between the following action method System.Web.Mvc.ActionResult New() on type PersonMVC.Controllers.PersonController System.Web.Mvc.ActionResult New(PersonMVC.Models.Person) on type PersonMVC.Controllers.PersonController
Model
namespace PersonMVC.Models
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
[Required]
[DisplayName("Social Sequrity Number")]
[MinLength(11, ErrorMessage ="Social Security Number Must be 11 digit.")]
[MaxLength(11, ErrorMessage ="")]
public string SocialSecurityNumber { get; set; }
[DisplayFormat(DataFormatString ="{0:dd.MM.yyyy}")]
public DateTime BirthDay { get; set; }
}
}
-View
#model PersonMVC.Models.Person
#{
ViewBag.Title = "New";
}
<h2>New</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Person</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LastName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.LastName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LastName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SocialSecurityNumber, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.SocialSecurityNumber, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SocialSecurityNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.BirthDay, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.BirthDay, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.BirthDay, "", 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>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Controller
namespace PersonMVC.Controllers
{
public class PersonController : Controller
{
public ActionResult New()
{
return View();
}
[HttpPost]
public ActionResult New(Person newPerson)
{
bool isTrue = ModelState.IsValid;
return View();
}
}
}
you need to decorate the method with appropriate http verb. Otherwise it will be considered as HttpPost usually.
i think adding [HttpPost] to the controller method would solve the problem

Resources