I have table user with fields in additional custom fields for user that added by admin.
sql click this to diagram explain this
so i want to create Register page with user fields and custom fields.
i get FiledType and FieldName to list.
in Controler:
public ActionResult Index()
{
ViewBag.CustomeFields = userCustomeFields.GetCustomeField();
return View();
}
UserField model:
public class UserField
{
public string FieldName;
public string FieldTypeName;
}
so i want to create dynamic fields in cshtml file.
i wrote this code:
Update:
#foreach (var item in ViewBag.CustomeFields)
{
<div class="form-group">
#*#Html.LabelFor(i=>item.FieldTypeName , htmlAttributes: new { #class = "control-label col-md-2" })*#
<div class="col-md-10">
#if (item.FieldTypeName == "Textbox")
{
#Html.TextBox(item.FieldName)
}
</div>
</div>
#*#Html.Editor(item.FieldName, new { htmlAttributes = new { #class = "form-control" } })*#
}
but i give error!
HtmlHelper<User>' has no applicable method named 'TextBox' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
i don't know is my way correct or not?!
Here again the answar to your first question, in case you want t mark it as a correct answer ^^
You have to cast it for example like this
#foreach(UserField item in ViewBag.CustomFields) {
or
#Html.TextBox(((UserField)item).FieldName)
or
#Html.TextBox((string)item.FieldName)
For the attributes you can use a anonymouse type object with new { ... }
#Html.TextBox("nameOfTheTexbox", "initialValue", new { #class ="myCssClass myOtherCssClass", disabled = "disabled" })
Related
The problem I will be describing is very similar to ones I already found (e.g. this post with nearly identical name) but I hope that I can make it into something that is not a duplicate.
I have created a new ASP.NET MVC 5 application in Visual Studio. Then, I defined two model classes:
public class SearchCriterionModel
{
public string Keyword { get; set; }
}
public class SearchResultModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
}
Then I created the SearchController as follows:
public class SearchController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult DisplaySearchResults()
{
var model = new List<SearchResultModel>
{
new SearchResultModel { Id=1, FirstName="Peter", Surname="Pan" },
new SearchResultModel { Id=2, FirstName="Jane", Surname="Doe" }
};
return PartialView("SearchResults", model);
}
}
as well as views Index.cshtml (strongly typed with SearchCriterionModel as model and template Edit) and SearchResults.cshtml as a partial view with model of type IEnumerable<SearchResultModel> (template List).
This is the Index view:
#model WebApplication1.Models.SearchCriterionModel
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>SearchCriterionModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Keyword, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Keyword, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Keyword, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="button" id="btnDisplaySearchResults" value="Search" onclick="location.href='#Url.Action("DisplaySearchResults", "SearchController")'" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
<div id="searchResults">
</div>
As you can see, I added a div with id="searchResults" below the standard template and edited the button. What I want is to display the partial view SearchResults.cshtml in the div on the bottom, but only after the button is clicked. I have succeeded in showing a partial view there by using #Html.Partial("SearchResults", ViewBag.MyData), but it is rendered when the parent view is loaded for the first time and I set ViewBag.MyData in the Index() method already, which is not what I want.
Summary: On clicking the button, I will obtain some List of SearchResultModel instances (via database access) and then the partial view should be rendered, using this newly obtained data as model. How can I accomplish this? I already seem fail at the first step, that is reacting to the button click with the above code. Right now, I navigate to the URL ~/Search/DisplaySearchResults, but of course there's nothing there and no code-behind method is called.
In traditional ASP.NET I'd just have added a server-side OnClick handler, set the DataSource for a grid and show the grid. But in MVC I already fail with this simple task...
Update: Changing the button to #Html.ActionLink I can finally enter the controller method. But naturally since it returns the partial view, it's displayed as the whole page content. So the question is: How do I tell the partial view to be rendered inside a specific div on the client side?
Change the button to
<button id="search">Search</button>
and add the following script
var url = '#Url.Action("DisplaySearchResults", "Search")';
$('#search').click(function() {
var keyWord = $('#Keyword').val();
$('#searchResults').load(url, { searchText: keyWord });
})
and modify the controller method to accept the search text
public ActionResult DisplaySearchResults(string searchText)
{
var model = // build list based on parameter searchText
return PartialView("SearchResults", model);
}
The jQuery .load method calls your controller method, passing the value of the search text and updates the contents of the <div> with the partial view.
Side note: The use of a <form> tag and #Html.ValidationSummary() and #Html.ValidationMessageFor() are probably not necessary here. Your never returning the Index view so ValidationSummary makes no sense and I assume you want a null search text to return all results, and in any case you do not have any validation attributes for property Keyword so there is nothing to validate.
Edit
Based on OP's comments that SearchCriterionModel will contain multiple properties with validation attributes, then the approach would be to include a submit button and handle the forms .submit() event
<input type="submit" value="Search" />
var url = '#Url.Action("DisplaySearchResults", "Search")';
$('form').submit(function() {
if (!$(this).valid()) {
return false; // prevent the ajax call if validation errors
}
var form = $(this).serialize();
$('#searchResults').load(url, form);
return false; // prevent the default submit action
})
and the controller method would be
public ActionResult DisplaySearchResults(SearchCriterionModel criteria)
{
var model = // build list based on the properties of criteria
return PartialView("SearchResults", model);
}
So here is the controller code.
public IActionResult AddURLTest()
{
return ViewComponent("AddURL");
}
You can load it using JQuery load method.
$(document).ready (function(){
$("#LoadSignIn").click(function(){
$('#UserControl').load("/Home/AddURLTest");
});
});
source code link
I'm having the following code:
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
And I'm looking for a way to add the html attribute required to it, so a user can't submit without having the field filld. But now sure how to do? I know the easies way is to add required But don't know how, i tryed with #html "reguired" Without any luck.
EDIT:
Answere = required = ""
You can add RequiredAttribute to your model property:
[Required(ErrorMessage = "Title is required")]
public string Title { get;set; }
And add ValidationMessageFor to your cshtml:
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m.Model)
Then add model validation to controller method via. It's the standard pipeline for asp.net mvc.
You also can implement your own HtmlHepler to add required attribute to your html code.
You will need this for client validation
"~/Scripts/jquery.js"
, "~/Scripts/jquery.validate.js"
,"~/Scripts/jquery.validate.unobtrusive.js"
whereas for only server side on Controller or necessary even if some disables javascript
if (ModelState.IsValid)
As above using annotation
[Required(ErrorMessage = "Title is required")]
public string Title { get;set; }
Using Fluent API
public class ClassNameConfiguration : EntityTypeConfiguration<ClassName>
{
public ClassNameConfiguration()
{
Property(x => x.Title).IsRequired();
}
}
I am trying to add a new record to a database that I've created. The database is called QUESTIONNAIRES and it has the columns: QuestionnaireUID, UserUID, QuestionnaireName, DateCreated, Link, and Image.
I want the user to specify the QuestionnaireName and provide an Image and I want to generate myself the QuestionnaireUID, UserUID, DateCreated, and Link. So far, this is my View() that represents this creation process:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
// Hide QuestionnaireUID, UserUID, and Link from user. These fields will be generated instead of assigned by user input.
#Html.HiddenFor(model => model.QuestionnaireUID)
#Html.HiddenFor(model => model.UserUID)
#Html.HiddenFor(model => model.Link)
<div class="form-group"> <!-- Questionnaire name. -->
<h2>Name</h2>
<p> Please provide a name for your decision tree.<p>
<div class="col-md-10">
#Html.EditorFor(model => model.QuestionnaireName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.QuestionnaireName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group"> <!-- Questionnaire image. -->
<h2>Image</h2>
<p> Please provide a background image for your decision tree.</p>
<!-- ADD FILE IMAGES & ENCODE IN BINARY. -->
<div class="col-md-10">
#Html.EditorFor(model => model.Image, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Image, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group btn_next"> <!-- Save and continue button. -->
<input type="submit" value="Save and Continue" class="btn">
</div>
}
The Questionnaire Controller methods that are being used are displayed below as well:
// GET: Questionnaires/Create
public ActionResult Create()
{
ViewBag.UserUID = new SelectList(db.Users, "UserUID", "FirstName");
return View();
}
// POST: Questionnaires/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "QuestionnaireUID, UserUID, QuestionnaireName, DateCreated, Link, Image")] QUESTIONNAIRE questionnaire)
{
if (ModelState.IsValid)
{
db.QUESTIONNAIRES.Add(questionnaire);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(questionnaire);
}
As you can see, I've hidden away the three attributes that I want to generate in my View(). I now don't know where I generate and assign these values. Do I do this in my View() or in the Controller method? And what does this assignment look like?
I'd generate those values in the Controller on HttpGet, and I'd use a ViewModel.
Echoing mituw16's comment, using a ViewModel is a good way of keeping everything logically consistent and separated. There's some pretty good in-depth discussions and explanations of ViewModels elsewhere that are worth reading.
Your ViewModel could, for instance, look something (roughly) like this:
public class QuestionnaireViewModel
{
public Guid QuestionnaireUID { get; set; }
public Guid UserUID { get; set; }
public string QuestionnaireName { get; set; }
public DateTime DateCreated { get; set; }
public string Link { get; set; }
public Image Image { get; set; }
}
And it could be passed to the View like this:
[HttpGet]
public ActionResult Create()
{
var vm = new QuestionnaireViewModel();
vm.QuestionnaireUID = Guid.NewGuid();
vm.UserUID = Guid.NewGuid();
return View(vm);
}
When the form is posted, MVC can automatically interpret the incoming data as a QuestionnaireViewModel:
[HttpPost]
public ActionResult Create(QuestionnaireViewModel vm)
{
if (ModelState.IsValid)
{
// map the viewmodel properties onto the domain model object here
db.SaveChanges();
return RedirectToAction("Index");
}
return View(questionnaire);
}
A couple more points:
In this example, you'll see that it may not even be necessary to include the UID stuff in the ViewModel, as the ViewModel only cares about data gathered from/presented to the user. Further, unless the #Html.HiddenFors have some sort of functional purpose on the view, you might be able to leave them out and generate them on HttpPost.
If you're looking to "create new record with a combination of values being assigned from user input and being generated in ASP.NET MVC 4" (a.k.a. creating a form in MVC), then the more complex your model/viewmodel gets, the more I'd stay away from using ViewBag for these purposes.
This is the model I'm using for a view
public class MainRegisterViewModel
{
public RegisterViewModel RegisterModel { get; set; }
public RegisterFormValuesViewModel RegisterValues { get; set; }
}
RegisterFormValuesViewModel contains all the values for the controls (list of countries, states and stuff like that) and RegisterViewModel contains the information for a user.
Then I load the controls like this.
#model ProjetX.Models.MainRegisterViewModel
#{
IEnumerable<SelectListItem> countries = Model.RegisterValues.Countries.Select(x => new SelectListItem()
{
Text = x.Country,
Value = x.Id.ToString()
});
IEnumerable<SelectListItem> states = Model.RegisterValues.States.Select(x => new SelectListItem()
{
Text = x.State,
Value = x.Id.ToString()
});
}
<div class="container">
<div class="row">
<div class="col-md-12">
#using (Html.BeginForm("Register", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
....
<div class="form-group">
#Html.LabelFor(m => m.RegisterModel.Country, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.RegisterModel.Country, countries, new { #class = "form-control" })
</div>
</div>
....
Also the Register function takes a MainRegisterViewModel as parameter.
public async Task<ActionResult> Register(MainRegisterViewModel model)
The problem is when I submit the form, RegisterFormValuesViewModel is NULL.
Any ideas why?
Thank you
Context
I'm doing this because I load the RegisterFormValuesViewModel from an API and I'm trying to call it only once. The problem was when a user POST a form with errors and you return the view back, I had to call the API again to get RegisterFormValuesViewModel.
Before this it was only one model and a viewbag for RegisterFormValuesViewModel but I had to call the API every time the form was loaded because the viewbag wasn't posted. That's why I thought I could use 2 models and POST them both.
If you want the values of RegisterFormValuesViewModel to be posted, they need to included in the form or other location that the ModelBinder looks for values. The (default) model binder will pick up values from the action params, Request.Form, route data, and Request.QueryString (I think Request.Files is included too).
If your RegisterFormValuesViewModel is expensive to create, you can add it's values as hidden fields that are posted with the form or implement a custom ValueProviderFactory that works with application or session state.
I have a class called CategoryModel, one of the properties of which is a list of objects of the same type. So CategoryModel.Categories is of type List<CategoryModel>.
On the category index page, I display an editor for each category so that the user can change any of the category names without having to go to a dedicated page to do so. Like so:
<ul id="categories>
#Html.EditorFor(model => model.Categories)
</ul>
And the editor template for CategoryModel looks like this:
<li class="folder">
#using (Html.BeginForm("Edit", "Category", new { id = Model.Key }, FormMethod.Post, new { #class = "ajaxoff"})) {
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Key)
#Html.HiddenFor(model => model.ParentKey)
#Html.HiddenFor(model => model.Sequence)
#Html.HiddenFor(model => model.IncludeDeleted)
#Html.TextBoxFor(model => model.Name, null, new { #class = "catName" })
#Html.ValidationMessageFor(model => model.Name)
<input type="submit" value="Save" class="icon save" />
}
</li>
The problem I have is that submitting the form does not bind correctly to the Edit action of the CategoryController:
[HttpPost]
public ActionResult Edit(CategoryModel category)
{
// At this point all properties in category are null
}
If I check the names on the hidden fields and textboxes, they are labelled based on their position in the current category (e.g. Categories[0].Name). If I create a dedicated edit view, however, they are simply named according to the field name (e.g. Name).
I have tried changing the controller to accept a list of Categories:
[HttpPost]
public ActionResult Edit(List<CategoryModel> categories)
{
var category = categories.First();
}
This works if I submit the very first category, but none of the others (in those cases Categories is null).
I have also tried changing how I display my EditorFor, by doing this:
<ul id="categories>
#foreach (var cat in Model.Categories)
{
#Html.EditorFor(model => cat);
}
</ul>
Which changes the field names to be the same for each category (e.g. all category names are called cat.Name), which I believe is a step in the right direction.
So how do I bind correctly to my controller? I realise that I could submit the whole parent category and then save each subcategory, but this seems like a very inefficient way to submit a single change.
I discovered how to do this. There is an overload for Html.EditorFor that allows you to specify the htmlFieldName property (the third parameter in the example below):
#foreach (var cat in Model.Categories)
{
#Html.EditorFor(model => cat, null, "");
}
This renders all of the field names without any prefix and allows me to submit any single category successfully.
Your Edit action accept CategoryModel category so you need to reset model prefix for right binding. For this you need to create your own extension method for HtmlHelper like this:
public class BeginHtmlScope : IDisposable
{
private readonly TemplateInfo templateInfo;
private readonly string previousHtmlFieldPrefix;
public BeginHtmlScope(TemplateInfo templateInfo, string htmlFieldPrefix)
{
this.templateInfo = templateInfo;
previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
}
public void Dispose()
{
templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
}
}
public static class MyHtmlExtensions
{
public static IDisposable BeginHtmlScope(this HtmlHelper html, string htmlFieldPrefix)
{
return new BeginHtmlScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
}
}
And then use it inside your editor template:
#using (Html.BeginHtmlScope(""))
{
<li class="folder">
#using (Html.BeginForm("Edit", "Category", new { id = Model.Key }, FormMethod.Post, new { #class = "ajaxoff"})) {
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Key)
#Html.HiddenFor(model => model.ParentKey)
#Html.HiddenFor(model => model.Sequence)
#Html.HiddenFor(model => model.IncludeDeleted)
#Html.TextBoxFor(model => model.Name, null, new { #class = "catName" })
#Html.ValidationMessageFor(model => model.Name)
<input type="submit" value="Save" class="icon save" />
}
</li>
}