I'm building a driver's application for employment and I'd like the list of 'accidents' to be dynamic in the sense of only one field appears, but they can add however many they need and once added they appear in a table above the input..
I'm trying to find out what the best way to do this is in a MVC3 Form that is using Razor syntax?
Model has a declaration similar to...
public class FormModel {
...other properties
public IEnumerable<AccidentDetail> AccidentDetails { get; set; }
...other properties
}
This is very much shortened, but wanted to ensure that I was declaring it properly in the model and then I need to know how it's generally handled in the Form to do something like this. I can't make any assumptions to the existence of an accident or the limit of how many they may have.
I personally would approach this with some JavaScript. In particular, I'd use jQuery to add a new field to the table when a button is clicked to "Add another accident". You can use jQuery to grab the values of the fields into an array. The array of accidents would be serialized to json and stored in a hidden field on the form that would be submitted to the server. In your controller you could deserialize that string of json to your strong type. On the server side I've used Json.NET with great success. On the client side you can use Doug Crockford's json2.js. Feel free to email me if you need a little bigger push in the right direction.
Related
Say I have a controller CatController with actions for GET, POST and PUT. They all use the same Cat resource which could look like this:
public class CatDto {
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public bool IsFriendly {get; set; }
}
However, the Name and IsFriendly properties should only be required when creating a new cat (POST), but optional when updating it (PUT) to allow updating only a single property.
The way I've handled this until now is simply having two classes, a CreateCat and UpdateCat which have the same properties but different data annotations. However I don't want to have to maintain two almost identical classes.
I could of course validate the model manually in each action, but data annotations are very useful for things like global model validators and automatic generation of Swagger schemas.
I'm also using the Swagger schema to automatically generate SDK's (using ApiMatic), and that results in having two duplicate classes generated (CreateCat and UpdateCat) for what really should only be a single resource (Cat).
Is there an alternative way to achieve what I'm trying to do with only a single class?
I prefer to keep separate models to be honest.
You could have a base abstract ( or not ) model with all the common properties although this is not required and simply adds a third class. Is there a need for it? I'd say no.
There are slight differences between POST and PUT.Neither POST nor PUT require the Id property if you already have that in the PUT endpoint. This negates the need of checking if that Id in URL matches the Id in the model.
Your example does not make the difference visible, but in many cases there are fields you don't really want to update. For example let's say you have a Created and Updated date fields, you would not want to change your Created date via a PUT for example. The more data you have that you do not want to update via a PUT, the more obvious and worthwhile the differences between the models become.
In your case even with those 2 properties I would still create 2 different models, even if they are virtually the same, this sets the expectation on how the API works and creates a clear design in the mind of everyone else who is working on it.
I would recommend against the design you are asking for. According to RFC [RFC7231] you can find here it is advised not to have partial content update in PUT methods.
"An origin server that allows PUT on a given target resource MUST send
a 400 (Bad Request) response to a PUT request that contains a
Content-Range header field (Section 4.2 of [RFC7233]), since the
payload is likely to be partial content that has been mistakenly PUT
as a full representation. Partial content updates are possible by
targeting a separately identified resource with state that overlaps a
portion of the larger resource, or by using a different method that
has been specifically defined for partial updates (for example, the
PATCH method defined in [RFC5789])."
The preferred solution is to use PATCH method instead of PUT. Patch method is described in the RFC in the this link. PATCH method were introduced for partial resource modifications
So look up PATCH methods or if you want to use PUT maybe have a separate endpoint that only takes one of the two values.
More information about PATCH method can be found here
So either go for a PATCH method or create different models and end point to cater for partial update using PUT.
I want to write a form to submit data about people that could have a greatly varying amount of fields, it is getting this data from an eav database. My idea was to write a model with a Dictionary of parameters. Something like
public class MyModel
{
public Dictionary<string,string> Parameters {get;set;}
}
Where the key would be the parameter name and the value would be the value. Then in the view I would just create a Html.TextBoxFor(...) each parameter in the dictionary and submit the form. The model would also handle stuff like populating the dictionary etc.
Another intern told me I should just use json and ajax/angularjs. The form isn't smart at all, just a big list of fields you have to fill in then submit.
Is there a downside to doing it just in razor and server code in this case?
If you are just editing preexisting data, you don't have to add any new data, then doing it with pure razor should be just fine, check this question, I think it will help you. However if you have dynamically add or delete, some pairs then this approach will not be enough and you should consider listening to the other intern.
I have an MVC 5 project and part of that is a registration. Presently the form submits using the standard form submit action and my model binder does its job gloriously and all is good.
However, I now need to add a dynamic element to my form such that a user needs to be able to add on the fly invitations to add team members via a textbox for an email address and a dropdown list for that team members role... and an add another button to add another textbox and dropdown list for the next team member... and the next... ad infinitum...
Upon form submission, my model binder is going to fall over and not know what to do with these dynamic elements, so I need suggestions for how to get my model binder to deserialize these items into an array of email & role (role binds to an enum).
I've had it suggested that client side script can catch the submission and serialize the items to a text string and put that in a hidden form field that I can then deserialize manually on my controller action, which seems like it'll work okay, but it feels dirty.
What is the most straightforward way of achieving this? Is there anything built into .NET that will automatically handle this somehow if I configure my view correctly?
Your model can store your "dynamic" fields in a collection like:
public List<string> Emails { get; set; }
In your view you just create subsequent indexed names (exact naming depends on how you're generating this new fields):
model.Emails[0] // name="Emails[0]"
model.Emails[1] // name="Emails[1]"
In your controller action, there should be nothing special the model binder has to do. It will easily be able to handle the collections.
There is an old article from Phil Haack about binding to a List:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
I guess you could add a ICollection<Invitation>-Property to your model and set the Name of the form fields as explained in the article.
Although personally I would bind the data to a JavaScript-ViewModel and post JSON back to the Server using Ajax.
Upon clicking the "Add more" button, simply inject the textbox and dropdown and let the form serialize like usual.
On the server, accept a list of invitations:
public class Invitation
{
public string EmailAddresss {get; set;}
public int InvitationType {get; set; }
}
public ActionResult Register(MyCurrentModel dto, Invitation[] invitations)
{
//..
}
One of the key features of a project I'm working on is the ability for the user to configure Forms (as in "Forms" to fill-up) based on a pool of pre-existing field types (well known types, for instance "user name", "date of birth" etc. but also "generic types" like "string", "DateTime" etc.).
We used to have a static ViewModel that worked fine for the "well known" types and looked like this:
public class UserInputModel
{
[StringLength(200)]
public string Name { get; set; }
[Required(ErrorMessageResourceName = "BirthDateEmptyError", ErrorMessageResourceType = typeof(Resources.ErrorMessages))]
public DateTime BirthDate { get; set; }
//Here comes a lot of other properties
}
All the known properties were listed and we were showing or hiding them given the context.
But the last requirement came and changed all that. The user shall now be able to add as many generic type fields as he wants. In order to do this, we decided to make this InputModel entirely dynamic. It now looks like this:
public class UserInputModel
{
// Each ModelProperty has an "Id" and a "Value" property
public ICollection<ModelProperty> Properties { get; set; }
}
This works like a charm. The razor view only has to iterates over the collection, create the corresponding controls for each property of the collection in a more than standard way:
#Html.TextBoxFor(m => m.Properties[index].Value);
... and we nicely get the data back as a filled form.
=> This works fine, but we don't have any client-side validation. For this, we would need some Metadata... which we don't have via annotations anymore since we're dynamically creating the model.
In order to provide those MetaData, I created a CustomModelMetadataProvider that inherits from DataAnnotationsModelMetadataProvider and registered it as the new ModelMetadataProvider in the Global.asax. The CreateMetadata() function gets called upon creation of the ViewModel, and that for each of the properties of my ViewModel... sofar so good.
Where the problem starts: in order to add some metadata to the current property, I first need to identify which property I am currently looking at ("Name" has a maxlength of 200, "date of birth" hasn't so I cannot assign a maxlength to every property per default). And somewhow I didn't manage to do that yet since all the properties have the same name Value and the same container type ModelProperty.
I tried accessing the container of the property via reflection, but since the ModelAccessor's target is the ViewModel itself (because of the lambda expression m => m.Properties), the following construct gives me the ViewModel as a whole, not just the ModelProperty:
var container = modelAccessor.Target.GetType().GetField("container");
var containerObject = (UserInputModel)container.GetValue(modelAccessor.Target);
I've been flipping this over and over but cannot find a way to identify which ModelProperty I have in hand. Is there a way to do this?
Update: after flipping this in every possible direction for a while, we finally went another way. We are basically using unobstrusive javascript to use MVC's validation capabilities without touching attributes nor metadata. In short, we add HTML attributes like value-data="true" (and all other required attributes) to the #Html.TextBoxFor() statements. This works wonderfully for all the atomic validations (required, stringlength etc.).
Tim, you can leverage what appears to be client-side validation through Ajax with the Remote attribute on your properties.
Basically, you'll need to set up a validation controller and then write some smarts into that controller. But at least you'd be able to write some helper methods and keep it all in one place. You would have a series of validators, based on the meta data that you are presenting to the end users, and each validator method would work for a particular type with good re-use.
The one pitfall to this approach would be that you would need to write a validation method for each type and condition that you want to support. Sounds like you're having to go down that road anyways, though.
Hope this helps.
See if this article help you: Technique for carrying metadata to View Models with AutoMapper.
Also use this one for ideas (custom model metadata provider): changing viewmodel's MetadataType attribute at runtime
Fluent validation is probably the best option for you in my mind, but its obviously up to you to select the best match among those above.
Update
Try use ModelMetadata and override ModelMetadataProvider: Dive Deep Into MVC: ModelMetadata and ModelMetadataProvider. This way you completely customize your model metadata (this replaces data annotations) and you have complete control on what is happening, rather than relying on ASP.NET MVC.
Another good place to look at it is Creating your own ModelMetadataProvider to handle custom attributes.
Hope this all is of help to you.
I am tacking a large refactor of a project, and I had asked this question to confirm/understand the direction I should go in and I think I got the answer that I wanted, which is not to throw away years worth of code. So, now begins the challenge of refactoring the code. I've been reading Martine Fowler and Martin Feathers' books, and they have a lot of insight, but I am looking for advice on the ultimate goal of where I want the application to be.
So to reiterate the application a little bit, its a dynamic forms system, with lots of validation logic and data logic between the fields. The main record that gets inserted is the set of form fields that is on the page. Another part of it is 'Actions' that you can do for a person. These 'Actions' can differ client by client, and there are hundreds of 'Actions'. There is also talk that we can somehow make an engine that can eventually take on other similar areas, where a 'person' can be something else (such as student, or employee). So I want to build something very de-coupled. We have one codebase, but different DBs for different clients. The set of form fields on the page are dynamic, but the DB is not - it is translated into the specific DB table via stored procs. So, the generic set of fields are sent to the stored proc and the stored proc then decides what to do with the fields (figure out which table it needs to go to). These tables in fact are pretty static, meaning that they are not really dynamic, and there is a certain structure to it.
What I'm struggling specifically is how to setup a good way to do the dynamic form control page. It seems majority of the logic will be in code on the UI/aspx.cs page, because its loading controls onto the webpage. Is there some way I can do this, so it is done in a streamlined fashion, so the aspx.cs page isn't 5000 lines long? I have a 'FORM' object, and one of the properties is its' 'FIELDS'. So this object is loaded up in the business layer and the Data layer, but now on the fron end, it has to loop through the FIELDS and output the controls onto the page. Also, someway to be able to control the placement would be useful, too - not sure how do get that into this model....
Also, from another point of view - how can I 'really' get this into an object-oriented-structure? Because technically, they can create forms of anything. And those form fields can represent any object. So, for example, today they can create a set of form fields, that represent a 'person' - tomorrow they can create a set of form fields that represent a 'furniture'. How can I possibly translate this to to a person or a furniture object (or should I even be trying to?). And I don't really have controls over the form fields, because they can create whatever....
Any thought process would be really helpful - thanks!
How can I possibly translate this to to a person or a furniture object
(or should I even be trying to?)
If I understand you correctly, you probably shouldn't try to convert these fields to specific objects since the nature of your application is so dynamic. If the stored procedures are capable of figuring out which combination of fields belongs to which tables, then great.
If you can change the DB schema, I would suggest coming up with something much more dynamic. Rather than have a single table for each type of dynamic object, I would create the following schema:
Object {
ID
Name
... (clientID, etc.) ...
}
Property {
ID
ObjectID
Name
DBType (int, string, object-id, etc.)
FormType ( textbox, checkbox, etc.)
[FormValidationRegex] <== optional, could be used by field controls
Value
}
If you can't change the database schema, you can still apply the following to the old system using the stored procedures and fixed tables:
Then when you read in a specific object from the database, you can loop through each of the properties and get the form type and simple add the appropriate generic form type to the page:
foreach(Property p in Object.Properties)
{
switch(p.FormType)
{
case FormType.CheckBox:
PageForm.AddField(new CheckboxFormField(p.Name, p.Value));
break;
case FormType.Email:
PageForm.AddField(new EmailFormField(p.Name, p.Value));
break;
case FormType.etc:
...
break;
}
}
Of course, I threw in a PageForm object, as well as CheckboxFormField and EmailFormField objects. The PageForm object could simply be a placeholder, and the CheckboxFormField and EmailFormField could be UserControls or ServerControls.
I would not recommend trying to control placement. Just list off each field one by one vertically. This is becoming more and more popular anyway, even with static forms who's layout can be controlled completely. Most signup forms, for example, follow this convention.
I hope that helps. If I understood your question wrong, or if you'd like further explanations, let me know.
Not sure I understand the question. But there's two toolboxes suitable for writing generic code. It's generics, and it's reflection - typically in combination.
I don't think I really understand what you're trying to do, but a method using relfection to identify all the properties of an object might look like this:
using System.Reflection;
(...)
public void VisitProperties(object subject)
{
Type subjectType = subject.GetType();
foreach (PropertyInfo info in subjectType.GetProperties()
{
object value = info.GetValue(subject, null);
Console.WriteLine("The name of the property is " + info.Name);
Console.WriteLine("The value is " + value.ToString());
}
}
You can also check out an entry on my blog where I discuss using attributes on objects in conjunction with reflection. It's actually discussing how this can be utilized to write generic UI. Not exactly what you want, but at least the same principles could be used.
http://codepatrol.wordpress.com/2011/08/19/129/
This means that you could create your own custom attributes, or use those that already exists within the .NET framework already, to describe your types. Attributes to specify rules for validation, field label, even field placement could be used.
public class Person
{
[FieldLabel("First name")]
[ValidationRules(Rules.NotEmpty | Rules.OnlyCharacters)]
[FormColumn(1)]
[FormRow(1)]
public string FirstName{get;set;}
[FieldLabel("Last name")]
[ValidationRules(Rules.NotEmpty | Rules.OnlyCharacters)]
[FormColumn(2)]
[FormRow(1)]
public string LastName{get;set;}
}
Then you'd use the method described in my blog to identify these attributes and take the apropriate action - e.g. placing them in the proper row, giving the correct label, and so forth. I won't propose how to solve these things, but at least reflection is a great and simple tool to get descriptive information about an unknown type.
I found xml invaluable for this same situation. You can build an object graph in your code to represent the form easily enough. This object graph can again be loaded/saved from a db easily.
You can turn your object graph into xml & use xslt to generate the html for display. You now also have the benefit of customising this transform for differnetn clients/versions/etc. I also store the xml in the database for performance & to give me a publish function.
You need some specific code to deal with the incoming data, as you're going to be accessing the raw request post. You need to validate the incoming data against what you think you was shown. That stops people spoofing/meddling with your forms.
I hope that all makes sense.