How to create a many to many relationship in a view - asp.net-mvc-3

Let me be more specific in my question.
I have a many to many relationship. I have an Opportunity and I have items.
In my Opportunity/Create view I want to be able to "add" as many items from the items list as I want. So when I click the save button on the create form, I would be potentially saving many items that would be associated with this record.
So the only way to do this is to include a click box for every item in my items table? How would I do something like Click this button to insert a new item from a drop down list?
Models:
public class Opportunity
{
public int Id { get; set; }
public string MyOpportunity { get; set; }
}
public class Items
{
public int Id { get; set; }
public string ItemName { get; set; }
}
public class ItemsToOpportunity
{
public int Id { get; set; }
public int OpportunityId { get; set; }
public int ItemId { get; set; }
}

Based on your question and comment, it sounds like you just need to provide a form with multiple checkboxes or so.
Then, when you post to the server, the shared name of the checkboxes becomes a comma delimited set of Ids to use.
<input type="checkbox" name="myNewOpportunityItems" value="1"><span>Item 1</span>
<input type="checkbox" name="myNewOpportunityItems" value="2"><span>Item 1</span>
<input type="checkbox" name="myNewOpportunityItems" value="3"><span>Item 1</span>
<input type="checkbox" name="myNewOpportunityItems" value="4"><span>Item 1</span>
Then, when the user selects the checkboxes of the items they want to associate, that gets posted as a comma delimited string:
public ActionResult MyPostActionResult(string myNewOpportunityItems)
{
// myNewOpportunityItems == "1,3,4";
}

Related

Updating article with multiple child elements from one view

I've been looking for this conundrum for days now and been on many possible solutions, but either the examples are too far removed from what I am attempting and I am just not good enough to take in the data because of it. Hence I decided to ask the question myself and show my particular situation.
I got a product model like this (simplified)
public class Product
{
[Key]
public int ProductId { get; set; }
public string SKU { get; set; }
public int ProductCategoriesId { get; set; }
public bool Purchase { get; set; }
public bool ShowProduct { get; set; }
public virtual IList<ProductTranslations> ProductTranslations { get; set; }
}
public class ProductTranslations
{
[Key]
public int ProductTranslationsId { get; set; }
public int ProductId { get; set; }
public string ProductLanguage { get; set; }
public string Description { get; set; }
public string ProductName { get; set; }
public virtual Product Product { get; set; }
}
So I have one product with language neutral data, then each product can have several child elements, one for each language. I have an edit form where I want to show the language neutral data and the language specific data (names and descriptions).
Simplified view:
#model Artikelhantering.Models.Product
#using (Html.BeginForm())
{
#Html.LabelFor(model => model.SKU)
#foreach (var item in Model.ProductTranslations)
{
#Html.EditorFor(TranslationItem => item.ProductName)
#Html.TextAreaFor(TranslationItem => item.Description)
}
<p>
<input type="submit" value="Save" />
</p>
}
This gets everything onto the view nice and tidy looking I think, I think I could (should?) make a dedicated viewmodel for the editor-view later on. Anyway the problem is I don't know how to update the data on submit. I can update the language neutral data using normal EF approach but any changes to the fields from the other model are unchanged...
And to be honest that makes perfect sense to me, since there's nothing in the form to distinquish them, no unique id's or such and the product model does not contain those fields either... The way I would this in old classic asp would be to name the fields with their unique Id's and then make an SQL update query that updated each child model on submitting the form. I guess I could implement such a practice here too but it doesn't feel like the right way to go about things.
Is there a proper or 'best practices' approach in MVC for this kind of thing?

Model binding showing errors with adequate data being provided

