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);
}
}
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 am new to MVC (coming from WebForm background). I have a main view which contains 2 tabs that need the same model for table view. However, I created a partial view containing table structure to display data records in the 2 tabs asynchronously. One tab gets its data from CSV while the other gets from SQL Server based on user's selection but having the same model.
I have the screenshot of the concept below: The box in red depicts the Partial View, it has 2 buttons, one to commit the table data into the database while the other is to add more records if needed;
Partial view in Parent View screenshot
My challenge is: I have a class method that needs to get a CSV file from FileUpload value and binds data to the model on the partial view.
See my model structure below:
[Table("atm")]
public class ATM
{
public ATM()
{
this._EJTransactions = new HashSet<EJTransaction>();
this._CassetteSettings = new HashSet<CassetteSetting>();
this._EJFileDownloads = new HashSet<EJFileDownload>();
this._CamFileDownloads = new HashSet<ImageFileDownload>();
this._TransImages = new HashSet<TransImage>();
this._UserAtms = new HashSet<UserAtms>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int atmId { get; set; }
[ForeignKey("ATMVendor")]
public int vendorId { get; set; }
[ForeignKey("BankBranch")]
public int branchId { get; set; }
[MaxLength(50)]
[Index("ix_uniq_terminal", 1, IsUnique = true)]
[DisplayName("Terminal ID")]
public string terminalId { get; set; }
[MaxLength(30)]
[Index("ix_uniq_ip", 1, IsUnique = true)]
[DisplayName("IP")]
public string ip { get; set; }
[MaxLength(100)]
[Index("ix_uniq_title", 1, IsUnique = true)]
[DisplayName("Title")]
public string title { get; set; }
[DisplayName("EJ Enabled")]
public bool ejEnabled { get; set; }
[DisplayName("Image Enabled")]
public bool camEnabled { get; set; }
[DisplayName("IsActive")]
public bool isActive { get; set; }
public virtual ATMVendor ATMVendor { get; set; }
public virtual BankBranch BankBranch { get; set; }
public virtual ICollection<EJTransaction> _EJTransactions { get; set; }
public virtual ICollection<CassetteSetting> _CassetteSettings { get; set; }
public virtual ICollection<EJFileDownload> _EJFileDownloads { get; set; }
public virtual ICollection<ImageFileDownload> _CamFileDownloads { get; set; }
public virtual ICollection<TransImage> _TransImages { get; set; }
public virtual ICollection<UserAtms> _UserAtms { get; set; }
}
The partial view binds to this model.
public class CSVAtmLoader
{
public IEnumerable<ATM> Read(string csvfile)
{
List<ATM> atmlist = new List<ATM>();
TextReader csvReader = new StreamReader(csvfile);
var csv = new CsvReader(csvReader, false);
csv.Configuration.DetectColumnCountChanges = true;
csv.Configuration.RegisterClassMap<AtmMap>();
//csv.Configuration.InjectionCharacters = new[] { '=', '#', '+', '-' };
//csv.Configuration.SanitizeForInjection = false;
//csv.Configuration.InjectionEscapeCharacter = '\t';
var atms = csv.GetRecords<ATM>();
foreach (var atm in atms)
{
atm.branchId = GetBranchId(atm.BankBranch.branch);
atm.vendorId = GetVendorId(atm.ATMVendor.vendor);
atmlist.Add(atm);
}
return atmlist;
}
private int GetBranchId(string branch)
{
BankBranch br = new BankBranch { branch = branch }.SelectBranch();
return 0;
}
private int GetVendorId(string vendor)
{
return 0;
}
}
In the parent view, I have a CSVAtm tab that hosts the partial view, I am headway blocked to get this done with dynamism of the layout. See my parent view that render the partial view with csvfile chosen from FileUpload control:
<div class="tab-pane fade" id="csvAtm">
<p>
<div class="form-inline">
<table>
<tr>
<td><span> Choose a File:</span> </td>
<td><input id="csvfile" type="file" name="file" class="btn btn-default" onchange="clear();"/></td>
<td> </td>
<td>
<input class="btn btn-default col-mid-2" type="button" id="upload" value="Upload" onclick="uploadfile();" />
</td>
</tr>
</table>
</div>
<div id="error"></div>
<br />
<div class="form-group">
#{
if (string.IsNullOrEmpty(filename))
{
//this is where my challenge is, how to get the filename from fileupload
//I know this is executed at the server side and hence, the client is not aware of server side scripting
Html.RenderPartial("LoadedAtms", new CSVAtmLoader().Read(filename));
}
}
</div>
I need a better way to achieve this with fileupload security in mind, my approach might be wrong. If I can achieve this successfully in the csv tab, it will be easier to replicate same for connecting to Sql server.
Thanks so much..
I'm working on ASP.NET MVC web application, and I need to submit List<ProdColor> to Controller using checkboxs. Here is my code
Model
public partial class ProdColor
{
public int ProdColor_ID { get; set; }
public Nullable<int> P_ID { get; set; }
public Nullable<int> Color_ID { get; set; }
public virtual ProdctModelView ProdctModelView { get; set; }
}
public class ProdctModelView
{
public ProdctModelView()
{
this.ProductColors = new HashSet<ProdColor>();
}
public int P_ID { get; set; }
public string P_name { get; set; }
public virtual ICollection<ProdColor> ProductColors { get; set; }
}
Controller
public ActionResult Create()
{
ViewBag.colorlist = db.Colors.OrderBy(m => m.Color_name).ToList();
return View();
}
[HttpPost]
public ActionResult Create(ProdctModelView product, List<ProdColor> ProductColors)
{
Product prod = new Product();
//Save new product
db.Products.Add(prod);
db.SaveChanges();
foreach (var color in ProductColors)
{
color.P_ID = prod.P_ID;
db.ProdColors.Add(color);
}
db.SaveChanges();
return RedirectToAction("Index");
}
View
#model mvc4test.Models.ProdctModelView
#using (Html.BeginForm("Create", "CP_Product", FormMethod.Post))
{
#for (int i = 0; i < ViewBag.colorlist.Count; i++)
{
<input type="checkbox" id="#ViewBag.colorlist[i].Color_name" name="[#i].Color_ID" value="#ViewBag.colorlist[i].Color_id"/>
}
<input type="submit" value="Save" />
}
The problem is when submitting the checkboxes without selecting the first one, the value of List<ProdColor> become Null. So how should I get the correct values at the Controller.
You manually creating checkboxes with indexers. Unchecked checkboxes do not post back a value, so if any of the checkboxes are unchecked, you get non-consecutive indexers so model binding fails.
Your model (view model) needs to include a boolean property (say) public bool IsSelected { get; set; } so that in the view you can use the #Html.CheckBoxFor() method to strongly bind to your model.
#for (int i = 0; i < Model.ColorList.Count; i++)
{
#Html.HiddenFor(m => m.ColorList[i].Color_id)
#Html.CheckBoxFor(m => m.ColorList[i].IsSelected)
#Html.LabelFor(m => m.ColorList[i].IsSelected, Model.ColorList[i].Color_name)
}
The CheckBoxFor() method generates a checkbox with value="true" and an associated hidden input with value="false". If the checkbox is checked, both true and false are posted, but only the first (true) value is bound. If the checkbox is unchecked, the only false is posted.
Then in the POST method, you can get the ID's of the selected items using (say)
var selectedColors = product.ColorList.Where(c => c.IsSelected).Select(c => c.Color_id);
Note that you do not need a parameter in your POST method for List<ProdColor> ProductColors since parameter ProdctModelView product already contains all those values.
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.