I'm in the process of upgrading an ASP.NET WebForms application to ASP.NET MVC. One of the features on the existing application is it has a dynamic form generator. Each form has steps (using the wizard control that are dynamically added in the Wizard_Init event) and validation (using the validation controls).
I already have some static MVC forms where I use data annotation attributes on my model properties and then use the in-built unobtrusive validation to validate the form.
Now say I have the following model:
public class Form {
public string Name { get; set; }
public IList<Step> Steps { get; set; }
}
public class Step {
public string Name { get; set; }
public IList<Field> Fields { get; set; }
}
public class Field {
public string Name { get; set; }
public bool IsRequired { get; set; }
}
Obviously the fields are dynamic so I can't use the data annotation attributes. Also I only wish to validate each step. One option I thought was to render all the steps within the form and show/hide the appropriate step using javascript as they click next/previous. But again I can't see how I can add validation to this. Another option is to load in each step using Ajax but again I'm struggling with this concept.
I'd really appreciate it if someone could help. Ideally I'd like advice on the best way to achieve this rather than linking to an old third party library unless it has been built since ASP.NET MVC 3. Thanks
I think the best thing to do in your case is to create your own validation script. Its not hard, nor complex and you can address only the things necessary. So in my opinion just use js to hide, show and validate by your own script. (Just only validate the not hidden fields. )
If the fields are dynamic, doesnt mean you cannot have validation.
you can make validation dynamic, by breaking models down into smaller pieces and add validation to each smaller model.
Related
I have a details view that is typed to IEnumerable. The view with a bunch of drop downs that let you add filters to the list of records rendered.
All these dropdowns correspond to properties on the MVC model:
public class Record
{
public string CustomerNumber { get; set; }
public string CustomerName { get; set; }
public string LineOfBusiness{ get; set; }
public DateTime? Date { get; set; }
}
Now, I'm using my model as my dto to shuffle data between my controller and my repo. Since all my drop down filters represent the model properties, I pass my model to a repo retrieval method, check its properties and filter based on its values? In other words:
public IEnumerable<TradeSpendRecord> Get(TradeSpendRecord record)
{
IQueryable<tblTradeSpend> query = _context.tblRecords;
if (!String.IsNullOrEmpty(record.CustomerName))
query = query.Where(x => x.CustomerNumber == record.CustomerNumber);
if (!String.IsNullOrEmpty(record.LineOfBusiness))
query = query.Where(r => r.LOB == record.LineOfBusiness);
SNIP
Hope this isn't too subjective, but I'm wondering if anyone has any input about whether this is a good/bad practice. I haven't seen a whole lot of examples of dynamic filtering like I need to do, and am looking for some guidance.
Thanks,
Chris
If you're doing what I think you're doing, I'm not sure this is the best way of doing it.
Keep your 'Models' in your MVC/presentation layer (whether this is one physical assembly or not) dedicated to your presentation layer. The only things that should be touching them are your Views and your Controllers. You don't want what should be independent entities to be so tightly coupled to your View Models.
I'd suggest creating a separate TradeSpendFilter class, which, at its simplest, exposes the filterable properties of your domain entity (likely more than any given View Model). You'd then pass this into your "filtering service" or whatever it may be. This also means you can extend your filtering functionality independent of both your domain models and your MVC app. For example, if you suddenly want to filter multiple objects, you can simply change...
public class TradeSpendFilter
{
public string CustomerName { get; set; }
...
}
...to...
public class TradeSpendFilter
{
public IEnumerable<string> CustomerNames { get; set; }
...
}
... without causing all sorts of problems for your MVC app.
Additionally, it will also mean you can make use of your filtering functionality elsewhere, without tying further components to your MVC app and ending up in a bootstrapped mess.
I am a long time backend .Net developer who is new to web application development. I am using MVC 4, Razor, EF 5, and I have a basic understanding on how to make a routine DB driven MVC 4 site with these tools.
I need to create a custom form capability for a workflow scenario. I have the entire code-first schema designed and classes for different formfield types, and formvalue type.
The model that will be passed to a view will be a form class with a list of formvalues, which contain form field specifications. So the view will have to iterate through the form fields and dynamically choose what editors to use and so on.
My problem is that this prevents me from using any data annotation for client side validation. And every place I find advice on custom validation assumes (not surprisingly) that all the fields are members of a class. Whereas, in this case, all the fields are on a list.
My goal is to end up with validation messages on each field and a validation summary as you would normally have if the view was simply bound to an annotated class.
Any suggestions on how to approach this? Thanks in advance.
Imagine view logic that has the following:
#foreach (var fieldvalue in Model.FormFieldValues) {
// Logic that chooses the appropriate editor based on typeof(fieldvalue.FormField)
// Binds the editor to fieldvalue.Value
// Validation specs are contained in the Formfield obtained by fieldValue.FormField
// And my problem is setting up the validation without the aid of the normal method
// of data annotation or class level custom validation.
}
and the fieldvalue class looks like this:
public class FormFieldValue : EboEntity
{
public string Value { get; set; }
[Required]
public int FormFieldId { get; set; }
[Required]
public virtual FormField FormField { get; set; }
[Required]
public int FormId { get; set; }
[Required]
public virtual Form Form { get; set; }
}
And imagine that the FormField object has fields such as Required, Maxlength, and so on, depending on the kind of field it is (i. e. FormFieldText would be a subclass of FormField)
ANSWER:
Ok, you can tell I am new at MVC.
The answer to my question is that the specific editors take htmlAttributes which can control validation. So in the case where one of my formfields is a required textfield of stringlenth(10), I can invoke the editor as follows:
<div class="editor-field">
#Html.TextBoxFor(model => model.NoTexty, new {
required = true,
maxlength = 10,
placeholder = "Texty"})
#Html.ValidationMessageFor(model => model.NoTexty)
</div>
but in my case the htmlAddtributes won't be hard coded, but rather they will come from the fields in the formvalue.FormField object.
Sorry if I wasted anyone's time on this. But I will leave this here for any other newbies like me.
I have a class:
class Item
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
I have a view where I want objects for the class above created using inputs (so I have a textbox for Name and a date select type thing for Date). However, I want thev user to be able to click a link and through jquery/javascript another textbox and date select will be added to the form, and this can happen unlimited times.
How can I bind this to a model so that I can return it to my action method? Ideally the model would be something like:
class MyModel
{
public string AProperty { get; set; }
public List<Item> Items { get; set; }
}
Apologies for the poor wording, struggling to describe what I want but I think this should get the point across.
You want to use a client-side template and then return JSON to your controller. If you are using MVC 3, JSON model binding is built-in, but in MVC 2 you need to set up your own binder. There is an example here.
I recommend using KnockoutJS for your client side. It's very simple for working with dynamic collections and very well documented. You can see an example similar to what you're trying to do here as well as in the previous link.
I have a model that looks like this:
public class Book
{
public string Name { get; set; }
public IEnumerable<Author> Authors { get; set; }
}
public class Author
{
public string FullName { get; set; }
public DateTime BirthDate { get; set; }
}
I have a form in a view for editing a book. The section for editing the Authors collection is in a partial view. The form fields are generated with the Html.EditorFor() method.
It works well for editing existing data. What I would like to do is to put in the Authors editing partial view multiple blank entries that if the user fills them they will be added as new items to the Authors collection.
The final view should look something like this:
http://s1.postimage.org/6g9rqfp20/image.jpg
What is the correct way to achieve this behavior?
If you are using MVC2 this is your best bet
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx#related-results
I am not sure how interested you are in using javascript libraries to get what you are looking to get done, but here is a great example of what you are trying to do: Contact Editor Example
It uses the knockouts library which allows you to work with JavaScript data binding. This also gives you a nice thick application feel on the web which users generally like.
If you are still curious about how this works with serverside, you can look at this presentation from Mix11
Good luck.
For a given report, the user will want to have multiple filtering options. This isn't bad when the options are enumerations, and other 'static' data types, however things can get silly fast when you need a select list that is populated by fields stored in a table in the backend.
How do you handle this scenario? I find myself constantly reshaping the View data to accommodate the additional filter fields, but it really is starting to be a bit much tracking not only the selected options, but also the options themselves...
is there not a better way?
I’m currently building out a new reporting section for one of our products at work and am dealing with this same issue. The solution I’ve come up with so far, though it hasn’t been implemented yet so this is still a work in progress, is along the lines of this.
There will be a class that will represent a report filter which will contain some basic info such as the label text and a list of option values.
public enum DisplayStyle
{
DropDown,
ListBox,
RadioList,
CheckList,
TextBox
}
public class FilterOption
{
public string Name { get; set; }
public string Value { get; set; }
public bool Selected { get; set; }
}
public class ReportFilter
{
public string Title { get; set; }
public DisplayStyle Style { get; set; }
public List<FilterOption> Options { get; set; }
}
And then my model will contain a list of these option classes that will be generated based on each report’s needs. I also have a base report class that each report will inherit from so that way I can handle building out the option lists on a per report basis and use one view to handle them all.
public class ReportModel
{
public string Name { get; set; }
public List<ReportFilter> Filters { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
Then inside my view(s) I’ll have some helper methods that will take in those option classes and build out the actual controls for me.
public static string ReportFilter(this HtmlHelper htmlHelper, DisplayStyle displayStyle, FilterOption filterOption)
{
switch (displayStyle)
{
case DisplayStyle.TextBox:
return string.Format("<input type=\"text\"{0}>", filterOption.Selected ? (" value=\"" + filterOption.Value + "\"") : string.Empty);
break;
...
}
}
My route would look like this
Reports/{reportID}/start/{startDate}/end/{endDate}/{*pathInfo}
All reports have a start and end date and then optional filters. The catchall parameter will have lists of filter values in the form of “Customer/1,4,7/Program/45,783”. So it’ll be like a key/value pair in list form. Then when the controller loads it’ll parse out those values into something more meaningful.
public static Dictionary<string, string> RouteParams(string pathInfo)
{
if (string.IsNullOrEmpty(pathInfo))
{
return new Dictionary<string, string>();
}
var values = new Dictionary<string, string>();
// split out params and add to the dictionary object
return values;
}
Then it will pass them off to the report class and validate them to make sure they’re correct for that report. Then when the options are loaded for that report anything that’s been set in the URL will be set to Selected in the ReportOption class so their state can be maintained. Then the filter list and other report data will be added to the model.
For my setup some filters will change when another filters selection changes so there will be some AJAX in here to post the data and get the updated filter options. The drilldown will work sort of like the search options at amazon or newegg when you narrow your search criteria.
I hope that all makes sense to someone beside me. And if anyone has some input on improving it I’d be happy to hear it.
You could go and retrieve the data asynchronously on the screen using jQuery and JsonResults from your MVC application, this is how we populate all of our lists and searches in our applications. I have an example of how it is done here.
This way the view data is loaded on demand, if they don't use the extra filters then they don't have to get the view data and if one selection relates to another then it's clear which set of data you need to retrieve.
Another option, though I don't like this one as much but jQuery solution may not suit you, is to have your model object for your view contain all the view data so that all you need to do is set the single model object and all the lists are loaded directly and strongly typed. This will simplify the view and the back end code because it will be more clear that for this view the only thing you need is a complete version of this model object.
For example if you had two lists for combo boxes then your model might look like:
public class MyViewMode
{
public int MyProperty { get; set; }
public string SomeString { get; set; }
List<string> ComboListA { get; set; }
List<string> ComboListB { get; set; }
}
Hope that makes sense, if not please comment and I'll expand on it.
Ad-hoc filtering on reports is indeed a tricky issue especially when you want to show a custom user interface control based on the data type, do validation, make some filters to be dependent on one another and others not, etc.
One thing I think that is worth considering is the old "build vs buy" issue here. There are specialized tools out there for ad-hoc reporting that do provide a UI for ad-hoc filters help with this such as the usual suspects Crystal Reports, Microsoft's Reporting Services, or our product ActiveReports Server. In ActiveReports Server we support cascading prompts (where available values in prompts depend on one another) and make it easy for anyone, even non-technical business users to modify the prompts (assuming they have permissions obviously). More information about using prompts in ActiveReports Server is here. ActiveReports Server is also, all managed .NET code, and provides ASP.NET controls and web services that allows you to integrate it into your web apps.
Scott Willeke
Product Manager - ActiveReports Server
GrapeCity inc.