I'm having an issue with a field being validated that seemingly is failing validation, but which has no requirements for being completed. I'll try to include all information, so apologies for such a lengthy question post!
The ViewModel that has been created is a DTO object which contains just enough information to action whatever data the user enters. Due to the unknown quantity of items, my view model is slightly more complex than would be ideal ...
public class UpdateViewModel
{
public UpdateViewModel()
{
WorkingPeriods = new List<WorkingPeriod>();
LeavePeriods = new List<LeavePeriod>();
}
public Guid UserID { get; set; }
public DateTime From { get; set; }
public DateTime Until { get; set; }
public DateTime Selected { get; set; }
public IEnumerable<WorkingPeriod> WorkingPeriods { get; set; }
public IEnumerable<LeavePeriod> LeavePeriods { get; set; }
public class WorkingPeriod
{
public int ID { get; set; }
public DateTime Date { get; set; }
public TimeSpan? StartTime { get; set; }
public TimeSpan? EndTime { get; set; }
}
public class LeavePeriod
{
public int ID { get; set; }
public DateTime Date { get; set; }
public int LeaveTypeID { get; set; }
public Guid? Reference { get; set; }
public string Period { get; set; }
public TimeSpan? UserDefinedPeriod { get; set; }
}
}
I've got a view that with the helper of a few static helpers is able to produce the HTML that represents the information being. The output HTML looks similar to:
<li class="editorRow">
<input type="hidden" name="LeavePeriods.index" autocomplete="off" value="8a5522c6-57fe-40a2-95bc-b9792fbe04d3" />
<input id="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__ID" name="LeavePeriods[8a5522c6-57fe-40a2-95bc-b9792fbe04d3].ID" type="hidden" value="4" />
<input id="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__Date" name="LeavePeriods[8a5522c6-57fe-40a2-95bc-b9792fbe04d3].Date" type="hidden" value="23/04/2012 00:00:00" />
<select class="leaveTypeDropdown" id="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__LeaveTypeID" name="LeavePeriods[8a5522c6-57fe-40a2-95bc-b9792fbe04d3].LeaveTypeID">
<option value="">- please select -</option>
<option selected="selected" value="2">Annual Leave</option>
<option value="34">Public Holiday</option>
<option value="1">Sickness</option>
</select>
<div class="leavePeriodRadio">
<input checked="checked" id="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__ALL" name="LeavePeriods[8a5522c6-57fe-40a2-95bc-b9792fbe04d3].Period" type="radio" value="ALL" /><label for="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__ALL">Full Day</label>
<input id="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__AM" name="LeavePeriods[8a5522c6-57fe-40a2-95bc-b9792fbe04d3].Period" type="radio" value="AM" /> <label for="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__AM">AM</label>
<input id="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__PM" name="LeavePeriods[8a5522c6-57fe-40a2-95bc-b9792fbe04d3].Period" type="radio" value="PM" /> <label for="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__PM">PM</label>
<input class="other-user-period" id="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__Other" name="LeavePeriods[8a5522c6-57fe-40a2-95bc-b9792fbe04d3].Period" type="radio" value="Other" /> <label for="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__Other">Other</label>
<span class="user-defined" style="display:none">
Duration: <input class="timepicker" id="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__UserDefinedPeriod" name="LeavePeriods[8a5522c6-57fe-40a2-95bc-b9792fbe04d3].UserDefinedPeriod" type="text" value="" />
</span>
</div>
</li>
The issue appears when a user submits the form using Ajax (set up via the Ajax.BeginForm construct) (MVC3 & JQuery are involved).
#using (Ajax.BeginForm("Index", "Timesheet", new AjaxOptions()
{
HttpMethod = "POST",
LoadingElementId = "loading",
UpdateTargetId = "contentPane",
OnComplete = "SaveCompleted",
}))
If the user omits the field (which they are perfectly entitled to do), they receive the message: The UserDefinedPeriod field is required..
Alternatively, if they enter a valid TimeSpan value (e.g. "12:00"), they get: The value '12:00' is not valid for UserDefinedPeriod..
If they enter an invalid string (e.g. "Boris"), the value is cleared and they are presented with the field required message above as if they had entered nothing. (Assumption: The value is cleared I believe because 'Boris' cannot be stored in a TimeSpan? and therefore cannot be transported back to the view)
Using breakpoints and inspecting the values of various things, I've concluded the following:
The value is present in Request.Form and can be seen with the others that are grouped with it.
The value is present within the Model and can be seen by inspecting it using the breakpoints in VS.
ModelState.IsValid is returning false because Model.LeavePeriods.UserDefinedPeriod has 1 error, which is the same as is displayed to the user in the end.
For reference, the Controller is:
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public class TimesheetController : Controller
{
[HttpPost]
public ActionResult Index(UpdateViewModel updates, User currentUser)
{
if (!ModelState.IsValid)
{
// error with model, so lets deal with it and return View();
}
// continue with parsing and saving.
}
}
The thing that confuses me the most about this issue is that I have other boxes that are built in similar ways that cause no issue - such as both of the TimeSpan?s within the WorkingPeriod enumerable. I'm running out of ideas as to where I can look to see what could be the issue. I've tried changing the ViewModel's expected value from a TimeSpan? to a normal string with no perceivable differences (even the error messages were identical).
I'm guessing that the issue lies either outside of the scope I've detailed below (though, the controller and model are relatively vanilla so I'm lost as to where else to look) or that there is something further I don't understand about model binding (and there is plenty I don't understand about model binding already!).
I've managed to solve this issue, but I'm not overly sure why what I've fixed, fixed it.
Basically, within the UpdateViewModel I changed the "UserDefinedPeriod" to be named "OtherPeriod".
public class LeavePeriod
{
public int ID { get; set; }
public DateTime Date { get; set; }
public int LeaveTypeID { get; set; }
public Guid? Reference { get; set; }
public string Period { get; set; }
public TimeSpan? OtherPeriod { get; set; }
}
Then obviously changed all code references to it elsewhere within my code, including the view:
<span class="user-defined" style="display:none">
Duration: <input class="timepicker" id="LeavePeriods_8a5522c6-57fe-40a2-95bc-b9792fbe04d3__OtherPeriod" name="LeavePeriods[8a5522c6-57fe-40a2-95bc-b9792fbe04d3].OtherPeriod" type="text" value="" />
</span>
This then seemed to fix the issue. I'm not sure what the specific reason for the change working was - so I'm open to any comments as to what may have caused it still. However, this tweak was the fix for me and may work for anyone else having this issue in the future.

MVC3, multiple file upload, model binding

There is a view to update a complex model(Transaction).
Complex model has properties which can have multiple attachments(files),
so that user can upload multiple files simultaneously in this form,
and I am trying to save these files to the database.
I have successfully posted multiple files to the server,
following blog post
http://haacked.com/archive/2010/07/16/uploading-files-with-aspnetmvc.aspx.
However in order to save these files, so that I can keep track of which files belongs to which object of the complex model(Transaction) and therefore show them later at appropriate places, I need some way to associate file uploaded to the object it belongs to, but since all files come under name 'files' I don't know how I can make this work.
Here is simplified complex model:
public class Transaction
{
[Key]
public int Id { get; set; }
public virtual PurchaseRequisition PurchaseRequisition { get; set; }
public virtual Evaluation Evaluation { get; set; }
}
Properties of complex model:
public class PurchaseRequisition
{
[Key, ForeignKey("Transaction")]
public int TransactionId { get; set; }
public virtual Transaction Transaction { get; set; }
[Display(Name = "Specifications/Requisitioner's Notes")]
public virtual ICollection<Attachment> SpecsRequisitionerNotesFiles { get; set; }
}
public class Evaluation
{
[Key, ForeignKey("Transaction")]
public int TransactionId { get; set; }
public virtual Transaction Transaction { get; set; }
public virtual ICollection<Attachment> BidResultsFiles { get; set; }
}
public abstract class Attachment
{
[Key]
public int Id { get; set; }
public string FileName { get; set; }
public string FileExtension { get; set; }
public byte[] Data { get; set; }
public Boolean Deleted { get; set; }
}
Here is the controller:
[HttpPost]
public ActionResult Create(TransactionViewModel model, IEnumerable<HttpPostedFileBase> files)
{ //save to database }
Create separate sections in the view for the purchase requisitions and bid results. Something like this:
<form action="" method="post" enctype="multipart/form-data">
<h3>Purchase Requistions</h3>
<label for="file1">Filename:</label>
<input type="file" name="purchasereqs" id="file1" />
<label for="file2">Filename:</label>
<input type="file" name="purchasereqs" id="file2" />
<h3>Bid Results</h3>
<label for="file3">Filename:</label>
<input type="file" name="bidresults" id="file3" />
<label for="file4">Filename:</label>
<input type="file" name="bidresults" id="file4" />
<input type="submit" />
</form>
Then you would have an action signature like this:
[HttpPost]
public ActionResult Create(
TransactionViewModel model,
IEnumerable<HttpPostedFileBase> purchasereqs,
IEnumerable<HttpPostedFileBase> bidresults)
{
//save to database
}

Razor proxy type error. System.Data.Entity.DynamicProxies

I have a class User and then another Type UserSpecial with some special user properties.
I pass it in razor to the partial method class to create the UserSpecial form which expects an object of type User Special but i get an error.
#model User
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
#Html.Partial("../UserSpecial/_CreateOrEdit", Model.UserSpecial)
<p class="submit clear">
<input type="submit" value="Register" />
</p>
</fieldset>
}
</div>
Error i get -
The model item passed into the dictionary is of type 'System.Data.Entity.DynamicProxies.User_AC9DED50C9495788046D6BFA3B90DDFC6AD2884157CF23C91FCB3B7A55F70B18', but this dictionary requires a model item of type 'UserSpecial'.
What am i doing wrong here?
From my controller i just pass the current User Object that i have stored in the session state.
Controller -
public ActionResult Register()
{
return View(userRepository.Current);
}
Here Current is of type "User"
Model -
public partial class User
{
public User()
{
}
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Sex { get; set; }
public System.DateTime CreateDate { get; set; }
public string Email { get; set; }
public string HomeTown { get; set; }
public short UserType { get; set; }
public virtual UserSpecial UserSpecial { get; set; }
}
Model Declaration for _CreateOrEdit is
#model UserSpecial
No idea what userRepository.Current is but it seems that it doesn't correctly/eagerly load the UserSpecial property. Why don't you use view models? Why are you passing domain entity models to your views? That's bad practice. You should define view models that contain only the data that's required by your view and then in your controller action map between your domain models and the corresponding view model which will be passed to the view.
The solution for this issue is pretty simple:
You should just use a wrapper class (view model) as Darin suggested.
So for example:
(entity domain model):
public class MyEntityModel
{
public int Id { get; set; }
public String Name { get; set; }
}
=> put it in a ViewModel (just a stupid wrapper) should result in this
public class MyViewModel
{
public MyEntityModel MyEntityModel { get; set; }
}
Now, in the view, u should be able to access the "name" property by doing this
<div>
The entity object's name is "#model.MyEntityModel.Name"
</div>
(notice you should not use #model.Name!)

How to dynamically add textbox in the MVC 3

I have two models:
public class Contact
{
public Guid ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Email
{
public Guid EmailId { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Name { get; set; }
public Guid ContactId { get; set; }
}
Is it possible with MvcScaffolding, to automatically be generated View Contacts-> Create with dynamic textbox for field email?
For example, when entering the email in the first textbox, then the following created another textbox, etc.
In your model, make the email field a List.
If you set the names as follows, the mvc model binder will automatically populate your list on postback:
<input name="Emails[0]" type="text" />
<input name="Emails[1]" type="text" />
<input name="Emails[2]" type="text" />
and so forth. I'm assuming you're using jquery or something similar to dynamically add the textboxes, so just set the new textboxes name equal to total count - 1. And if you want to allow the user to remove any one of the textboxes, you'll have to go back through and recalculate the names and their index.

Resources