Submitting form doesn't bring over all the data - c#

I'm trying to add an object using a PartialView inside a popup. It's a simple Rental application for which the data model was generated Model First through Entity Framework. The Controllers and Views have mostly been Scaffolded by EF. The relationship between RentalApplication and RentalObject is 1 to many, meaning a RentalObject always has to have 1 RentalApplication.
My controller looks like this:
// GET: /Calendar/Add/1
// Create a PartialView using a RentalObject as the model.
// Use the provided ID to lock in the RentalApplication.
[HttpGet]
public PartialViewResult Add(int id)
{
return PartialView(
new RentalObject(db.RentalApplicationSet.FirstOrDefault(x => x.Id == id)));
}
// POST: /Calendar/Add
// Save the submitted RentalObject to the db
[HttpPost]
public ActionResult Add(RentalObject rentalobject)
{
if (ModelState.IsValid)
{
try
{
db.RentalObjectSet.Add(rentalobject);
db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
return View();
}
My object looks like this:
public partial class RentalObject
{
public RentalObject()
{
this.Lease = new HashSet<Lease>();
}
public RentalObject(RentalApplication rentapp)
{
this.Lease = new HashSet<Lease>();
RentalApplication = rentapp;
PricePerHour = RentalApplication.DefaultPricePerHour;
Currency = RentalApplication.DefaultCurrency;
}
public int Id { get; set; }
public string Name { get; set; }
public bool IsAvailable { get; set; }
public string Illustration { get; set; }
public string Description { get; set; }
public decimal PricePerHour { get; set; }
public string Currency { get; set; }
public virtual ICollection<Lease> Lease { get; set; }
public virtual RentalApplication RentalApplication { get; set; }
}
So when I'm opening the popup (using #Ajax.ActionLink to GET the first controller Add action) I'm creating a RentalObject WITH a RentalApplication (2nd constructor) to use as the model. This works so far, the popup dialog shows the values PricePerHour and Currency from the RentalApplication.
However, when I submit the form in my PartialView popup everything gets copied over BUT the RentalApplication object. It somehow ends up creating a new RentalObject object using the PricePerHour and Currency from the original RentalApplication, but doesn't include the object itself under the RentalApplication property. My debugger even goes to the first constructor for RentalObject.
So I guess it's having trouble keeping a complex object inside another object when submitted from controller to view (GET) and back to controller (POST). Is this just poor practice on my part? Should I be using a ViewModel?

In the past I've had to use #Html.HiddenFor(m=>m.yourObjectHere) on objects that were not changed in the form to keep them from getting new-ed up again. I did this for every object I didn't use in the form (about 2 or 3).
Hope this helps.

Related

There is no ViewData item of type IEnumerable

I have this ViewModel :
public class FoundViewModel
{
public string Title { get; set; }
public string Description { get; set; }
public int City { get; set; }
public int SubGroup { get; set; }
public string Address { get; set; }
public DateTime FindDate { get; set; }
}
and in View I add two Dropdown and Fill it in actionresult when call get:
var Provinces = _provinces.SelectAll();
ViewBag.Provinces = new SelectList(Provinces,"Id","Title",Provinces.FirstOrDefault().Id);
and in view :
#Html.DropDownList("drpProvince", (SelectList)ViewBag.Provinces, new { #onchange="javascript:getcity(this.value);"})
But when Click on the button and post it , I getting this ERROR:
There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'drpProvince'
I found some similar question on stackover flow but can't know .
What's a Problem ?
thanks.
What happens is that, at the time of the post and reload the page to show the cities according to the selected province, are not generating ViewBag again.
The solution is to initialize the ViewBag again since this is lost after PostBack each call.
Example.
public ActionResult loadCities () {
    /* Logic .... */
    // Here initialize ViewBag again to view the know.
    return View ();
}
This Error Occured when you fill viewbag in Get View and when it post and Model is not Valid , You Returned model to the View And viewBag is null, You should be Fill all Viewbag if Model is not Valid befor return view.
[Httppost]
public ActionResult YouAction(myVm mymodel)
{
if (!ModelState.IsValid)
{
// fill your Viewbags
return View(mymodel);
}
}

Add new Item to list mvc .net

I'm trying to add a new item to an existent list in my MVC application, my problem that's I don't know the correct approach to do it.
I've done this so far:
My Customer code:
Controller
public class CustomerController : Controller
{
public CustomerBusiness customerBusiness { get; set; }
public CustomerController()
{
customerBusiness = new CustomerBusiness();
}
//Some code that makes CRUD and more these methods
[HttpGet]
public ActionResult ViewAllJobOfferts(int id)
{
var cust = customerBusiness.GetById(id);
return View(cust.JobOfferts);
}
public ActionResult CreateJobOffert(int id)
{
var cust = customerBusiness.GetById(id);
return View(cust);
}
/* [HttpPost]
public ActionResult CreateJobOffert(JobOffertModel jobOffert)
{
return View();
}*/
}
I have a relationship 1 to n between my entities Customer and JobOffert, and the method ViewAllJobOfferts works fine, but I got stuck when I try to add a new JobOffert.
I have a couple of questions, here we go:
I must to create a controller special to JobOfferts, or control
inside the CustomerController?
When I try to create the view that will submit the form to new
JobOffert I didn't know how to link the customer to this new
JobOffert, f I try create a page using customer model, I do not have the JobOffert attributes and if I create using the JobOffert model, I dont know how make the link between these two objects. how must I do this?
PS.: Here the code of both models:
JobOffert Model
Customer Model
I must to create a controller special to JobOfferts, or control inside
the CustomerController?
Not necessarily, controller like other class should follow SRP (Single Responsibility Principle). In this case as long as CustomerController facilitating information related to Customer, it's completely fine.
When I try to create the view that will submit the form to new JobOffert I didn't know how to link the customer to this new JobOffert, f I try create a page using customer model, I do not have the JobOffert attributes and if I create using the JobOffert model, I dont know how make the link between these two objects. how must I do this?
The link between the customer and JobOffer is as you defined One-to-Many and entities contains references to each other. E.g. You can find all JobOffer for a customer whose Id is 1024 by querying JobOffer table where customerID = 1024. Likewise each JobOffer is traceable by Customer reference in entity class.
Now about creating a new JobOffer for a customer this is how you can go about:
public class CustomerController : Controller
{
public CustomerBusiness customerBusiness { get; set; }
public CustomerController()
{
customerBusiness = new CustomerBusiness();
}
//Some code that makes CRUD and more these methods
[HttpGet]
public ActionResult ViewAllJobOffersForCustomer(int customerId)
{
ICollection<JobOfferModel> model = customerBusiness.GetAllJobOffersByCustomerId(customerId);
return View(model);
}
[HttpGet]
public ActionResult CreateJobOffer()
{
// Blank model object to accept values from user,
// you may like to create a view model based on UI needs.
JobOfferModel jobOfferModel = new JobOfferModel();
return View(jobOfferModel);
}
[HttpPost]
public ActionResult CreateJobOffer(JobOfferModel jobOffer)
{
// You get a filled object here that contains customer id and job offer details
customerBusiness.CreateJobOffer(jobOffer);
return RedirectToAction("ViewAllJobOffersForCustomer", new { customerId = jobOffer.CustomerId });
}
Sample business service class:
public class CustomerBusiness
{
public ICollection<JobOfferModel> GetAllJobOffersByCustomerId(int customerId)
{
// TODO: Fetch job offer details from persistent store
// E.g.
// dataContext.JobOffers.Where(x => x.CustomerId == customerId).ToList();
throw new NotImplementedException();
}
public void CreateJobOffer(JobOfferModel jobOffer)
{
// TODO: Add job offer details in persistent store
// E.g.
// dataContext.JobOffers.Add(jobOffer);
}
}
Modified entity classes:
public class JobOfferModel
{
[Key]
public int Id { get; set; }
public string Description { get; set; }
[Required]
[DefaultValue(false)]
public bool Acepted { get; set; }
[Required]
[DefaultValue(true)]
public bool Active { get; set; }
[Required]
[Column(TypeName = "DateTime2")]
public DateTime JobDate { get; set; }
[ForeignKey("Id")]
public int CustomerId { get; set; }
public virtual CustomerModel Customer { get; set; }
}
public class CustomerModel
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Column(TypeName = "DateTime2")]
public DateTime BirthDate { get; set; }
public int PhoneNumber { get; set; }
public ICollection<JobOfferModel> JobOffert { get; set; }
}
So basically you will have a method in CustomerController that returns empty ViewModel or Model object. On view you will make customerId hidden. So that when form is posted it is mapped to correct customer along with JobOffer details. Once you have model object in HttpPost method you just need to insert an entry in JobOffer table (any persistent store) with customerId associated with it.
There are other nitty gritty but above typical approach will give you a good start I hope. Cheers

Passing ViewModel - View to Controller When JavaScript disabled

It's difficult for me for developping an functionality without JavaScript..
I have a ViewModel :
public class AccountRegisterViewModel
{
#region Properties
public User User { get; set; }
public ExternalAccounts ExtAccounts { get; set; }
public LocalPassword Password { get; set; }
public Company CompanyARegister { get; set; }
public Company CompanyBRegister { get; set; }
public bool SameCompanies { get; set; }
public int NbCompanies { get; set; }
...
}
In view, i have a link with checkbox for copying the first company with the second
But I don't know how pass this viewModel (View to Controller) for keeping my data and return the same View with the copie of company..
i try this, in view :
#Html.ActionLink("Click", "CopyCompanies","Account", new { model = Model })
#Html.CheckBoxFor(model => model.SameCompanies)
In Controller :
[AllowAnonymous]
public ActionResult CopyCompanies(AccountRegisterViewModel model)
{
...
if (model.SameCompanies)
{
// copie
}else //clear
...
return View("Step2Register", model);
}
Any idea ?? Thank you for your help
If the CopyCompanies action method requires the AccountRegisterViewModel object, then you will need to provide it. Unfortunately, you will not be able to provide the value using the approach you are following when creating the link.
Your two options would be to have a hidden field for each property in AccountRegisterViewModel and then let the model binding create the object, but even this would not be ideal, since the viewModel is composed of complex objects, so you would have way too many hidden fields.
Your second option, which I think is a better approach, would be to pass in some kind of Id that corresponds to the AccountRegisterViewModel that CopyCompany can use to look up the values it would need.

Handling postbacks in MVC properly

In my update actions I am doing this:
[HttpPost]
public ActionResult Update()
{
if(Request.Form["..."])
{
}
..
}
So I am grabbing the ID of the entity, then loading it, then updating the properties based on the posted form values.
I'm using MVC2.
How should I be doing this as I read about model state but not sure how to start?
I want to refactor to make this right.
Update
My viewmodel looks like:
public class SomeViewModel
{
public User User {get; set;}
}
You should define a view model containing all the necessary information that the view will send:
public class MyViewModel
{
public int Foo { get; set; }
[Required]
public string Bar { get; set; }
[Required]
public DateTime? Baz { get; set; }
}
and then have your controller action take this view model as parameter:
[HttpPost]
public ActionResult Update(MyViewModel model)
{
if (!ModelState.IsValid)
{
// validation failed (the user left the Bar field empty) =>
// we redisplay the view so that he can fix the errors
return View(model);
}
// at this stage we know that the model is valid =>
// we could do some processing. The model.Foo and model.Bar
// properties will contain the values entered by the user in the
// corresponding form fields so that you don't need to fetch them
// manually from the Request. The default model binder will take
// care of this
...
}

View and ICollection Question

I am coverting my app from webforms to mvc, at the moment i am at a design issue (well i just dont know how to do it in mvc).
Basically my model would be something like this:
public class DamagedItem
{
public Int32 LoanId {get;set;}
public String IdentityCode {get;set;}
public virtual ICollection<DamagedItems> DamagedItems {get;set;}
}
In my controller i would like to do:
public ActionResult Add(DamagedItem damagedItem)
{
//Do update logic here
}
Then in my view i can add to the ICollection as needed.
But, i can't do this because if i try and access the ICollection from my controller it is null.
Here is an image of when i want to do:
I just dont know how to lay it out in my view, how to i add such items to my ICollection, update the view then when i need to save i have access to what i have added from my controller?
Thanks,
Nick
Edit:
I was thinking of using a partial in the view and doing all the logic for the bottom half using ajax and storing it in a session variable, but i would prefer NOT to make it reliant on ajax.
It is better to separate: you shoud have 2 actions, which produce 2 view.
You should have LoadInformationModel classe:
public class LoadInformationModel
{
public string StudentCode { get; set; }
public string FirstName { get; set; }
// etc..
public ICollection<Damage> Type { get; set; }
}
corresponding action
[HttpGet]
public ActionResult LoanInformation(int id)
{
var loanInfo = // get data by given id..
var model = new LoadInformationModel {
StudentCode = loanInfo.StudentCode,
// etc
Type = new List<Damage> { new Damage { Value = "Damaged"}, new Damage { Value = "Damaged Again" }
}
return View(model);
}
As well as RepairDataModel class
public class RepairDataModel
{
public bool CoveredByWarranty { get; set; }
public ICollection Status { get; set; }
}
And corresponding action
[HttpGet]
public ActionResult Repair(int id)
{
// logic
return View(model);
}
Your task is to create Post handler, that would save data to DB then form submitted
[HttpPost]
public ActionResult(RepairDataModel model)
{
// save to db
return View();
}
The view returned by Index() method, could be created like
#Html.RenderAction("LoanInformation")
#Html.RenderAction("Repair")
The rest depends on your desing and imagination. I hope that would give you direction.
What I can see is only the DamagedItem lacks a contructor with values for Collection;
public class DamagedItem
{
public DamagedItem()
{
DamagedItems = new List<DamagedItems>();
DamagedItems.Add(new DamagedItem { Description = "Damaged" } );
}
public Int32 LoanId {get;set;}
public String IdentityCode {get;set;}
public virtual ICollection<DamagedItems> DamagedItems {get;set;}
}

Categories

Resources