我正在尝试使用Angular-Formly从一组.NET类开始动态构建表单。
我在json中序列化类属性信息并将其返回给Formly,但是没有显示任何字段。
我按照以下建议查找:How to load angular-formly vm.fields object from remotely-generated json? 但似乎不适合我。
我的表单代码是:
<form ng-submit="vm.onSubmit()" novalidate>
<formly-form model="vm.model" fields="vm.formFields">
<button type="submit" class="btn btn-primary submit-button">Submit</button>
</formly-form>
</form>
角度代码是:
<script>
/* global angular */
(function () {
'use strict';
var app = angular.module('formlyExample', ['formly', 'formlyBootstrap'], function config(formlyConfigProvider) {
// set templates here
//formlyConfigProvider.setType({
// name: 'custom',
// templateUrl: 'custom.html'
//});
});
app.factory('User', function ($http) {
return {
getFields: getFields
};
function getFields() {
return $http.post('TestFormly.aspx/LoadData', { headers: { 'Cache-Control': 'no-cache' } });
}
});
app.controller('MainCtrl', function MainCtrl($scope, $http, User) {
var vm = this;
// funcation assignment
vm.onSubmit = onSubmit;
vm.loadingData = User.getFields().then(function (result) {
vm.fields = JSON.parse(result.data.d);
vm.originalFields = angular.copy(vm.fields);
});
vm.model = {
};
// function definition
function onSubmit() {
alert(JSON.stringify(vm.model), null, 2);
}
});
})();
</script>
CSharp.Net代码是:
[WebMethod]
public static string LoadData()
{
string retValue = null;
List<FieldItem> m_fields = new List<FieldItem>();
FieldItem item1 = new FieldItem();
item1.key = "text";
item1.type = "input";
item1.templateOptions = new TemplateOptions() { label = "Text", placeholder = "Formly is terrific!" };
FieldItem item2 = new FieldItem();
item2.key = "story";
item2.type = "textarea";
item2.templateOptions = new TemplateOptions() { label = "Some sweet story", placeholder = "It allows you to build and maintain your forms with the ease of JavaScript :-)" };
m_fields.Add(item1);
m_fields.Add(item2);
retValue = JsonConvert.SerializeObject(m_fields);
return retValue;
}
JSON结果是:
[
{
"key":"text",
"type":"input",
"templateOptions":{
"label":"Text",
"placeholder":"Formly is terrific!"
}
},
{
"key":"story",
"type":"textarea",
"templateOptions":{
"label":"Some sweet story",
"placeholder":"It allows you to build and maintain your forms with the ease of JavaScript :-)"
}
}
]
使用firebug进行调试我看到JSON正确传递给了vm.fields,但没有显示输入框,只显示了Sumbit按钮。
我注意到,Formly example也没有显示字段。
你能帮忙吗?
提前致谢, 朱塞佩。
答案 0 :(得分:1)
显然,示例中使用的Angular-Formly(6.0.0-beta.1)版本是抛出异常。我记得以前这个工作。无论如何我将它恢复到一个稳定的版本并再次运行。
这是jsbin与你的形式json正常工作: http://jsbin.com/towozegiqu/edit
答案 1 :(得分:1)
这是一个我很快被黑客攻击的解决方案,作为我自己的概念证明。
基本上,FormlyModelBuilder会扫描ViewModel类并构建一个形式上的字段模型。
样本用法
public IActionResult Index()
{
return Ok(new ViewModels.Account.FormlyModelBuilder<ViewModels.Account.RegisterViewModel>().JsonStringify(new ViewModels.Account.RegisterViewModel { Email = "test@test.com" }));
}
转换此
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare ("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
进入这个
{
"fields": [{
"key": "Email",
"type": "email",
"templateOptions": {
"isRequired": false,
"label": "Email"
},
"expressionProperties": {
"templateOptions.focus": "Email"
}
}, {
"key": "Password",
"type": "password",
"templateOptions": {
"isRequired": false,
"label": "Password"
},
"expressionProperties": {}
}, {
"key": "ConfirmPassword",
"type": "password",
"templateOptions": {
"label": "Confirm password"
},
"expressionProperties": {}
}],
"model": {
"email": "test@test.com"
},
"expressionProperties": {}
}
源代码
public class FormlyModelBuilder<T>
{
internal T GetAttributeFrom<T>(object instance, string propertyName) where T : Attribute
{
var property = instance.GetType().GetProperty(propertyName);
return GetAttributeFrom<T>(property);
}
internal T GetAttributeFrom<T>(PropertyInfo property) where T : Attribute
{
var attrType = typeof(T);
T t = (T)property.GetCustomAttributes(attrType, false).FirstOrDefault();
if (t == null)
{
var metaAttr = (MetadataTypeAttribute[])property.ReflectedType.GetCustomAttributes(typeof(MetadataTypeAttribute), true);
if (metaAttr.Length > 0)
{
foreach (MetadataTypeAttribute attr in metaAttr)
{
var subType = attr.MetadataClassType;
var pi = subType.GetField(property.Name);
if (pi != null)
{
t = (T)pi.GetCustomAttributes(attrType, false).FirstOrDefault();
return t;
}
}
}
}
else
{
return t;
}
return null;
}
internal FormlyModel<T> Build(T dataModel)
{
if (dataModel == null) throw new ArgumentNullException(nameof(dataModel));
//
var modelType = typeof(T);
var model = new FormlyModel<T>(dataModel);
foreach (var property in modelType.GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty | BindingFlags.Public))
{
var type = GetAttributeFrom<DataTypeAttribute>(property);
var field = new FormlyField(property.Name, GetInputTypeFromDataType(type?.DataType, property.PropertyType));
model.AddField(field);
//
var display = GetAttributeFrom<DisplayAttribute>(property);
field.TemplateOptions.Label = display?.Name;
//
var required = GetAttributeFrom<RequiredAttribute>(property);
field.TemplateOptions.IsRequired = required?.AllowEmptyStrings;
//
}
var focusField = model.Fields.First();
focusField.ExpressionProperties["templateOptions.focus"] = focusField.Key;
return model;
}
internal string JsonStringify(T dataModel)
{
if (dataModel == null) throw new ArgumentNullException(nameof(dataModel));
//
var dcr = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
dcr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic;
//
return Newtonsoft.Json.JsonConvert.SerializeObject(Build(dataModel),
new Newtonsoft.Json.JsonSerializerSettings
{
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
ContractResolver = dcr
});
}
private string GetInputTypeFromDataType(DataType? dataType, Type propertyType)
{
if (dataType != null)
{
//
switch (dataType)
{
case DataType.Text:
return "input";
case DataType.Password:
return "password";
case DataType.EmailAddress:
return "email";
case DataType.Html:
case DataType.MultilineText:
case DataType.Custom:
case DataType.DateTime:
case DataType.Date:
case DataType.Time:
case DataType.Duration:
case DataType.PhoneNumber:
case DataType.Currency:
case DataType.Url:
case DataType.ImageUrl:
case DataType.CreditCard:
case DataType.PostalCode:
case DataType.Upload:
default:
break;
}
}
switch (propertyType.Name)
{
case nameof(System.Boolean):
return "checkbox";
default:
return "input";
}
}
}
internal class FormlyModel<T>
{
internal FormlyModel(T dataModel)
{
if (dataModel == null) throw new ArgumentNullException(nameof(dataModel));
//
this.Fields = new List<FormlyField>();
this.Model = dataModel;
this.ExpressionProperties = new Dictionary<string, string>();
}
internal IEnumerable<FormlyField> Fields { get; }
internal T Model { get; }
internal Dictionary<string, string> ExpressionProperties { get; }
internal void AddField(FormlyField field)
{
if (field == null) new ArgumentNullException(nameof(field));
//
((List<FormlyField>)this.Fields).Add(field);
}
}
internal class FormlyField
{
internal FormlyField(string key, string type)
{
if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key));
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentNullException(nameof(type));
//
TemplateOptions = new TemplateOptions();
ExpressionProperties = new Dictionary<string, string>();
Key = key;
Type = type;
}
internal string Key { get; }
internal string Type { get; }
internal string HideExpression { get; set; }
internal TemplateOptions TemplateOptions { get; }
internal Dictionary<string, string> ExpressionProperties { get; }
}
internal class TemplateOptions
{
public bool? IsRequired { get; set; }
public string Label { get; set; }
public string Placeholder { get; set; }
}