Models
public class IntegerList
{
public int IntegerListID { get; set; }
public string Direction { get; set; }
public long Performance { get; set; }
public virtual ICollection<Integer> Integers { get; set; }
}
public class Integer
{
public int IntegerID { get; set; }
[Required(ErrorMessage = "An integer is Required")]
[Range(0, 9999999, ErrorMessage = "Enter an integer")]
public int IntegerValue { get; set; }
public int IntegerListID { get; set; }
public virtual IntegerList IntegerList { get; set; }
}
The above models are for an application that sorts a range of integers into ascending or descending order and calculates the time taken to perform the sort.
The integers are entered into textboxes by the user who can add or remove them using jquery.
I've got the application working by passing formcollection to the controller, splitting the string of integers into an array of integer values to be added to IntegerList.Integers.
However, I'm looking for a more elegant strongly-typed solution.
ViewModel
public class IntegerViewModel
{
[UIHint("Integers")]
public IEnumerable<Integer> Integers { get; set; }
public string Direction { get; set; }
}
Where I'm struggling is adding Integers.IntegerValues to the view so they can be passed to the controller via the viewmodel. I also need to increment/decrement each IntegerValue textbox value as it is added/removed by jquery. I'm not sure I'm approaching this from the right angle.
I hope that's clear. Thanks for your assistance.
Razor
<form method="post">
<div id="integers">
#Html.Label("Enter Integers")
<div class="integer">
#Html.EditorFor(model => model.Integers)
Add Integer
</div>
</div>
#Html.Label("Sort Direction")
<div>
#Html.DropDownListFor(model => model.Direction, new[] {
new SelectListItem() {Text = "Ascending", Value = "Ascending"},
new SelectListItem() {Text = "Descending", Value = "Descending"}
})
<input class="button" type="submit" value="Submit" />
</div>
</form>
Editor Template (for Integers)
#model Integer
#Html.EditorFor(m => m.IntegerValue)
Let me summarize everything.
You will not find any strongly-typed solution there as far as you are adding and removing values (integers) using JavaScript (jQuery).
So, when you are adding integer editor using jquery you should render approppriate HTML according to ASP.NET MVC contracts.
David's comment is very valuable. Check out this link. http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Here you can see how you should render your HTML to receive different types of data on the server (ASP.NET MVC action).
Also you can check your EditorFor output and render HTML like it was rendered using MVC.
And finally, here is the question about dynamic form fields in ASP.NET MVC. ASP.NET MVC Dynamic Forms
Hope this helps.
Related
Is it correct a model?
public class NewForm
{
public string[] Field { get; set; }
public bool[] Check { get; set; }
}
for such a VIEW:
#Html.TextAreaFor(model => model.Field)
#Html.TextAreaFor(model => model.Field)
#Html.TextAreaFor(model => model.Field)
#Html.CheckBoxFor(model => model.Check)
#Html.CheckBoxFor(model => model.Check)
Or is there a better way to create fields of the same name?
In Controller displays only the first value. But i need all
[HttpPost]
public ActionResult Edit(NewForm model)
{
Response.Write(model.Field);
Response.Write(model.Check);
}
Fields may be an indefinite number due to the fact that by clicking on the button JavaScript adds a new field of the same name with the same name
It sounds like you want to submit multiple instances of your model back to the controller.
You could do something like this. My example will submit 10 instances of Field back to your controller.
View:
#using (Html.BeginForm())
{
<div>
#for(int i = 0; i<10; i++)
{
<div>#Html.TextBox("items[" + i + "].Field", "", new { id = "items[" + i + "].Field", placeholder = "Enter Text..." })</div>
#Html.Hidden("items.Index", i)
}
</div>
<input type="submit" value="Submit" />
}
Class:
public class MyClass
{
public string Field {get;set;}
}
Controller Method:
[HttpPost]
public ActionResult ActionName(List<MyClass> items)
{
//...do stuff
}
Obviously you could also add your checkbox into the model and form too in order to submit many of those.
Why you want to be the same name for the fields, each field has proper name
public class NewForm
{
public string FirstField { get; set; }
public string Field { get; set; }
public bool Check { get; set; }
}
VIEW
#Html.TextAreaFor(model => model.FirstField)
#Html.TextAreaFor(model => model.Field)
#Html.CheckBoxFor(model => model.Check)
ee
[HttpPost]
public ActionResult Edit(NewForm model)
{
Response.Write(model.FirstField);
Response.Write(model.Field);
Response.Write(model.Check);
}
No, it isn't. In the model you defined, you have created two different arrays: The first property Field is an array of strings and the second property Check an array of bools. putting the [] after a type indicates an array.
If you have an unknown number of what I'll call "mini forms" and the number of these is decided by the user via the UI, then you should create a view model to represent this mini form, and a container view model to house it and any other properties your view will need.
For example:
public class MiniFormViewModel
{
public string MyInput { get; set; }
public bool MyCheck { get; set; }
}
then in your container view model:
public class ContainerViewModel
{
public IEnumerable<MiniFormViewModel> MiniForms { get; set; }
//Any other properties you need on the view that will occur a single time
}
Now, in the JS you'll need to add some manipulation in order to do this:
function getViewModel() {
//You'll have to decide how you want to get the values of the mini form's fields. Perhaps you might even have a function to supply these values. Up to you.
return {
MiniForms: [{
MyInput: '', //value from the first "mini form' string field
Mycheck: false //value from the first "mini-form" bool field
},
{
MyInput: '', //value from the second"mini form' string field
Mycheck: false //value from the second"mini-form" bool field
}
]
}
}
Then you'll need to post this back to the server. I'll demonstrate how to do this via the built in JS Fetch function:
fetch(yourUrlForTheControllerAction,
{
method: 'post',
body: JSON.stringify(getViewModel()),
headers: {
'content-type': 'application/json; charset=UTF-8'
}
})
And then blammo, you should be good to go. I excluded the part of dynamically adding the mini form fields because it sounds like you have a solution for that already.
This is my first time using MVC in a business capacity, so please forgive me if this is an easily answered question.
My issue is that I have a razor view for editing a model, we will call it
MainModel. Here is a reduced version of MainModel:
using System;
using System.Collections.Generic;
namespace MyProject.Models
{
public partial class MainModel
{
public MainModel()
{
SecondModel = new HashSet<SecondModel>();
}
public int MainModelId { get; set; }
public DateTime? DueDate { get; set; }
public int? CreatedByUserId { get; set; }
public DateTime? CreatedDateTime { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<SecondModel> SecondModel { get; set; }
public virtual Users CreatedByUser { get; set; }
}
}
MainModel has a reference to another Model, SecondModel (original names, I know). This reference is a collection so that it can be looped through with a foreach within the razor view. Here is SecondModel:
namespace MyProject.Models
{
public partial class SecondModel
{
public int SecondModelId { get; set; }
public string Comment { get; set; }
public double? Value { get; set; }
public int? ThirdModelId { get; set; }
public bool IsActive { get; set; }
public virtual ThirdModel ThirdModelVariable { get; set; }
}
}
So my issue is that I need to be able to edit the values of variables such as Comment and Value within SecondModel, but from the razor edit page that is using the model from MainModel.
I've tried a few different variation of this, with no luck.
<input asp-for="SecondModel.Comment" class="form-control" />
Usually trying something like this presents the following error:
ICollection<SecondModel> does not contain a definition for 'Comment' and no extension method 'Comment' accepting a first argument of type 'ICollection<SecondModel>' could be found
You may have noticed that there is also a reference to a ThirdModel within SecondModel. I also need to be able to reference some variables from that model within this view, but I assume the solution for one will be the solution for both in this case.
Hopefully I explained that well enough, please let me know if you need to see anymore code to reference or if I wasn't clear enough.
Thanks for your time!
When you specify to what input should be bound, you must specify valid C# accessor (behind the scene this is interpreted as lambda expression which represents access to specific property/field). Error message is telling you that ICollection<SecondModel> does not have property Comment, which is correct since SecondModel property is of type ICollection<SecondModel> and not SecondModel.
To fix your error, you have to retrieve specific element from collection first. So you can either loop the collection and generate input for every element or access specific element if you know it is there. For accessing element you have a problem with your collection type since you cannot use indexer. You should change it to IList<SecondModel>. Then you can for example access write input for comment field for first element in collection like this:
<input asp-for="SecondModel[0].Comment" class="form-control" />
And for accessing ThirdModel:
<input asp-for="SecondModel[0].ThirdModel.SomeProperty" class="form-control" />
I suggest reading Model binding to a list. It is quite old post which does not use tag helpers, but behavior is the same.
ICollection<SecondModel> does not contain a definition for 'Comment'`
The error is clear. You're looking for Comment property in a collection. It should be
#for (int i=0; i< Model.SecondModel.Count(); i++)
{
<input asp-for="SecondModel[i].Comment" class="form-control" />
// other properties
}
or use the HTML Helpers:
#for (int i=0; i< Model.SecondModel.Count(); i++)
{
#Html.TextBoxFor(m => m.SecondModel[i].Comment, new { #class = "form-control" })
// other properties
#Html.TextBoxFor(m => m.SecondModel[i].ThirdModelVariable.Property, new { #class = "form-control" })
}
For the indexers to work, you'd have to change the property from an ICollection<SecondModel> to IList<SecondModel>
To avoid this kind of confusion, whenever you have a IList<T> type property, it's better to name them as plurals. In your case:
public virtual IList<SecondModel> SecondModels { get; set; }
Now you'll know you're referring to the SecondModels property rather than keeping it SecondModel and forgetting it's a collection.
Afternoon Folks,
Im new to MVC 5 and C# and have a simple form with several fields and a dropdown box. I have used the CRUD method for entity framework and can successfully view and create new records in the system.
The only issue that I have is I have a 'Title' dropdown that links to the entity framework and populates these titles into a list. when I load my web page I can now see the titles available in the dropdown list, but when submitting the form, all the values but the 'Title' field are submitted into the database.
When I debug my program, this field regardless of what I select displays null.
I have followed the following tutorial to get this wo work and looked around the net but im struggeling to find a solution.
Link to tutorial
I have two tables in my model, one named 'Title' and the other named 'Client Record'.
As I have used the database first approach and not code first I have combined these two database models into one:
namespace EDT_Test.Models.ViewModels
{
public partial class Marie_Testing
{
[Display(Name = "Client Ref:")]
public int id { get; set; }
[Display(Name = "Created By:")]
public string CreatedBy { get; set; }
public List<Title> allTitles { get; set; }
[Required]
[Display(Name = "Surname:")]
public string Surname { get; set; }
[Display(Name = "Additional Surname:")]
public string Surname2 { get; set; }
[Required]
[Display(Name = "Forename:")]
public string Forename1 { get; set; }
[Display(Name = "Additional Forename:")]
public string Forename2 { get; set; }
The generated Entity Framework model looks like this:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace EDT_Test.Models
{
using System;
using System.Collections.Generic;
public partial class ClientRecord
{
public int id { get; set; }
public string CreatedBy { get; set; }
public string Title { get; set; }
public string Surname { get; set; }
public string Surname2 { get; set; }
public string Forename1 { get; set; }
public string Forename2 { get; set; }
}
}
The only difference for the Title field between the auto created model and Marie_Testing model is I have changed the Title field from a string to a list item.
My Create.cshtml holds a div for the Title dropdown that looks like this (This links to my model named Marie_Testing and not the auto generated ones created by the Entity Framework:
<div class="form-group">
#Html.LabelFor(model => model.allTitles, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<select id="titleid" name="titlename" class="form-control">
#foreach (var item in Model.allTitles)
{
<option value="#item.id">#item.Title1</option>
}
</select>
</div>
</div>
My code for the ClientRecordsController is:
// GET: ClientRecords/Create
public ActionResult Create()
{
////set the defaults (dropdown) of the page for the ceaton of a new record.
Marie_Testing vmPopulateData = new Marie_Testing();
List<Title> titles = (from t in db.Titles select t).ToList();
//List<Title> titles = Title.Select(t => new{t.id, t.Title}.ToString.ToList());
vmPopulateData.allTitles = titles;
return View(vmPopulateData);
}
// POST: ClientRecords/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,CreatedBy,Title,Surname,Surname2,Forename1,Forename2")] ClientRecord clientRecord)
{
if (ModelState.IsValid)
{
db.ClientRecords.Add(clientRecord);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(clientRecord);
}
Any help is much appreciated as I don't understand how can see the dropdown list in the web page but cannot seem to grab the selected value and post this to the database.
Regards
Betty B
Why not try the #Html.DropDownListFor?
Instead of this:
<select id="titleid" name="titlename" class="form-control">
#foreach (var item in Model.allTitles)
{
<option value="#item.id">#item.Title1</option>
}
</select>
try:
#Html.DropDownListFor(x => x.PropertyToBindTo, new SelectList(Model.allTitles, "id", "Title1"), new { #class = "form-control", id = "Title", name = "Title" })
Where x.PropertyToBindTo is whatever value that you need to get from that select list. Try it out... you may have to play with it a little in order to really understand how you need to work it.
You need to have a string field to hold the value of the selected from the dropdown so your view would change from
<select id="titleid" name="titlename" class="form-control">
to
<select id="Title" name="Title" class="form-control">
And you will also have a Title property on your view model like this
public string Title{get;set;}
You need to have a read on how MVC binds forms to models to get an understanding of why this happens.
Hope this helps..
well I prefer to use this
C#
List<Titles> oTitlesList = TitlesService.getTitles();
ViewBag.DropDownListBag = new SelectList(oTitlesList , "Id", "Name");
Razor
#Html.DropDownListFor(model => model.yourModelAtt, #ViewBag.DropDownListBag as SelectList, "Select.....", new { #class = "form-control" })
I wants to make a text editor for a site to allow user to write his own text or past text from another source to create pages (like wordpress for example).
To do that i chose Jquery-Te as text editor.
My problem is that when i copy/paste a text from wikipedia for example, i got a 404.15 error. I have read that i can do some changes on the RequestLimit but i can't know how long user text can be. I can set a limit to 400000 but if user enter 500000 he will get an error. Is there another way to allow user to pass a lot of text?
My second problem is Jquery-Te generates html and my text is in fact an html code. Sometimes i cant get error like "A potentially dangerous Request.Form value was detected from the client".
Can someone help me to do what i want?
I give you my code :
View :
<form action="SaveArticle" method="post">
#Html.TextBoxFor(x => x.Article.Titre)
#Html.TextBoxFor(x => x.Article.Contenu, new { #class = "editor" })
<button type="submit" class="btn btn-info btn-circle">
<i class="fa fa-search"></i>
</button>
</form>
<script>
$(".editor").jqte();
</script>
Controller :
public ActionResult GestionDesPages(GestionDesPagesViewModel gdpvm)
{
return View(gdpvm);
}
[HttpPost]
public ActionResult SaveArticle(GestionDesPagesViewModel gdpvm)
{
Articles article = gdpvm.Article;
article.Date = DateTime.Now;
article.Auteur = "Pascal";
db.Articles.Add(article);
db.SaveChanges();
return View("GestionDesPages");
}
ViewModel :
public class GestionDesPagesViewModel
{
public Articles Article{get; set;}
}
Model :
public partial class Articles
{
public int Id { get; set; }
public string Titre { get; set; }
public string Contenu { get; set; }
public string Auteur { get; set; }
public Nullable<System.DateTime> Date { get; set; }
public Nullable<bool> Actif { get; set; }
}
Sorry For Bad English.
Thanks.
EDIT :
Thank You Nava.
The solution given by Nava helped me to resolve all my problems.
I didn't said that i uses EntityFramework. To add [AllowHttp], I used the procedure defined in this theAdd annotation to Entity Framework Generated class
To allow HTML to be inputted in your form and not get the "potentially dangerous" message add the [AllowHtml] attribute to Contenu
[AllowHtml]
public string Contenu { get; set; }
Keep in mind that when you display that field, you will probably want to Html encode to prevent a Cross site scripting problem
This is the model:
public class PolicyDetail
{
public Policy Policy { get; set; }
public IEnumerable<Insured> Insured { get; set; }
public IEnumerable<Risk> Risk { get; set; }
public IEnumerable<Construction> Construction { get; set; }
}
Construction just looks like this:
public class Construction
{
public int ConstructionID { get; set; }
public string ConstructionType { get; set; }
}
And in the DB, there are only 4 rows. It's basically an enum.
And within Risk is this property:
public int ConstructionID { get; set; }
Before sending the model to the view, we fill up each object within PolicyDetail, where Insured and Risk are children of Policy. Construction is loaded up every time with all four of it's rows.
So, in the model, we are listing off all the Risks. And as we display them, we want to show Constructions as a dropdown list, and set the selected value to whatever is the value in Risk.ConstructionID.
It's amazing how much heart burn these simple dropdowns can be in MVC.
#foreach (var item in Model.Risk)
{
... Other items in Risk
<div class="form-group">
#Html.Label("Construction Type", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList(?????????)
</div>
</div>
... Other items in Risk
}
How do I fill up that drop down with all of the items from
Model.Constrution
and set the selected item to what's in
item.ConstrucitonID
?
Thanks!
EDIT:
Here's the working solution:
#Html.DropDownList("ConstructionType", new SelectList(Model.Construction.Distinct().ToList(), "ConstructionID", "ConstructionType", item.ConstructionID))
You need to convert the IEnumerable<Construction> to SelectList and specify the selected value.
Something like the following (note: I did not code this in VS, so syntax or parameters are probably not correct, but you'll get the idea)
#Html.DropDownList("ddlConstructionId", new SelectList(Model.Constrution, "ConstructionID" , "ConstructionType", item.ConstrucitonID))
You'll need to specify the selected value when creating the SelectList. I think this is the best constructor for you:
public SelectList(
IEnumerable items,
string dataValueField,
string dataTextField,
object selectedValue
)
So, in your case, it would be:
#Html.DropDownList("CompanyName",
new SelectList(Model.Construction.Distinct().ToList(), "ConstructionID", "ConstructionType", item.ConstrucitonID));