Pretty new to MVC and the like. I have a class the looks like this:
public class SomeExampleModel
{
public int Id { get; private set; }
public string Name { get; private set;}
public string Street { get; private set; }
public IList<Contact> Contacts { get; private set; }
...
}
Where Contact looks like:
public class Contact
{
public int Id { get; private set; }
public int SomeExampleModelId { get; private set; }
public ContactType Type { get; private set; }
public string ContactValue { get; private set; }
...
}
Now the problem is, I have a Post Create action and Create.cshtml view to add new SomeExampleModel to my database. It works great for all the basic properties, but there isn't anything for the Contacts property (which is a separate table in the database).
How would I be able to add contacts (single for now, plural in the future) using forms on my view?
EDIT: I am sorry if I was not clear. The problem is not getting data to save in the database correctly. I have tested that already and it works fine if I just manually insert a SomeExampleModel record into the database with the SomeExampleContext. What I dont know is how to draft my View so that it allows users to add Contacts to the record
Here is one approach using an EditorTemplate. I have made minor changes to your model classes (this works; however, note that this is only for you to understand the concept. You can extend upon this)
Models
public class SomeExampleModel
{
public int Id { get; set; }
public string Name { get; set;}
public string Street { get; set; }
public IList<Contact> Contacts { get; set; }
}
public class Contact
{
public int Id { get; set; }
public int SomeExampleModelId { get; set; }
public ContactType Type { get; set; }
public string ContactText { get { return Type.ToString(); } }
public string ContactValue { get; set; }
}
public enum ContactType
{
email,
Phone,
mobile,
fax
}
Make a note that I've created a property ContactText that returns the enum text (for display purposes)
Create a editor template for Contact (named Contact.cshtml; Template name must match the class name). Find below the screen shot on where to place your editor template.
Here is the code for Contact.cshtml
#model Test1.Models.Contact
<table>
#Html.HiddenFor(a=>a.Type)
<tr>
<td>#Html.Label(Model.ContactText)</td>
<td>#Html.TextBoxFor(a => a.ContactValue)</td>
</tr>
</table>
Here is the code for the 'Create' view (ExampleCreateView.cshtml in my case)
#model Test1.Models.SomeExampleModel
#{
ViewBag.Title = "ExampleCreateView";
}
<h2>ExampleCreateView</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>SomeExampleModel</legend>
#Html.HiddenFor(model=>model.Id)
<table>
<tr>
<td>#Html.LabelFor(model=>model.Name)</td>
<td>#Html.EditorFor(model=>model.Name)</td>
</tr>
<tr>
<td>#Html.LabelFor(model=>model.Street)</td>
<td>#Html.EditorFor(model=>model.Street)</td>
</tr>
<tr>
<td>#Html.LabelFor(model=>model.Contacts)</td>
<td>#Html.EditorFor(model=>model.Contacts)</td>
</tr>
</table>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Make a note on how I've used #Html.EditorFor for the Contacts property.
Here is how the Get, Post actions will look like
public ActionResult ExampleCreateView()
{
SomeExampleModel model = new SomeExampleModel();
Contact contactEmail = new Contact();
contactEmail.Type = ContactType.email;
Contact contactFax = new Contact();
contactFax.Type = ContactType.fax;
Contact contactPhone = new Contact();
contactPhone.Type = ContactType.Phone;
Contact contactMobile = new Contact();
contactMobile.Type = ContactType.mobile;
List<Contact> contacts = new List<Contact>();
contacts.Add(contactEmail);
contacts.Add(contactFax);
contacts.Add(contactPhone);
contacts.Add(contactMobile);
model.Contacts = contacts;
return View(model);
}
[HttpPost]
public ActionResult ExampleCreateView(SomeExampleModel model)
{
//Your operations
return View(model);
}
Run the application. This is how the view looks like
Screen shot of what you will get in the POST action
The way I would do it is to have two separate actions - one that does the initial create of SomeExampleModel, and a separate action for adding a Contact to that model.
That way your create SomeExampleModel view would just have Name and street, and when it saved you would show a readonly version of the SomeExampleModel . The readonly version of SomeExampleModel would list all related contacts in a table below the Name and Street, with an edit and delete link, and an 'add new' contact link under the table
e.g.
<table>
#foreach (var contact in Model.Contacts)
{
<tr>
<td>#contact.ContactType</td>
<td>#contact.ContactValue</td>
<td>#Html.Action("Edit", "Edit", "Contact", new { id = contact.Id }</td>
<td>#Html.Action("Delete", "Delete", "Contact", { id = contact.Id }</td>
</tr>
}
</table>
#Html.Action("Add new contact", "Add", "Contact" new { id = Model.Id }
Initially, there would be no contacts listed, then later you would have multiple contacts.
Related
Edit My view is using the Employer model. Employer and JobPosting have a 1:M relationship. I will share more of the view for context.
Context: In my application, I want to show the Employer the number of applicants who applied for their JobPosting. The code that I currently have written is not returning any value. It's not throwing any errors- but it's not working either. I'm pretty sure the issue is in my controller, but I'll provide the Model and View as well.
Controller:
public ActionResult AppCount()
{
foreach (var app in db.JobPostings.ToList())
{
int id = app.JobPostingID;
int count= db.Applications.Where(a => a.JobPostingID == id).Count();
app.AppCount = count;
ViewBag.AppCount = count;
}
return View();
}
View:
#model InTurn_Model.Employer
.
.
.
<h2>My Job Postings</h2>
<p>
#Html.ActionLink("Create New", "Create", "JobPostings", null, null)
</p>
<div id="employeeContainer"></div>
<table class="table table-striped">
<tr>
<th>Position</th>
<th>Job Type</th>
<th>Number of Applicatiosn</th>
<th></th>
</tr>
#foreach (var item in Model.JobPostings)
{
if (item.EmployerID == Model.EmployerID)
{
<tr>
<td>
#Html.DisplayFor(model => item.Position)
</td>
<td>
#Html.DisplayFor(model => item.JobType)
</td>
<td>#ViewBag.AppCount</td>
<td>#Html.ActionLink("Details", "Details", "JobPostings", new { id = item.JobPostingID }, null) </td>
</tr>
}
}
</table>
Model:
[MetadataType(typeof(JobPostingMetaData))]
public partial class JobPosting
{
public int AppCount { get; set; }
private sealed class JobPostingMetaData
{
[Display(Name = "Job Posting ID")]
public int JobPostingID { get; set; }
[Display(Name = "Employer ID")]
public int EmployerID { get; set; }
[Display(Name = "Description")]
public string Desc { get; set; }
[Display(Name = "Job Type")]
public JobType JobType { get; set; }
[Display(Name = "Employment Type")]
public TimeType TimeType { get; set; }
[DataType(DataType.Currency)]
public decimal Wage { get; set; }
}
}
There are two problems that I see.
First, you are not passing Model from controller to view. However, you are iterating through Model.JobPostings. It is empty.
Second, you assign ViewBag.AppCount in the loop. So, all values, except for the last one are lost. But if you fix the first problem (use Model instead of ViewBag) - the second might go away by itself.
You need to specify the model in the view with #model:
#model YourNameSpace.JobPosting
Then return that model to the view:
public ActionResult AppCount()
{
foreach (var app in db.JobPostings.ToList())
{
int id = app.JobPostingID;
int count= db.Applications.Where(a => a.JobPostingID == id).Count();
app.AppCount = count;
ViewBag.AppCount = count;
}
return View(app);
}
This will make the values in the model available to the view. There is no need to use ViewBag, as AppCount is part of the model.
I was overthinking this. I just needed to set this up from the JobPosting model, and then the rest worked, I didn't need to iterate through the Controller at all.
public int AppCount => Applications.Count;
I want to store fields and checkboxes in a database table in a form: The connection table contains the following fields:
connection table:
public partial class Connection
{
[Key]
public int ID { get; set; }
public string CommunicationName { get; set; }
public bool IsSelected { get; set; }
}
Register table:
public class RegisterForm
{
#region Ctor
public RegisterForm()
{
}
#endregion Ctor
#region Properties
[Key]
[Required]
public int ID { get; set; }
[Required(ErrorMessage = ("Required"))]
[StringLength(50, ErrorMessage = "This field must be a maximum of 50 characters")]
[TypeConverter("NVarchar(121)")]
[DisplayName("FullName")]
public string FullName { get; set; }
public string Email { get; set; }
public List<Connection> Communications { get; set; }
}
The values of the checkbox fields in the list are displayed using the following method:
questionForm.Communications = db.Connections.ToList<Connection>();
Now how to save the information in the post and save it to the register table. ????????? What changes should be Create to the update, delete operation in the register?
controller for register:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,FullName,PhoneNumber,Email,Communication,")]RegisterForm questionForm)
{
if (ModelState.IsValid)
{
db.Registers.Add(questionForm);
var data = db.SaveChanges();
return View("FormSuccessfullySubmitted");
}
return View(questionForm);
}
You should read about MVC model binding. Normally it could bound it without any problem. But lists are slightly different. You are to provide index of item in list. That is why it is better to use for, instead of foreach.
Check this view and grab it POSTed values to examine. Pay attention, that all list items are displayed using its index in list.
<table class="table">
#using (Html.BeginForm("Bind", "Bind", FormMethod.Post))
{
for (int i = 0; i < Model.Count(); i++)
{
<tr>
<td>
#Html.DisplayFor(modelItem => Model[i].CommunicationName)
</td>
<td>
#Html.CheckBoxFor(modelItem => Model[i].IsSelected)
</td>
</tr>
}
<button type="submit">Submit</button>
}
</table>
I have the following view:
#model Entities.Models.Tournament
#using (Html.BeginForm("Index", "Game"))
{
<table>
<tr>
<td><label>Team</label></td>
<td><label>Points Brought To Tournament</label></td>
</tr>
#{
const int maxNumOfTeams = 8;
for (int i = 0; i < maxNumOfTeams; i++)
{
<tr>
<td>#Html.DropDownList("SelectedTeams[" + i + "].TeamId", Model.Teams, "Please select:")</td>
<td>#Html.TextBox("SelectedTeams[" + i + "].Points", "", new { type = "number" })</td>
</tr>
}
}
</table>
<input type="submit" value="Create game" />
}
Is there a more elegant/"best practice" way of generating 8 dropdowns which are then used to build a List<T> and send it to an action method using HTTP Post?
The way I am doing it now seems messy as I have to build the dropdown's html ID string using concatenation and the i variable.
Here is the model:
public class Tournament
{
//This is populated with teams from the DB and then used in the view to allow the user to select a team
public IEnumerable<SelectListItem> Teams { get; set; }
//This represents the teams the user selected
public List<TeamWithPointsBroughtForward> SelectedTeams { get; set; }
}
public class TeamWithPointsBroughtForward
{
public int TeamId { get; set; }
public int Points { get; set; }
}
Here is the controller and action method:
public class GameController : Controller
{
public ActionResult Index(Tournament tournament)
{
Game game = new Game();
//TODO: set up the game object based on the tournament settings
return View(game);
}
}
I have an MVC proyect using EF (database first) and I have already created CRUD for some entities.
Now I am trying to create a dashboard page that contains widgets or similar, each listing the last 10 entities from different db tables (last 10 products created, last 10 customers, etc)
To create the widget I have followed this tutorial https://www.codeproject.com/Articles/598383/Widgets-in-MVC
So I have 2 interfaces and 2 classes that implements those interfaces:
public interface IWidget
{
int SortOrder { get; set; }
string Entity { get; set; }
string CssClassName { get; set; }
string HeaderText { get; set; }
string FooterText { get; set; }
ISubWidget SubWidget { get; set; }
}
public interface ISubWidget
{
ICollection<Products> EntitiesList { get; set; }
}
public class Widget : IWidget
{
public int SortOrder { get; set; }
public string Entity { get; set; }
public string HeaderText { get; set; }
public string FooterText { get; set; }
public ISubWidget SubWidget { get; set; }
}
public class SubWidget : ISubWidget
{
public ICollection<Products> EntitiesList { get; set; }
}
then I have 3 partial views: one for the widget itself, one for the subwidget, and one that will act as a container of all created widgets.
#model Proyect.Comun.Widget.IWidget
<div class="widget">
<div class="#Model.CssClassName">
<div class="widgetHeader">
#Model.HeaderText
</div>
<div class="widgetBody">
#Html.Partial(string.Concat(new[] { "Widget", "/", "_SubWidget" }), #Model.SubWidget)
</div>
<div class="widgetFooter">
#Model.FooterText
</div>
</div>
</div>
#model Proyect.Comun.Widget.ISubWidget
<table>
#foreach (var item in Model.EntitiesList)
{
<tr>
<td> #Html.DisplayFor(modelItem => item.product_number) </td>
<td> #Html.DisplayFor(modelItem => item.product_name) </td>
<td> #Html.DisplayFor(modelItem => item.product_description) </td>
</tr>
}
</table>
#model Fruterias.Comun.Widget.IWidget
#foreach (Proyect.Comun.Widget.IWidget wm in ViewBag.Widgets)
{
#Html.Partial(string.Concat(new[] { "Widget", "/", "_Widget" }), wm)
}
then in the Dashboard controller:
public ActionResult Index()
{
ViewBag.Widgets = GetWidgetData();
return View();
}
public List<IWidget> GetWidgetData()
{
var lastWidget = new List<IWidget>{
new Widget()
{
SortOrder = 1,
CssClassName = "high",
Entity = "Products",
HeaderText = "Last Products",
FooterText = "",
SubWidget = new SubWidget {
EntitiesList = db.products.OrderByDescending(p => p.date).Take(10).ToList(),
}
},
};
return lastWidget;
}
And finally in the view for Dashboard/Index:
<p>
#{
#Html.Partial("Widget/_WidgetContainer");
}
</p>
This works ok and shows a list of Product entities. But now I want to create different widgets associated to different entities.
Of course the problem is that the property EntitiesList is declared as a collection of Product objects, so I can not fill it with, for example, customers objects.
What would be the solution here? I could create different interfaces (and classes) for each type of entity, but Im sure there must be a better way...
Also, as the entities models (Products, Customers...) are generated with Entity Framework db first, I guess I can not create an Interface and make them implement that interface (or can I?)... (anytime the db changes and models are regenerated by EF, all that would be lost?)
If you don't need to have the EntitiesList to be a specific type you could just have the SubWidget be generic and set its type when you instantiate.
public class SubWidget<T> : ISubWidget<T> where T : class
{
public ICollection<T> EntitiesList { get; set; }
}
or you could use dynamic object.
public class SubWidget : ISubWidget
{
public ICollection<dynamic> EntitiesList { get; set; }
}
My model is :
public class ContactInfo
{
public IEnumerable<SupplierContact> PriceRequest { get; set; }
public IEnumerable<SupplierContact> OrderConfirmation { get; set; }
public IEnumerable<SupplierContact> Account { get; set; }
}
public class SupplierContact
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Title { get; set; }
public string Email { get; set; }
public string MobilePhone { get; set; }
}
and my Controller action is
public ActionResult EditContactInfo(ContactInfo contactInfo)
{
// not getting any values here..
}
View is rendering like :
#foreach (SupplierContact PriceRequest in Model.PriceRequest)
{
<tr class="">
<td style="text-align: left;" class="csstd-left">#Html.TextBoxFor(m => PriceRequest.Email)</td>
<td class="csstd">#Html.TextBoxFor(m => PriceRequest.MobilePhone)</td>
<td class="csstd">#PriceRequest.Title</td>
<td class="csstd">#PriceRequest.FirstName</td>
<td class="csstd">#PriceRequest.LastName</td>
</tr>
}
And I am referencing #model ContactInfo in my view
However i can achieve it using
Request.Form.Get("PriceRequest.Email")
but I want to use model binding feature .
You need to use a for loop (and you will need to change the collections from IEnumerable to IList to the name attributes are correctly indexed
#for (int i = 0; i < Model.PriceRequest.Count; i++) {
#Html.TextBoxFor(m => Model.PriceRequest[0].Email)
#Html.TextBoxFor(m => Model.PriceRequest[i].MobilePhone)
}
Alternatively you can create a EditorTemplate for SupplierContact and use
#Html.EditorFor(m => m.PriceRequest)
This will generate html like
<input name="PriceRequest[0].Email" ...
<input name="PriceRequest[0].MobilePhone" ...
<input name="PriceRequest[1].Email" ...
<input name="PriceRequest[2].MobilePhone" ...
etc.
Take a look at display and editor templates. Than you can create a view called SupplierContact. MVC automatically knows what to show if he see the complex type.
See this example:
http://www.asp.net/mvc/tutorials/javascript/using-the-html5-and-jquery-ui-datepicker-popup-calendar-with-aspnet-mvc/using-the-html5-and-jquery-ui-datepicker-popup-calendar-with-aspnet-mvc-part-2
So create a folder: DisplayTemplates in your views folder.
Then create a partial view called SupplierContact.
Set the model of the partial view as a SupplierContact.
Create the labels for displaying and run your application again.
For editing, create a EditorTemplates folder.