{"id":28984,"date":"2019-03-06T01:14:00","date_gmt":"2019-03-06T00:14:00","guid":{"rendered":"https:\/\/blexin.com\/form-dinamiche-in-angular-si-puo\/"},"modified":"2021-05-20T19:22:46","modified_gmt":"2021-05-20T17:22:46","slug":"dynamic-forms-in-angular-its-possible","status":"publish","type":"post","link":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/","title":{"rendered":"Dynamic forms in Angular? It&#8217;s possible!"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"608\" data-attachment-id=\"28970\" data-permalink=\"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/attachment\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9-2\/\" data-orig-file=\"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&amp;ssl=1\" data-orig-size=\"1024,608\" data-comments-opened=\"0\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"7c7399a1-dd64-48a0-86f1-ffa7839d45b9\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?resize=1024%2C608&#038;ssl=1\" alt=\"\" class=\"wp-image-28970\" srcset=\"https:\/\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png 1024w, https:\/\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9-980x582.png 980w, https:\/\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9-480x285.png 480w\" sizes=\"auto, (min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 1024px, 100vw\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Some time ago, I had the chance to work on Raptor Framework, our product for the dynamic generation of CRUD operation starting from .Net code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">During the porting from .Net Full Framework to .Net Core and Angular, I deal with the refactoring of validation management of Angular forms, using ReactiveForm, replacing the custom management created.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Our framework basically generates a metadata JSON, used from the Angular&nbsp;front-end, for the dynamic generation of the UI. This JSON is generated starting from Data Annotations custom applied to the ViewModel of a .NET application. This is a simplified version:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>meta-data<\/em><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n{\n\u00a0\u00a0&quot;name&quot;: &quot;DynamicForm&quot;,\n\u00a0\u00a0&quot;displayName&quot;: &quot;DynamicForm&quot;,\n\u00a0\u00a0&quot;type&quot;: &quot;object&quot;,\n\u00a0\u00a0&quot;properties&quot;: {\n\u00a0\u00a0\u00a0\u00a0&quot;Name&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;Required&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;AllowEmptyStrings&quot;: { &quot;type&quot;: &quot;System.Boolean&quot;, &quot;value&quot;: false },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;ErrorMessage&quot;: { &quot;type&quot;: &quot;System.String&quot;, &quot;value&quot;: &quot;Name is mandatory&quot; },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;ErrorMessageResourceName&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;System.String&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;value&quot;: &quot;ErrorMessageResourceName&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;ErrorMessageResourceType&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;System.Type&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;value&quot;: &quot;ErrorMessageResourceType&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;RequiresValidationContext&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;System.Boolean&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;value&quot;: false\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;uiControl&quot;: false,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;uiValidation&quot;: true,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;Raptor.Core.DataAnnotations.Validation.RequiredAttribute&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;StringLength&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;ErrorMessage&quot;: { &quot;type&quot;: &quot;System.String&quot;, &quot;value&quot;: &quot;Name length must be between 3 and 20 characters&quot; },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;ErrorMessageResourceName&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;System.String&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;value&quot;: &quot;ErrorMessageResourceName&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;ErrorMessageResourceType&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;System.Type&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;value&quot;: &quot;ErrorMessageResourceType&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;MaximumLength&quot;: { &quot;type&quot;: &quot;System.Int32&quot;, &quot;value&quot;: 20 },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;MinimumLength&quot;: { &quot;type&quot;: &quot;System.Int32&quot;, &quot;value&quot;: 3 },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;RequiresValidationContext&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;System.Boolean&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;value&quot;: false\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;uiControl&quot;: false,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;uiValidation&quot;: true,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;Raptor.Core.DataAnnotations.Validation.StringLengthAttribute&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;TextBox&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;ColLg&quot;: { &quot;type&quot;: &quot;System.Int32&quot;, &quot;value&quot;: 6 },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;ColMd&quot;: { &quot;type&quot;: &quot;System.Int32&quot;, &quot;value&quot;: 6 },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;ColSm&quot;: { &quot;type&quot;: &quot;System.Int32&quot;, &quot;value&quot;: 6 },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;ColXs&quot;: { &quot;type&quot;: &quot;System.Int32&quot;, &quot;value&quot;: 6 },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;EnabledWhenPropertyName&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;System.String&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;value&quot;: &quot;EnabledWhenPropertyName&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;EnabledWhenPropertyValue&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;System.Object&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;value&quot;: &quot;EnabledWhenPropertyValue&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;HiddenWhenCreate&quot;: { &quot;type&quot;: &quot;System.Boolean&quot;, &quot;value&quot;: false },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;HiddenWhenDelete&quot;: { &quot;type&quot;: &quot;System.Boolean&quot;, &quot;value&quot;: false },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;HiddenWhenEdit&quot;: { &quot;type&quot;: &quot;System.Boolean&quot;, &quot;value&quot;: false },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;Order&quot;: { &quot;type&quot;: &quot;System.Int32&quot;, &quot;value&quot;: 0 },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;ReadOnly&quot;: { &quot;type&quot;: &quot;System.Boolean&quot;, &quot;value&quot;: false },\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;VisibleWhenPropertyName&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;System.String&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;value&quot;: &quot;VisibleWhenPropertyName&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;VisibleWhenPropertyValue&quot;: {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;System.Object&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;value&quot;: &quot;VisibleWhenPropertyValue&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;uiControl&quot;: true,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;uiValidation&quot;: false,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;Raptor.Core.DataAnnotations.Form.TextBoxAttribute&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;uiControlType&quot;: &quot;textbox&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;type&quot;: &quot;System.String&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;order&quot;: 0,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;displayName&quot;: &quot;Name&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;placeholder&quot;: &quot;NamePlaceHolder&quot;\n\u00a0\u00a0\u00a0\u00a0},\n\u00a0\u00a0\u00a0\u00a0...\n\u00a0\u00a0}\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Next call will provide us intead with the data of the entity, whose life cycle is to be managed::<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n{\n\u00a0\u00a0&quot;Name&quot;: &quot;Name&quot;,\n\u00a0\u00a0&quot;Surname&quot;: &quot;Surname&quot;,\n\u00a0\u00a0&quot;BirthDate&quot;: null,\n\u00a0\u00a0&quot;TermsOfService&quot;: false,\n\u00a0\u00a0&quot;Id&quot;: 0\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">The result of the elaboration on client-side is the following:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"978\" height=\"1024\" data-attachment-id=\"28976\" data-permalink=\"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/attachment\/shot-2\/\" data-orig-file=\"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/shot.png?fit=1266%2C1326&amp;ssl=1\" data-orig-size=\"1266,1326\" data-comments-opened=\"0\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"shot\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/shot.png?fit=978%2C1024&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/shot.png?resize=978%2C1024&#038;ssl=1\" alt=\"\" class=\"wp-image-28976\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">In the demo, that you can find in my GitHub account, I created a simplified version of this process: in the&nbsp;<em>assets<\/em>&nbsp;folder, I replaced&nbsp;HTTP calls&nbsp;that obtain required JSON, with example files. Thus we can focus exclusively on the dynamic generation of the interface.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"formcomponent\">FormComponent<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We start from the component, that represents the container of the form we want to generate, called&nbsp;<strong><em>FormComponent<\/em><\/strong>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nimport { Component, OnInit } from &#039;@angular\/core&#039;;\nimport { FormGroup } from &#039;@angular\/forms&#039;;\nimport { forkJoin } from &#039;rxjs&#039;;\nimport { FormService } from &#039;..\/services\/form.service&#039;;\nimport { RaptorDetailModel } from &#039;.\/form.model&#039;;\n\u00a0\n@Component({\n\u00a0\u00a0selector: &#039;app-form&#039;,\n\u00a0\u00a0templateUrl: &#039;form.component.html&#039;\n})\nexport class FormComponent implements OnInit {\n\u00a0\u00a0public detailsForm: FormGroup;\n\u00a0\u00a0public detail: RaptorDetailModel;\n\u00a0\n\u00a0\u00a0constructor(private service: FormService) { }\n\u00a0\n\u00a0\u00a0ngOnInit() {\n\u00a0\u00a0\u00a0\u00a0this.detailsForm = new FormGroup({});\n\u00a0\u00a0\u00a0\u00a0this.loadData();\n\u00a0\u00a0}\n\u00a0\n\u00a0\u00a0private loadData() {\n\u00a0\u00a0\u00a0\u00a0forkJoin(\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.service.getMetadata(),\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.service.getData()\n\u00a0\u00a0\u00a0\u00a0).subscribe(result =&gt; {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.detail = {} as RaptorDetailModel;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.detail.viewModelSchema = result&#x5B;0];\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.detail.viewModelData = result&#x5B;1];\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.initDetail();\n\u00a0\u00a0\u00a0\u00a0});\n\u00a0\u00a0}\n\u00a0\n\u00a0\u00a0private initDetail() {\n\u00a0\u00a0\u00a0\u00a0this.detail.viewModelDisplayName = this.detail.viewModelSchema&#x5B;&#039;name&#039;];\n\u00a0\n\u00a0\u00a0\u00a0\u00a0this.detail.propertiesDescriptors = &#x5B;];\n\u00a0\u00a0\u00a0\u00a0this.detail.viewModelProperties = this.detail.viewModelSchema&#x5B;&#039;properties&#039;];\n\u00a0\u00a0\u00a0\u00a0this.detail.viewModelPropertiesKeys = Object.keys(\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.detail.viewModelSchema&#x5B;&#039;properties&#039;]\n\u00a0\u00a0\u00a0\u00a0);\n\u00a0\n\u00a0\u00a0\u00a0\u00a0this.detail.viewModelPropertiesKeys.forEach(propertyKey =&gt; {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.detail.propertiesDescriptors.push({\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0key: propertyKey,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0schema: this.detail.viewModelProperties&#x5B;propertyKey],\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0viewModelData: this.detail.viewModelData,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0order: this.detail.viewModelProperties&#x5B;propertyKey].order,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0dependencies: this.detail.viewModelDependencies\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});\n\u00a0\u00a0\u00a0\u00a0});\n\u00a0\u00a0}\n\u00a0\n\u00a0\u00a0public submit() {\n\u00a0\u00a0\u00a0\u00a0if (this.detailsForm.valid) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0alert(JSON.stringify(this.detailsForm.value));\n\u00a0\u00a0\u00a0\u00a0} else {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Object.keys(this.detailsForm.controls).forEach(field =&gt; {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0const control = this.detailsForm.get(field);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0control.markAsTouched({ onlySelf: true });\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});\n\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0}\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">As you can see from the code, after the component creation,&nbsp;the method&nbsp;<em>loadData()<\/em>, that hands to make two calls to the backend (metadata and data), is called. Since we need both of them to go&nbsp;on, we should wait until both calls finish their job. We use the&nbsp;<strong>forkJoin<\/strong>&nbsp;operator for this purpose. If everything is ok, we divide the answers in two properties&nbsp;<em>viewModelSchema<\/em>&nbsp;(metadata) and&nbsp;<em>viewModelData<\/em>&nbsp;(data) and invoke the private method&nbsp;<em>initDetail()<\/em>&nbsp;to initialize the properties, that will contain all information we need.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Information we need are defined in the interface&nbsp;<em>RaptorDetailForm<\/em>:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>viewModelDisplayName<\/strong>, which contains the name of the entity to edit;<\/li><li><strong>propertiesDescriptors<\/strong>, which contains metadata on every form property;<\/li><li><strong>viewModelProperties<\/strong>, which contains data of every property of the form;<\/li><li><strong>viewModelPropertiesKeys<\/strong>, which contains the keys of every property of the form.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">These values will simplify the generation of the interface. The&nbsp;<em>submit()<\/em>&nbsp;method, as you can imagine, will allow us to manage the transmission of the form to the server, together with data edited by&nbsp;the user.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this demo, we simulate the data transmission with an&nbsp;<em>alert()<\/em>, but it&#8217;s interesting to look at&nbsp;<em>else<\/em>&nbsp;branch, to analyze how to display validation errors at the click&nbsp;on the button. The&nbsp;<em>markAsTouched()<\/em>&nbsp;method of&nbsp;<em>AbstractControl<\/em>&nbsp;(the basis class of Angular forms checks ) exactly do what it states:&nbsp; it &#8216;touches&#8217; the control and doesn&#8217;t modify it, in order to cause the update, which would normally occur&nbsp;with&nbsp;<em>focus<\/em>&nbsp;and&nbsp;<em>blur<\/em>&nbsp;events. In fact, we are forcing the form validation, if it fails.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The component markup is really simple:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&lt;div *ngIf=&quot;detail&quot;&gt;\n\u00a0\u00a0&lt;h3&gt;{{detail.viewModelDisplayName}}&lt;\/h3&gt;\n\u00a0\u00a0&lt;form class=&quot;form-horizontal&quot; novalidate\n\u00a0\u00a0\u00a0\u00a0&#x5B;formGroup]=&quot;detailsForm&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0&lt;div class=&quot;row&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;app-control-container\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#x5B;detailsForm]=&quot;detailsForm&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0*ngFor=&quot;let property of detail.propertiesDescriptors&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#x5B;viewModelProperty]=&quot;property&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/app-control-container&gt;\n\u00a0\u00a0\u00a0\u00a0&lt;\/div&gt;\n\u00a0\u00a0\u00a0\u00a0&lt;div class=&quot;row mt-5&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;button type=&quot;button&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0class=&quot;btn btn-primary col-12&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0scrollToInvalidField (click)=&quot;submit()&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0OK\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/button&gt;\n\u00a0\u00a0\u00a0\u00a0&lt;\/div&gt;\n\u00a0\u00a0&lt;\/form&gt;\n&lt;\/div&gt;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">As you can see, there&#8217;s a title, a form, a button to invoke the&nbsp;<em>submit()<\/em>&nbsp;method and a&nbsp;<em>for<\/em>&nbsp;cycle to generate every single control, implemented in the&nbsp;<strong>app-control-container<\/strong>&nbsp;component.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"controlcontainer\">ControlContainer<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The component ControlContainer deals with the wrapping of effective controls, that made up the form, starting from metadata received from the server. What we are going to do with this control, is to select the component to render, create the AbstractControl to add to the FormGroup and apply requested validations. We will add also some classes, selected from metadata, that will allow the layout of the component, based on the Bootstrap grid system.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n@Component({\n\u00a0\u00a0selector: &#039;app-control-container&#039;,\n\u00a0\u00a0templateUrl: &#039;control-container.component.html&#039;\n})\nexport class ControlContainerComponent implements OnInit {\n\u00a0\n\u00a0\u00a0@Input() detailsForm: FormGroup;\n\u00a0\u00a0@Input() formControl: AbstractControl;\n\u00a0\u00a0@Input() viewModelProperty: RaptorPropertyDescriptorModel;\n\u00a0\n\u00a0\u00a0@HostBinding(&#039;class&#039;) controlClass = &#039;&#039;;\n\u00a0\n\u00a0\u00a0ngOnInit() {\n\u00a0\u00a0\u00a0\u00a0this.formControl = new FormControl(this.viewModelProperty.viewModelData&#x5B;this.viewModelProperty.key]);\n\u00a0\u00a0\u00a0\u00a0this.detailsForm.addControl(this.viewModelProperty.key, this.formControl);\n\u00a0\u00a0\u00a0\u00a0this.addValidationsToFormControl();\n\u00a0\u00a0\u00a0\u00a0this.addClassesToControl();\n\u00a0\u00a0}\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">In the template, a simple&nbsp;<em>ngSwitch<\/em>&nbsp;allows us to choose the control to display, passing in input the form and associated metadata. We add an invisible label too, for any kind of validation expected in the field we show in case of validation error.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&lt;ng-container &#x5B;ngSwitch]=&quot;viewModelProperty.schema.uiControlType&quot;&gt;\n\u00a0\u00a0&lt;ng-container *ngSwitchCase=&quot;&#039;textbox&#039;&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;app-textbox &#x5B;detailsForm]=&quot;detailsForm&quot; &#x5B;viewModelProperty]=&quot;viewModelProperty&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/app-textbox&gt;\n\u00a0\u00a0&lt;\/ng-container&gt;\n\u00a0\u00a0&lt;ng-container *ngSwitchCase=&quot;&#039;checkbox&#039;&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;app-checkbox &#x5B;detailsForm]=&quot;detailsForm&quot; &#x5B;viewModelProperty]=&quot;viewModelProperty&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/app-checkbox&gt;\n\u00a0\u00a0&lt;\/ng-container&gt;\n\u00a0\u00a0&lt;ng-container *ngSwitchCase=&quot;&#039;date&#039;&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0&lt;app-date &#x5B;detailsForm]=&quot;detailsForm&quot; &#x5B;viewModelProperty]=&quot;viewModelProperty&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0&lt;\/app-date&gt;\n\u00a0\u00a0&lt;\/ng-container&gt;\n&lt;\/ng-container&gt;\n\u00a0\n&lt;ng-container *ngIf=&quot;this.detailsForm.get(this.viewModelProperty.key).touched || this.detailsForm.get(this.viewModelProperty.key).dirty&quot;&gt;\n\u00a0\u00a0&lt;ng-container *ngFor=&quot;let item of validationDictionary&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0&lt;small\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0class=&quot;form-text\u00a0 text-danger&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0*ngIf=&quot;this.detailsForm.hasError(item.validation, this.viewModelProperty.key)&quot; &gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{{validations&#x5B;item.validationGlobal]?.errorMessage}}\n\u00a0\u00a0\u00a0\u00a0&lt;\/small&gt;\n\u00a0\u00a0&lt;\/ng-container&gt;\n&lt;\/ng-container&gt;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s see in detail&nbsp;how validations and Bootstrap class setup for layout will be associated.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Our problem, here, is to map the validations&nbsp;that arrive us from metadata, with Validator of ReactiveForms of Angular. Every element of the form may have one validation or more, we create then an array of&nbsp;<strong><em>ValidatorFn,&nbsp;<\/em><\/strong>where we insert all validations associated with&nbsp;the field. The block&nbsp;<em>for<\/em>&nbsp;takes care to verify that among field properties, there are also those defined in&nbsp;<em>VALIDATION_GLOBALS<\/em>&nbsp;and, through the method&nbsp;<em>extractValidatorsFromValidationName()<\/em>, it converts them in the validations provided to the class&nbsp;<em>Validators<\/em>. At the end of the cycle, we set validations found through the method&nbsp;<em>setValidators()<\/em>&nbsp;of&nbsp;<em>AbstractControl<\/em>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>control-container.component.ts<\/em><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic validations: { &#x5B;key: string]: RaptorValidationModel } = { };\npublic validationDictionary: { validation: string, validationGlobal: VALIDATION_GLOBALS }&#x5B;] = &#x5B;\n\u00a0\u00a0{validation: &#039;required&#039;, validationGlobal: VALIDATION_GLOBALS.REQUIRED },\n\u00a0\u00a0{validation: &#039;pattern&#039;, validationGlobal: VALIDATION_GLOBALS.REGULAR_EXPRESSION },\n\u00a0\u00a0{validation: &#039;maxlength&#039;, validationGlobal: VALIDATION_GLOBALS.STRING_LENGTH },\n\u00a0\u00a0{validation: &#039;minlength&#039;, validationGlobal: VALIDATION_GLOBALS.STRING_LENGTH },\n\u00a0\u00a0{validation: &#039;email&#039;, validationGlobal: VALIDATION_GLOBALS.EMAIL },\n];\n\u00a0\nprivate addValidationsToFormControl() {\n\u00a0\u00a0const validators: ValidatorFn&#x5B;] = &#x5B;];\n\u00a0\n\u00a0\u00a0for (const key in this.viewModelProperty.schema) {\n\u00a0\u00a0\u00a0\u00a0if (this.viewModelProperty.schema&#x5B;key].hasOwnProperty(VALIDATION_GLOBALS.VALIDATION_PROPERTY)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&amp;&amp; this.viewModelProperty.schema&#x5B;key]&#x5B;VALIDATION_GLOBALS.VALIDATION_PROPERTY] === true) {\n\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0const validation = this.viewModelProperty.schema&#x5B;key];\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.validations&#x5B;key] = {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0errorMessage: validation&#x5B;VALIDATION_GLOBALS.ERROR_MESSAGE].value\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0};\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.extractValidatorsFromValidationName(validation, key, validators);\n\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0}\n\u00a0\n\u00a0\u00a0this.detailsForm.controls&#x5B;this.viewModelProperty.key].setValidators(validators);\n}\n\u00a0\nprivate extractValidatorsFromValidationName(validation: any, key: string, validators: ValidatorFn&#x5B;]) {\n\u00a0\u00a0switch (key) {\n\u00a0\u00a0\u00a0\u00a0case VALIDATION_GLOBALS.EMAIL:\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0validators.push(Validators.email);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0break;\n\u00a0\u00a0\u00a0\u00a0case VALIDATION_GLOBALS.REGULAR_EXPRESSION:\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0validators.push(Validators.pattern(validation&#x5B;VALIDATION_GLOBALS.PATTERN].value));\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0break;\n\u00a0\u00a0\u00a0\u00a0case VALIDATION_GLOBALS.REQUIRED:\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0validators.push(Validators.required);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0break;\n\u00a0\u00a0\u00a0\u00a0case VALIDATION_GLOBALS.STRING_LENGTH:\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (validation&#x5B;VALIDATION_GLOBALS.MAXIMUM_LENGTH]) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0validators.push(Validators.maxLength(validation&#x5B;VALIDATION_GLOBALS.MAXIMUM_LENGTH].value));\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (validation&#x5B;VALIDATION_GLOBALS.MINIMUM_LENGTH]) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0validators.push(Validators.minLength(validation&#x5B;VALIDATION_GLOBALS.MINIMUM_LENGTH].value));\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0break;\n\u00a0\u00a0}\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">To set the classes, we run thus a cycle on the diagram properties, we verify if they belong to&nbsp;<em>uiControl<\/em>&nbsp;and that they are part of the classes expected by&nbsp;<em>FORM_GLOBAL<\/em>. If the result is positive, we modify the class name&nbsp;in order to let it corresponds the Bootstrap one, and assign it to the attribute&nbsp;<em>controlClass<\/em>&nbsp;that is in&nbsp;<strong>HostBinding<\/strong>&nbsp;with&nbsp;<em>class<\/em>. HostBinding allows us to project this property on the tag component, which action is required by Bootstrap to do its job (for more information, you can consult the article by Michele:&nbsp;<a href=\"https:\/\/www.blexin.com\/en-US\/Article\/Blog\/Angular-Advanced-Components-how-to-create-a-dashboard-17\" target=\"_blank\" rel=\"noreferrer noopener\">Angular Advanced Components: how to create a dashboard<\/a>).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>control-container.component.ts<\/em><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nprivate addClassesToControl() {\n\u00a0\u00a0const dataAnnotations = Object.keys(this.viewModelProperty.schema);\n\u00a0\n\u00a0\u00a0dataAnnotations.forEach(annotationKey =&gt; {\n\u00a0\u00a0\u00a0\u00a0const jsonAnnotation = this.viewModelProperty.schema&#x5B;annotationKey];\n\u00a0\n\u00a0\u00a0\u00a0\u00a0if (\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0jsonAnnotation.hasOwnProperty(FORM_GLOBALS.UI_CONTROL) &amp;&amp;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0jsonAnnotation&#x5B;FORM_GLOBALS.UI_CONTROL] === true\n\u00a0\u00a0\u00a0\u00a0) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (jsonAnnotation.hasOwnProperty(FORM_GLOBALS.COL_LG)) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.addClass(jsonAnnotation, FORM_GLOBALS.COL_LG);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (jsonAnnotation.hasOwnProperty(FORM_GLOBALS.COL_MD)) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.addClass(jsonAnnotation, FORM_GLOBALS.COL_MD);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (jsonAnnotation.hasOwnProperty(FORM_GLOBALS.COL_SM)) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.addClass(jsonAnnotation, FORM_GLOBALS.COL_SM);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (jsonAnnotation.hasOwnProperty(FORM_GLOBALS.COL_XS)) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.addClass(jsonAnnotation, FORM_GLOBALS.COL_XS);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0});\n}\n\u00a0\nprivate addClass(jsonAnnotation, jsonClass) {\n\u00a0\u00a0this.controlClass = this.controlClass + &#039; &#039; +\n\u00a0\u00a0\u00a0\u00a0this.humanizeClassStyleString(jsonClass, &#039;-&#039;) +\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#039;-&#039; +\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0jsonAnnotation&#x5B;jsonClass].value;\n}\n\u00a0\nprivate humanizeClassStyleString(target, separator) {\n\u00a0\u00a0return target\n\u00a0\u00a0\u00a0\u00a0.replace(\/(&#x5B;A-Z])\/g, &#039; $1&#039;)\n\u00a0\u00a0\u00a0\u00a0.replace(\/(&#x5B;A-Z])\/g, function(str) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return str.toLowerCase();\n\u00a0\u00a0\u00a0\u00a0})\n\u00a0\u00a0\u00a0\u00a0.trim(&#039; &#039;)\n\u00a0\u00a0\u00a0\u00a0.replace(&#039; &#039;, separator);\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"checks\">Checks<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We only need to define the checks. In the demo, I defined only three for simplicity: the checkbox, the textbox and the date picker. The target is to make the creation of these controls the more stupid as possible, focusing the requested logic in a basic&nbsp;control, that we will call&nbsp;<em>BaseControl<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nimport { Injectable, OnInit, Input } from &#039;@angular\/core&#039;;\nimport { FormGroup } from &#039;@angular\/forms&#039;;\nimport { ViewModelProperty } from &#039;.\/controls.model&#039;;\n\u00a0\n@Injectable()\nexport abstract class BaseControl implements OnInit {\n\u00a0\u00a0@Input() viewModelProperty: ViewModelProperty;\n\u00a0\u00a0@Input() detailsForm: FormGroup;\n\u00a0\n\u00a0\u00a0public controlType: string;\n\u00a0\u00a0public controlKey: string;\n\u00a0\u00a0public controlLabel: string;\n\u00a0\u00a0public controlValue: any;\n\u00a0\n\u00a0\u00a0ngOnInit() {\n\u00a0\u00a0\u00a0\u00a0this.controlType = this.viewModelProperty.schema.uiControlType;\n\u00a0\u00a0\u00a0\u00a0this.controlKey = this.viewModelProperty.key;\n\u00a0\u00a0\u00a0\u00a0this.controlLabel = this.viewModelProperty.schema&#x5B;&#039;displayName&#039;];\n\u00a0\u00a0\u00a0\u00a0this.controlValue = this.viewModelProperty.viewModelData&#x5B;this.viewModelProperty.key];\n\u00a0\u00a0}\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">As you can see, if we have in input all the needed information, we can just divide them into single properties, much convenient to use. Now, single components, that represent the interface checks, just have to extend this component.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"checkbox\">Checkbox<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nimport { Component } from &#039;@angular\/core&#039;;\nimport { BaseControl } from &#039;..\/base-control&#039;;\n\u00a0\n@Component({\n\u00a0\u00a0\u00a0\u00a0selector: &#039;app-checkbox&#039;,\n\u00a0\u00a0\u00a0\u00a0templateUrl: &#039;checkbox.component.html&#039;\n})\nexport class CheckboxComponent\n\u00a0\u00a0extends BaseControl {\n}\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;div class=&quot;form-check mt-2 mt-sm-2 mb-2 mr-sm-2 mb-sm-0&quot;\n\u00a0\u00a0&#x5B;ngClass]=&quot;{&#039;has-danger&#039;: !isValid}&quot; &#x5B;formGroup]=&quot;detailsForm&quot;&gt;\n\u00a0\u00a0&lt;label class=&quot;form-check-label&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0&lt;input &#x5B;formControlName]=&quot;controlKey&quot; type=&quot;checkbox&quot; class=&quot;form-check-input&quot;&gt; {{controlLabel}}\n\u00a0\u00a0&lt;\/label&gt;\n&lt;\/div&gt;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Textbox<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nimport { Component} from &#039;@angular\/core&#039;;\nimport { BaseControl } from &#039;..\/base-control&#039;;\n\u00a0\n@Component({\n\u00a0\u00a0\u00a0\u00a0selector: &#039;app-textbox&#039;,\n\u00a0\u00a0\u00a0\u00a0templateUrl: &#039;textbox.component.html&#039;\n})\nexport class TextboxComponent\n\u00a0\u00a0extends BaseControl {\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Datepicker<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nimport { Component } from &#039;@angular\/core&#039;;\nimport { BaseControl } from &#039;..\/base-control&#039;;\n\u00a0\n@Component({\n\u00a0\u00a0\u00a0\u00a0selector: &#039;app-date&#039;,\n\u00a0\u00a0\u00a0\u00a0templateUrl: &#039;.\/date.component.html&#039;,\n})\nexport class DateComponent\n\u00a0\u00a0extends BaseControl {\n}\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;div class=&quot;form-group mb-1&quot; &#x5B;formGroup]=&quot;detailsForm&quot;&gt;\n\u00a0\u00a0&lt;label &gt;{{controlLabel}}&lt;\/label&gt;\n\u00a0\u00a0&lt;div class=&quot;input-group&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0&lt;input &#x5B;formControlName]=&quot;controlKey&quot; class=&quot;form-control&quot; ngbDatepicker #d=&quot;ngbDatepicker&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0&lt;div class=&quot;input-group-append&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;button class=&quot;input-group-append btn btn-outline-secondary&quot; (click)=&quot;d.toggle()&quot; type=&quot;button&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;i class=&quot;fa fa-calendar&quot;&gt;&lt;\/i&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&lt;\/button&gt;\n\u00a0\u00a0\u00a0\u00a0&lt;\/div&gt;\n\u00a0\u00a0&lt;\/div&gt;\n&lt;\/div&gt;\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"scrolltoinvalidfielddirective\">ScrollToInvalidFieldDirective<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">During the test on complex forms, I realize that, scrolling the page and clicking on the enter button, invisible validation errors don\u2019t let the user understand which was the problem, due to which the click doesn\u2019t return any feedback. I want to add then a functionality that, when invalid fields are present, will automatically scroll down the page, showing the first invalid field.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By a&nbsp;<em>guideline<\/em>&nbsp;put on the form submit, I intercept the&nbsp;<em>click<\/em>&nbsp;on its and, with a simple query, I verify if there are elements with&nbsp;<em>ng-invalid<\/em>&nbsp;class: if so, I scroll to the first element found.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nimport { Directive, HostListener } from &#039;@angular\/core&#039;;\n\u00a0\n@Directive({\n\u00a0\u00a0selector: &#039;&#x5B;scrollToInvalidField]&#039;\n})\nexport class ScrollToInvalidFieldDirective {\n\u00a0\u00a0@HostListener(&#039;click&#039;) onClick() {\n\u00a0\u00a0\u00a0\u00a0const elementList = document.querySelectorAll(&#039;input.ng-invalid&#039;);\n\u00a0\u00a0\u00a0\u00a0const element = elementList&#x5B;0] as HTMLElement;\n\u00a0\u00a0\u00a0\u00a0if (element) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0element.scrollIntoView({ behavior: &#039;smooth&#039; });\n\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0}\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Simple and effective:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"988\" height=\"544\" data-attachment-id=\"28982\" data-permalink=\"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/attachment\/scroll-2\/\" data-orig-file=\"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/scroll.gif?fit=988%2C544&amp;ssl=1\" data-orig-size=\"988,544\" data-comments-opened=\"0\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"scroll\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/scroll.gif?fit=988%2C544&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/scroll.gif?resize=988%2C544&#038;ssl=1\" alt=\"\" class=\"wp-image-28982\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You can download the code here&nbsp;<a href=\"https:\/\/github.com\/AARNOLD87\/AngularDymanicForm\" rel=\"noreferrer noopener\" target=\"_blank\">https:\/\/github.com\/AARNOLD87\/AngularDymanicForm<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">See you next!<\/p>\n\n\n\n\n","protected":false},"excerpt":{"rendered":"<p>Let&#8217;s see how to dynamically generate a form in Angular, starting from a metadata JSON<\/p>\n","protected":false},"author":196716250,"featured_media":28970,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"off","_et_pb_old_content":"","_et_gb_content_width":"","_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","_crdt_document":"","inline_featured_image":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"_wpas_customize_per_network":false,"jetpack_post_was_ever_published":false},"categories":[688637524],"tags":[688637390,688637416,688637443],"class_list":["post-28984","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-en","tag-angular-en","tag-asp-net-core-en","tag-raptor-en"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Dynamic forms in Angular? It&#039;s possible! - Blexin<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Dynamic forms in Angular? It&#039;s possible! - Blexin\" \/>\n<meta property=\"og:description\" content=\"Let&#039;s see how to dynamically generate a form in Angular, starting from a metadata JSON\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/\" \/>\n<meta property=\"og:site_name\" content=\"Blexin\" \/>\n<meta property=\"article:published_time\" content=\"2019-03-06T00:14:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2021-05-20T17:22:46+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/i1.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&ssl=1\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"608\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Adolfo Arnold\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Adolfo Arnold\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/blog-en\\\/dynamic-forms-in-angular-its-possible\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/blog-en\\\/dynamic-forms-in-angular-its-possible\\\/\"},\"author\":{\"name\":\"Adolfo Arnold\",\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/#\\\/schema\\\/person\\\/0de430b61c8a48b0e9d81308817c1517\"},\"headline\":\"Dynamic forms in Angular? It&#8217;s possible!\",\"datePublished\":\"2019-03-06T00:14:00+00:00\",\"dateModified\":\"2021-05-20T17:22:46+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/blog-en\\\/dynamic-forms-in-angular-its-possible\\\/\"},\"wordCount\":1122,\"image\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/blog-en\\\/dynamic-forms-in-angular-its-possible\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/i0.wp.com\\\/blexin.com\\\/wp-content\\\/uploads\\\/2020\\\/12\\\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&ssl=1\",\"keywords\":[\"Angular\",\"Asp.net core\",\"Raptor\"],\"articleSection\":[\"Blog\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/blog-en\\\/dynamic-forms-in-angular-its-possible\\\/\",\"url\":\"https:\\\/\\\/blexin.com\\\/en\\\/blog-en\\\/dynamic-forms-in-angular-its-possible\\\/\",\"name\":\"Dynamic forms in Angular? It's possible! - Blexin\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/blog-en\\\/dynamic-forms-in-angular-its-possible\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/blog-en\\\/dynamic-forms-in-angular-its-possible\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/i0.wp.com\\\/blexin.com\\\/wp-content\\\/uploads\\\/2020\\\/12\\\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&ssl=1\",\"datePublished\":\"2019-03-06T00:14:00+00:00\",\"dateModified\":\"2021-05-20T17:22:46+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/#\\\/schema\\\/person\\\/0de430b61c8a48b0e9d81308817c1517\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/blog-en\\\/dynamic-forms-in-angular-its-possible\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/blexin.com\\\/en\\\/blog-en\\\/dynamic-forms-in-angular-its-possible\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/blog-en\\\/dynamic-forms-in-angular-its-possible\\\/#primaryimage\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/blexin.com\\\/wp-content\\\/uploads\\\/2020\\\/12\\\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&ssl=1\",\"contentUrl\":\"https:\\\/\\\/i0.wp.com\\\/blexin.com\\\/wp-content\\\/uploads\\\/2020\\\/12\\\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&ssl=1\",\"width\":1024,\"height\":608},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/blog-en\\\/dynamic-forms-in-angular-its-possible\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/blexin.com\\\/en\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Dynamic forms in Angular? It&#8217;s possible!\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/blexin.com\\\/en\\\/\",\"name\":\"Blexin\",\"description\":\"Con noi \u00e8 semplice\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/blexin.com\\\/en\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/blexin.com\\\/en\\\/#\\\/schema\\\/person\\\/0de430b61c8a48b0e9d81308817c1517\",\"name\":\"Adolfo Arnold\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ff2a87b54d0f130d7452164533199af05ef16dbd08b9241729946cea0eec7cca?s=96&d=identicon&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ff2a87b54d0f130d7452164533199af05ef16dbd08b9241729946cea0eec7cca?s=96&d=identicon&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ff2a87b54d0f130d7452164533199af05ef16dbd08b9241729946cea0eec7cca?s=96&d=identicon&r=g\",\"caption\":\"Adolfo Arnold\"},\"url\":\"https:\\\/\\\/blexin.com\\\/en\\\/author\\\/adolfo-arnoldblexin-com\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Dynamic forms in Angular? It's possible! - Blexin","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/","og_locale":"en_US","og_type":"article","og_title":"Dynamic forms in Angular? It's possible! - Blexin","og_description":"Let's see how to dynamically generate a form in Angular, starting from a metadata JSON","og_url":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/","og_site_name":"Blexin","article_published_time":"2019-03-06T00:14:00+00:00","article_modified_time":"2021-05-20T17:22:46+00:00","og_image":[{"width":1024,"height":608,"url":"https:\/\/i1.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&ssl=1","type":"image\/png"}],"author":"Adolfo Arnold","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Adolfo Arnold","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/#article","isPartOf":{"@id":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/"},"author":{"name":"Adolfo Arnold","@id":"https:\/\/blexin.com\/en\/#\/schema\/person\/0de430b61c8a48b0e9d81308817c1517"},"headline":"Dynamic forms in Angular? It&#8217;s possible!","datePublished":"2019-03-06T00:14:00+00:00","dateModified":"2021-05-20T17:22:46+00:00","mainEntityOfPage":{"@id":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/"},"wordCount":1122,"image":{"@id":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/#primaryimage"},"thumbnailUrl":"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&ssl=1","keywords":["Angular","Asp.net core","Raptor"],"articleSection":["Blog"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/","url":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/","name":"Dynamic forms in Angular? It's possible! - Blexin","isPartOf":{"@id":"https:\/\/blexin.com\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/#primaryimage"},"image":{"@id":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/#primaryimage"},"thumbnailUrl":"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&ssl=1","datePublished":"2019-03-06T00:14:00+00:00","dateModified":"2021-05-20T17:22:46+00:00","author":{"@id":"https:\/\/blexin.com\/en\/#\/schema\/person\/0de430b61c8a48b0e9d81308817c1517"},"breadcrumb":{"@id":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/#primaryimage","url":"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&ssl=1","contentUrl":"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&ssl=1","width":1024,"height":608},{"@type":"BreadcrumbList","@id":"https:\/\/blexin.com\/en\/blog-en\/dynamic-forms-in-angular-its-possible\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blexin.com\/en\/"},{"@type":"ListItem","position":2,"name":"Dynamic forms in Angular? It&#8217;s possible!"}]},{"@type":"WebSite","@id":"https:\/\/blexin.com\/en\/#website","url":"https:\/\/blexin.com\/en\/","name":"Blexin","description":"Con noi \u00e8 semplice","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blexin.com\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blexin.com\/en\/#\/schema\/person\/0de430b61c8a48b0e9d81308817c1517","name":"Adolfo Arnold","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/ff2a87b54d0f130d7452164533199af05ef16dbd08b9241729946cea0eec7cca?s=96&d=identicon&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/ff2a87b54d0f130d7452164533199af05ef16dbd08b9241729946cea0eec7cca?s=96&d=identicon&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/ff2a87b54d0f130d7452164533199af05ef16dbd08b9241729946cea0eec7cca?s=96&d=identicon&r=g","caption":"Adolfo Arnold"},"url":"https:\/\/blexin.com\/en\/author\/adolfo-arnoldblexin-com\/"}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/blexin.com\/wp-content\/uploads\/2020\/12\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9.png?fit=1024%2C608&ssl=1","jetpack_shortlink":"https:\/\/wp.me\/pcyUBx-7xu","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/blexin.com\/en\/wp-json\/wp\/v2\/posts\/28984","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blexin.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blexin.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blexin.com\/en\/wp-json\/wp\/v2\/users\/196716250"}],"replies":[{"embeddable":true,"href":"https:\/\/blexin.com\/en\/wp-json\/wp\/v2\/comments?post=28984"}],"version-history":[{"count":5,"href":"https:\/\/blexin.com\/en\/wp-json\/wp\/v2\/posts\/28984\/revisions"}],"predecessor-version":[{"id":32005,"href":"https:\/\/blexin.com\/en\/wp-json\/wp\/v2\/posts\/28984\/revisions\/32005"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blexin.com\/en\/wp-json\/wp\/v2\/media\/28970"}],"wp:attachment":[{"href":"https:\/\/blexin.com\/en\/wp-json\/wp\/v2\/media?parent=28984"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blexin.com\/en\/wp-json\/wp\/v2\/categories?post=28984"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blexin.com\/en\/wp-json\/wp\/v2\/tags?post=28984"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}