I have implemented something that might help you on the most recent project I am working on. We use AngularJS on the front-end and ASP.NET WEB API on the back-end. All of the HTML forms are generated automatically based on the properties and data annotations contained in my POCO classes.
On the server-side I have entities and DTO's. My entities contain the database specific annotations and DTO's contain my view specific annotations. I will give a brief example showing one property in one class and how I render the UI for this. Here are the server-side objects:
public class Discount
{
[StringLength(40)]
[Required]
public String Name { get; set; }
}
public class DiscountDto : IDto<Discount>
{
[Display(ResourceType = typeof(ApplicationStrings), Name = "Name", ShortName = "Name_Placeholder")]
[UI(Row = 1, Width = 6)]
public String Name { get; set; }
}
This property gets rendered on the UI like so:
<div class="form-group">
<label class="col-sm-2 control-label"> Name: </label>
<div class="col-sm-6">
<input class="form-control" ng-model="model[options.key]" required="required" maxlength="40" placeholder="Enter the name...">
</div>
</div>
The <input /> field has the required, placeholder and maxlength properties auto set. The HTML label, bootstrap column widths are also auto set based on the custom UI data annotation. Row = 1 means to to display this field first in the form and Width = 6 means the field should take up a column width of 6: class="col-sm-6". The label text and placeholder text are pulled from Resource files.
If this is what you are looking for then read on :-)
I have created a Controller MetaController that takes in the name of the DTO as a parameter: api/Meta/DiscountDTO for example. This controller simply loops through all the properties on the DTO object and the associated entity and pulls out the data annotations, transforms them into a FormMetadata class and returns a List<FormMetadata> to the client. The FormMetadata class just contains properties like IsRequired, IsDisplayed, IsReadonly, etc. just to turn the annotations into something more readable for the front-end developers. Here is a snippet from the MetaController:
var type = Type.GetType("<DTO_goes_here>");
List<FormMetadata> formMetadata = new List<FormMetadata>();
foreach (var prop in type.GetProperties())
{
var metadata = new FormMetadata();
metadata.Key = prop.Name.ToLower().Substring(0, 1) + prop.Name.Substring(1, prop.Name.Length - 1);
metadata.Type = prop.PropertyType.FullName;
object[] attrs = prop.GetCustomAttributes(true);
foreach (Attribute attr in attrs)
{
if (attr is RequiredAttribute)
{
metadata.IsRequired = true;
}
else if (attr is StringLengthAttribute)
{
var sla = (attr as StringLengthAttribute);
metadata.MinLength = sla.MinimumLength;
metadata.MaxLength = sla.MaximumLength;
}
// etc.
}
formMetadata.Add(metadata);
}
This endpoint would return the following JSON for the Name property:
{
"$id":"3",
"key":"name",
"display":"Name",
"type":"System.String",
"placeholder":"Enter the name...",
"isRequired":true,
"isEditable":true,
"isDisplayed":true,
"isReadonly":false,
"displayInList":true,
"width":6,
"row":1,
"col":0,
"order":0,
"maxLength":40,
"minLength":0,
"lookup":null,
"displayAs":null
}
On the client side I have created a custom Angular directive <entity-form /> that takes in the name of the DTO as a parameter like so:
<entity-form entity-type="DiscountDTO"></entity-form>. This directive will then call the MetaController to get the validation rules for the Discount entity and render the form based on the rules returned. To render the form I use an awesome library called angular-formly. This library allows to create forms from javascript without writing any HTML. I won't get in to too much detail about angular-formly here but you basically create a Javascript object with the details of the form you want to render and pass it into an angular-formly directive and it takes care of rendering the form for you. This is a basic example of the type of object you pass to angular-formly to render an <input /> box with a label of "Text":
{
"key": "text",
"type": "input",
"templateOptions": {
"label": "Text",
"placeholder": "Type here to see the other field become enabled..."
}
}
So, I basically take the metadata returned from the MetaController and build up an object that angular-formly understands and pass it into the angular-formly directive and it renders the form for me. I know this answer could have been a LOT longer with more examples, etc. but I felt this was a lot to read through as is. I hope this gives you enough info.
I would love to make this more generic and open-source it - if anyone is interested in working on something like this let me know :-)