javascript - 如何从服务器加载 angular-formly vm.fields

标签 javascript json angularjs forms angular-formly

我正在尝试使用 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>

Angular 代码是:

<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显示字段。

你能帮忙吗?

提前致谢, 朱塞佩。

最佳答案

这是一个我很快破解的解决方案,作为我自己的概念证明。

基本上,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; }
    }

关于javascript - 如何从服务器加载 angular-formly vm.fields,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32268179/

相关文章:

javascript - 使用 ng-click 和函数设置变量之间的 Angular 差异

javascript - 阶段 0 与阶段 4 TC39 进程 javascript

javascript - 自动换行功能在 chrome 中不起作用

javascript - 如何保持标题静态,滚动时始终位于顶部?

android - 没有收到完整的 JSON

javascript - 数据更改时 ng 类不刷新

javascript - 当 Polymer 元素中的 bool 属性发生更改时,如何重新运行 dom-repeat 和 sort

json - 在黑莓操作系统 5.0 上启动标签 : Module 'net_rim_json_org' not found. 时出错(在操作系统 7 上运行正常)

json - ColdFusion:如何检查JSON属性是否为null?

angularjs - 每次我展示它们时如何强制 Angular2 重新创建组件