使用Angular进行基于ViewModel的验证

时间:2015-10-30 04:47:34

标签: angularjs asp.net-mvc model-view-controller mvvm asp.net-core-mvc

我想有一个包含验证规则的JavaScript视图模型对象,类似于带有数据注释的ASP.NET MVC视图模型,我可以绑定到Angular视图。然后,我会...喜欢在该对象的生命周期的某些阶段调用Validate方法,然后将其发布到服务器。

这将允许我接近类似MVVM的方法,其中UI非常轻且不太聪明,并且视图所代表的视图模型(无论视图模型)需要更改视图标记以更改验证规则。

这样我甚至可以利用MVC的数据注释在服务器上构建一个带有规则的可序列化视图模型,并将该模型及其所有验证等返回给客户端作为JSON

我怎样才能在Angular中实现这种类型的验证,而通过指令实现的每个元素/模型属性验证更常见?

2 个答案:

答案 0 :(得分:7)

我已经实施了一些可能对我正在开展的最新项目有帮助的东西。我们在前端使用AngularJS,在后端使用ASP.NET WEB API。所有HTML表单都是根据我的POCO类中包含的属性和数据注释自动生成的。

在服务器端,我有实体和DTO。我的实体包含特定于数据库的注释,而DTO包含我的视图特定注释。我将给出一个简短的示例,显示一个类中的一个属性以及如何为此呈现UI。以下是服务器端对象:

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; }
}

此属性在UI上呈现,如下所示:

<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>

<input />字段自动设置requiredplaceholdermaxlength属性。 HTML标签,引导列宽度也是基于自定义UI数据注释自动设置的。 Row = 1表示首先在表单中显示此字段,Width = 6表示该字段应占用6:class="col-sm-6"的列宽。标签文本和占位符文本是从资源文件中提取的。 如果这是您正在寻找的,请继续阅读: - )

我创建了一个Controller MetaController,它将DTO的名称作为参数:api/Meta/DiscountDTO。该控制器简单地遍历DTO对象和关联实体上的所有属性,并提取数据注释,将它们转换为FormMetadata类并将List<FormMetadata>返回给客户端。 FormMetadata类只包含IsRequiredIsDisplayedIsReadonly等属性,只是为了将注释转化为前端开发人员更具可读性的内容。这是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);
}

此端点将为Name属性返回以下JSON:

{  
   "$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
}

在客户端,我创建了一个自定义Angular指令<entity-form />,它将DTO的名称作为参数,如下所示:

<entity-form entity-type="DiscountDTO"></entity-form>。然后,该指令将调用MetaController以获取Discount实体的验证规则,并根据返回的规则呈现表单。为了呈现表单,我使用了一个名为angular-formly的真棒库。该库允许从javascript创建表单而无需编写任何HTML。我不会在这里获得关于角度形式的太多细节,但你基本上创建了一个Javascript对象,其中包含你想要渲染的表单的细节,并将其传递给一个angular-formly指令,它负责渲染形式给你。这是您传递给角度形式的对象类型的基本示例,以呈现标签为&#34的<input />框;文字&#34;:

{
  "key": "text",
  "type": "input",
  "templateOptions": {
    "label": "Text",
    "placeholder": "Type here to see the other field become enabled..."
  }
}

所以,我基本上从MetaController获取元数据并构建一个角度形式理解的对象,并将其传递给angular-formly指令,并为我呈现表单。我知道这个答案本来可以用更多的例子等等,但是我觉得这是很多东西。我希望这会给你足够的信息。

我很想让这个更通用和开源 - 如果有人有兴趣做这样的事情让我知道: - )

答案 1 :(得分:1)

根据MVC and MVVM with AngularJS,AngularJS遵循 MVVM 设计模式。

我认为你需要像Adding Unobtrusive Client Validation from the Microsoft .NET MVC Framework to Angular这样的东西。

如果您想为Razor创建自定义HTML帮助,请参阅Validation in Angular forms,其中包含2部分。

我还发现ngval似乎不稳定