We've built a dynamic form directive that gets metadata from the server and then builds a form dynamically. The rendered inputs are bound to a Model
object separate from the metadata. In order to achieve that, we are doing something like this:
<input type="field.Type"
ng-model="Model[field.Name]"
ng-repeat="field in metadata.Fields" />
Assume that the above mark-up works (well, it does - in a simple scenario) and the binding works as expected. Unfortunately, it all breaks when the model we're using is not a collection of scalar properties. Examples include:
As you can see, the problem occurs when I have a nested property and/or when I have a list that I need to bind to, which is obviously a normal behavior because we're using the Model[propertyName]
notation to achieve the dynamic binding.
I've thought about parsing the expression coming from the server myself and walk-down the hierarchy of the model (the nested properties) and figure out the binding, but I couldn't get it right (yet). Moreover, I still have nothing in mind of hour I'm going to solve the list/array binding issue.
Any thoughts?
I'm not quite sure if your trying to get a list property from the model or if you're trying to create a Dynamic Set Model ?
As for the list property that isn't very difficult just append an array to the model or even append some object with a list property.
Multiple Models
Or are you looking to use multiple 'models'?
function MyCtrl($scope, $filter)
{
$scope.Model = {
SubModel : {
// Submodel stuff
},
FooModel : {
// Submodel stuff
}
}
}
EDIT:
I completely missed the point of the question my first try. The question really wanted to render different content based on a property's type (if that's not what you wanted I apologize) Plunker Example
HTML
<div ng-app ng-controller="MyCtrl">
<div ng-if="Configuration.hasLevels">
<div ng-repeat="lvl in Configuration.levels">
<input type="textbox" value="{{lvl.severity}}">
{{lvl.name}}
</div>
</div>
<div ng-if="!Configuration.hasLevels">
no levels: {{Configuration.levels}}
</div>
</div>
JavaScript
function MyCtrl($scope, $filter)
{
// Configuration Object
$scope.Configuration = {
debug : true,
// Log Levels
levels : [
new Level("log", 0),
new Level("warning", 1),
new Level("error", 2)
]
// levels : "hello world"
};
$scope.Configuration.hasLevels = ($scope.Configuration.levels instanceof Array);
}
function Level(name, severity) { this.name = name; this.severity = severity; }
So I ended up having to create the HTML in the JavaScript code instead of using an HTML template... Something like that:
FormBuilder.prototype.getAtomField = function (field) {
var self = this;
var atom = '<atom type="field.Type" name="field.Title" data-ng-show="field.Visible || evaluateExpression(field.VisibleIf)" hidevalidation="field.HideValidation" withlabel="field.WithLabel"'
+ 'datavalue="' + self.getDataValueBindingExpression(field.Name) + '" class="field.ClassName" required="field.Required || evaluateExpression(field.RequiredIf)"'
+ 'enabled="(field.Enabled || evaluateExpression(field.EnabledIf))" watermark="field.Watermark" orientation="field.Orientation" example="field.Example" modelkey="api.getFieldModelKey(field.Name)"'
+ 'min="field.Min" max="field.Max" description="field.Description" suggestedvalues="field.SuggestedValues" limittosuggestions="field.LimitToSuggestions" multiple="field.Multiple"'
+ 'displaypath="field.DisplayPath" valuepath="field.ValuePath" remoteurl="field.RemoteUrl" changehandler="invokeAction" changehandlerparam="field.ChangeHandler"'
+ 'source="field.Source" sourcefilter="field.SourceFilter" groupnamepath="field.GroupNamePath" disabledpath="field.DisabledPath" iconpath="field.IconPath"'
+ 'toggle="field.Toggle" formatresult="field.FormatResult" formatselection="field.FormatSelection" requestdatamapper="field.RequestDataMapper"'
+ 'responsedatamapper="field.ResponseDataMapper" pagelimit="field.PageLimit" remotedatatype="field.RemoteDataType" eagersearch="field.EagerSearch"'
+ 'numberofdigits="field.NumberOfDigits" step="field.Step" format="api.evaluateRegexExpression(field.Format, field.Type)"></atom>';
return atom;
};
Note that atom
is just a wrapper directive that is kind of equivalent to a form field. The part doing the trick here is the call to self.getDataValueBindingExpression(field.Name)
which simply returns the concatenated field name properly:
FormBuilder.prototype.getDataValueBindingExpression = function (name) {
var self = this;
return 'api.description.Model.' + name;
};
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.