{"id":28968,"date":"2019-03-06T01:14:00","date_gmt":"2019-03-06T00:14:00","guid":{"rendered":"https:\/\/blexin.com\/?p=28968"},"modified":"2021-01-13T09:40:44","modified_gmt":"2021-01-13T08:40:44","slug":"form-dinamiche-in-angular-si-puo","status":"publish","type":"post","link":"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/","title":{"rendered":"Form dinamiche in Angular? Si pu\u00f2!"},"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=\"28969\" data-permalink=\"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/attachment\/7c7399a1-dd64-48a0-86f1-ffa7839d45b9\/\" 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-28969\" 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\">Qualche tempo fa ho potuto lavorare su Raptor Framework, il nostro prodotto per la generazione dinamica di operazioni CRUD a partire da codice .Net.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Durante il porting da .Net Full Framework a .Net Core e Angular, mi sono occupato del refactoring della gestione delle validazioni delle form Angular, utilizzando ReactiveForm, sostituendo la gestione custom che era stata creata.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Di base, il nostro framework genera un JSON di metadati, utilizzato dal front-end Angular per la generazione dinamica della UI. Tale JSON viene generato a partire da Data Annotations custom, applicate ai ViewModel di una applicazione .NET. Eccone una versione semplificata:<\/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\">Una successiva chiamata, invece, ci fornir\u00e0 i dati dell&#8217;entit\u00e0 di cui gestire il ciclo di vita:<\/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\">Il risultato dell&#8217;elaborazione lato client \u00e8 il seguente:,<\/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=\"28975\" data-permalink=\"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/attachment\/shot\/\" 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-28975\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Nella demo, che potete trovare sul mio account GitHub, ho creato una versione semplificata di questo processo: ho sostituito, nella cartella&nbsp;<em>assets,<\/em>&nbsp;le chiamate HTTP che ricavano i JSON necessari, con dei file di esempio. In questo modo, possiamo concentrarci unicamente sulla parte di generazione dinamica dell&#8217;interfaccia.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"formcomponent\">FormComponent<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Partiamo dal componente che rappresenta il contenitore della form che vogliamo generare, chiamato&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\">Come potete vedere dal codice, dopo la creazione del componente, viene chiamato il metodo&nbsp;<em>loadData(),<\/em>&nbsp;che si preoccupa di fare le due chiamate al backend: i metadati e i dati. Dato che abbiamo bisogno di entrambi per proseguire, dobbiamo aspettare che entrambe le chiamate abbiano ultimato il loro lavoro.&nbsp;Usiamo quindi l&#8217;operatore&nbsp;<strong>forkJoin<\/strong>&nbsp;proprio a tale scopo. Se tutto va bene, dividiamo le due risposte in due propriet\u00e0,&nbsp;<em>viewModelSchema<\/em>&nbsp;(i metadati) e&nbsp;<em>viewModelData<\/em>&nbsp;(i dati), e invochiamo il metodo privato&nbsp;<em>initDetail(),<\/em>&nbsp;per inizializzare le propriet\u00e0 che conterranno tutte le informazioni che ci servono.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le informazioni di cui abbiamo bisogno sono definite nell&#8217;interfaccia&nbsp;<em>RaptorDetailForm<\/em>:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>viewModelDisplayName<\/strong>, che conterr\u00e0 il nome dell&#8217;entit\u00e0 da editare;<\/li><li><strong>propertiesDescriptors<\/strong>, che conterr\u00e0 i metadati su ogni propriet\u00e0 della form;<\/li><li><strong>viewModelProperties<\/strong>, che conterr\u00e0 i dati di ogni propriet\u00e0 della form;<\/li><li><strong>viewModelPropertiesKeys<\/strong>, che conterr\u00e0 le chiavi di ogni propriet\u00e0 della form.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Questi valori ci semplificheranno la generazione dell&#8217;interfaccia. Il metodo&nbsp;<em>submit()<\/em>, come potete immaginare, ci permetter\u00e0 di gestire l&#8217;invio della form al server con i dati editati dall&#8217;utente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In questa demo, simuliamo l&#8217;invio dei dati con una&nbsp;<em>alert()<\/em>, ma \u00e8 interessante guardare il ramo&nbsp;<em>else,<\/em>&nbsp;per analizzare come visualizzare gli errori di validazione al click del pulsante. Il metodo&nbsp;<em>markAsTouched()<\/em>&nbsp;di&nbsp;<em>AbstractControl<\/em>&nbsp;(la classe base dei controlli form di Angular) fa esattamente quello che dice: &#8216;tocca&#8217; il controllo senza modificarne il valore, in modo da scatenarne l&#8217;aggiornamento, che normalmente avverrebbe con gli eventi&nbsp;<em>focus<\/em>&nbsp;e&nbsp;<em>blur<\/em>. Di fatto, stiamo forzando la validazione della form in caso di fallimento.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il markup del componente \u00e8 molto semplice:<\/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\">Come potete vedere, c\u2019\u00e8 un titolo, una form, un pulsante per l&#8217;invocazione del metodo&nbsp;<em>submit()<\/em>&nbsp;e un ciclo&nbsp;<em>for<\/em>&nbsp;per la generazione dei singoli controlli, implementata nel componente&nbsp;<strong>app-control-container<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"controlcontainer\">ControlContainer<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Il componente ControlContainer si occupa di wrappare gli effettivi controlli che compongono la form, partendo dai metadati ricevuti dal server. Quello che facciamo in questo controllo, \u00e8 quindi selezionare il componente da renderizzare, creare l&#8217;AbstractControl da aggiungere al FormGroup e applicare le validazioni richieste. Aggiungeremo anche delle classi, sempre selezionate dai metadati, che permetteranno l&#8217;impaginazione del componente, sulla base del grid system di Bootstrap.<\/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\">Nel template, un semplice&nbsp;<em>ngSwitch<\/em>&nbsp;permette di scegliere il controllo da visualizzare passando in input la form e i metadati associati. Inoltre, aggiungiamo una label invisibile per ogni tipo di validazione prevista dal campo che mostreremo in caso di errore di validazione.<\/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\">Guardiamo nel dettaglio come vengono associate le validazioni e l&#8217;impostazione delle classi di Bootstrap per l&#8217;impaginazione.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il nostro problema, qui, \u00e8 mappare le validazioni che ci arrivano dai metadati, con i Validator di ReactiveForms di Angular. Ogni elemento della form pu\u00f2 avere pi\u00f9 di una validazione.&nbsp;Creiamo quindi un array di&nbsp;<strong><em>ValidatorFn<\/em><\/strong>, in cui inseriremo tutte le validazioni associate al campo. Il blocco&nbsp;<em>for<\/em>&nbsp;si occupa proprio di verificare che tra le propriet\u00e0 di quel campo vi siano quelle definite nel&nbsp;<em>VALIDATION_GLOBALS<\/em>&nbsp;e, mediante il metodo&nbsp;<em>extractValidatorsFromValidationName()<\/em>, le converte nelle validazioni messe a disposizione dalla classe&nbsp;<em>Validators<\/em>. Alla fine del ciclo, settiamo le validazioni trovate attraverso il metodo&nbsp;<em>setValidators()<\/em>&nbsp;di&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\">Per impostare le classi, invece, eseguiamo un ciclo sulle propriet\u00e0 dello schema, verifichiamo che appartengano effettivamente ad&nbsp;<em>uiControl<\/em>&nbsp;e che facciano parte delle possibili classi previste dal&nbsp;<em>FORM_GLOBAL<\/em>. In caso positivo, modifichiamo il nome della classe in modo tale da farla corrispondere a quella di Bootstrap, e la assegniamo all&#8217;attributo&nbsp;<em>controlClass<\/em>&nbsp;che \u00e8 in&nbsp;<strong>HostBinding<\/strong>&nbsp;con&nbsp;<em>class<\/em>. HostBinding ci permette di proiettare questa propriet\u00e0 sul tag del componente, cosa necessaria a Bootstrap per fare il suo lavoro (per maggiori dettagli date un occhio all&#8217;articolo di Michele:&nbsp;<a href=\"https:\/\/www.blexin.com\/it-IT\/Article\/Blog\/Componenti-avanzati-in-Angular-come-creare-una-dashboard-17\" target=\"_blank\" rel=\"noreferrer noopener\">Componenti avanzati in Angular:come creare una 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=\"controlli\">Controlli<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Non resta che definire i controlli. Nella demo, per semplicit\u00e0, ne ho definiti solo tre: la checkbox, la textbox e il datepicker. L&#8217;obiettivo \u00e8 rendere la creazione di questi controlli il pi\u00f9 stupido possibile, concentrando la logica necessaria in un controllo base che chiameremo&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\">Come potete vedere, avendo in input tutte le informazioni che ci servono, ci basta dividerle in singole propriet\u00e0 pi\u00f9, comode da utilizzare. A questo punto, i singoli componenti, che rappresentano i controlli dell&#8217;interfaccia, devono solo estendere questo componente.<\/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\">Durante i test su form molto complesse, mi sono accorto che, scrollando la pagina e cliccando sul pulsante di invio, errori di validazione non visibili non facevano capire all&#8217;utente quale fosse il problema per il quale il click non desse nessun feedback. Ho voluto aggiungere quindi una funzionalit\u00e0 che, in presenza di campi non validi, scrollasse in automatico la pagina, mostrandomi il primo campo non valido.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mediante una&nbsp;<em>direttiva<\/em>&nbsp;posta sul submit della form, intercetto il&nbsp;<em>click<\/em>&nbsp;dello stesso e, con una semplice query, verifico se vi sono elementi con classe&nbsp;<em>ng-invalid<\/em>: in caso positivo scrollo al primo trovato.<\/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\">Semplice ed efficace<\/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=\"28981\" data-permalink=\"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/attachment\/scroll\/\" 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-28981\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Potete scaricare il codice qui:&nbsp;<a href=\"https:\/\/github.com\/AARNOLD87\/AngularDymanicForm\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/AARNOLD87\/AngularDymanicForm<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Alla prossima!<\/p>\n\n\n\n\n","protected":false},"excerpt":{"rendered":"<p>Vediamo come generare dinamicamente un form in Angular partendo da un JSON di metadati<\/p>\n","protected":false},"author":196716250,"featured_media":28969,"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":"","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,"_wpcom_ai_launchpad_first_post":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"{title}\n\n{excerpt}\n\n{url}","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":[688637374],"tags":[688637387,688637414,688637442],"class_list":["post-28968","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","tag-angular","tag-asp-net-core","tag-raptor"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Form dinamiche in Angular? Si pu\u00f2! - 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\/it\/blog\/form-dinamiche-in-angular-si-puo\/\" \/>\n<meta property=\"og:locale\" content=\"it_IT\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Form dinamiche in Angular? Si pu\u00f2! - Blexin\" \/>\n<meta property=\"og:description\" content=\"Vediamo come generare dinamicamente un form in Angular partendo da un JSON di metadati\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/\" \/>\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-01-13T08:40:44+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=\"Scritto da\" \/>\n\t<meta name=\"twitter:data1\" content=\"Adolfo Arnold\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tempo di lettura stimato\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minuti\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/form-dinamiche-in-angular-si-puo\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/form-dinamiche-in-angular-si-puo\\\/\"},\"author\":{\"name\":\"Adolfo Arnold\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/#\\\/schema\\\/person\\\/0de430b61c8a48b0e9d81308817c1517\"},\"headline\":\"Form dinamiche in Angular? Si pu\u00f2!\",\"datePublished\":\"2019-03-06T00:14:00+00:00\",\"dateModified\":\"2021-01-13T08:40:44+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/form-dinamiche-in-angular-si-puo\\\/\"},\"wordCount\":1072,\"image\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/form-dinamiche-in-angular-si-puo\\\/#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\":\"it-IT\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/form-dinamiche-in-angular-si-puo\\\/\",\"url\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/form-dinamiche-in-angular-si-puo\\\/\",\"name\":\"Form dinamiche in Angular? Si pu\u00f2! - Blexin\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/form-dinamiche-in-angular-si-puo\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/form-dinamiche-in-angular-si-puo\\\/#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-01-13T08:40:44+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/#\\\/schema\\\/person\\\/0de430b61c8a48b0e9d81308817c1517\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/form-dinamiche-in-angular-si-puo\\\/#breadcrumb\"},\"inLanguage\":\"it-IT\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/form-dinamiche-in-angular-si-puo\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/blog\\\/form-dinamiche-in-angular-si-puo\\\/#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\\\/it\\\/blog\\\/form-dinamiche-in-angular-si-puo\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/blexin.com\\\/it\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Form dinamiche in Angular? Si pu\u00f2!\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/#website\",\"url\":\"https:\\\/\\\/blexin.com\\\/it\\\/\",\"name\":\"Blexin\",\"description\":\"Con noi \u00e8 semplice\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/blexin.com\\\/it\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"it-IT\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/blexin.com\\\/it\\\/#\\\/schema\\\/person\\\/0de430b61c8a48b0e9d81308817c1517\",\"name\":\"Adolfo Arnold\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"it-IT\",\"@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\\\/it\\\/author\\\/adolfo-arnoldblexin-com\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Form dinamiche in Angular? Si pu\u00f2! - 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\/it\/blog\/form-dinamiche-in-angular-si-puo\/","og_locale":"it_IT","og_type":"article","og_title":"Form dinamiche in Angular? Si pu\u00f2! - Blexin","og_description":"Vediamo come generare dinamicamente un form in Angular partendo da un JSON di metadati","og_url":"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/","og_site_name":"Blexin","article_published_time":"2019-03-06T00:14:00+00:00","article_modified_time":"2021-01-13T08:40:44+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":{"Scritto da":"Adolfo Arnold","Tempo di lettura stimato":"11 minuti"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/#article","isPartOf":{"@id":"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/"},"author":{"name":"Adolfo Arnold","@id":"https:\/\/blexin.com\/it\/#\/schema\/person\/0de430b61c8a48b0e9d81308817c1517"},"headline":"Form dinamiche in Angular? Si pu\u00f2!","datePublished":"2019-03-06T00:14:00+00:00","dateModified":"2021-01-13T08:40:44+00:00","mainEntityOfPage":{"@id":"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/"},"wordCount":1072,"image":{"@id":"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/#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":"it-IT"},{"@type":"WebPage","@id":"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/","url":"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/","name":"Form dinamiche in Angular? Si pu\u00f2! - Blexin","isPartOf":{"@id":"https:\/\/blexin.com\/it\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/#primaryimage"},"image":{"@id":"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/#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-01-13T08:40:44+00:00","author":{"@id":"https:\/\/blexin.com\/it\/#\/schema\/person\/0de430b61c8a48b0e9d81308817c1517"},"breadcrumb":{"@id":"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/#breadcrumb"},"inLanguage":"it-IT","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/"]}]},{"@type":"ImageObject","inLanguage":"it-IT","@id":"https:\/\/blexin.com\/it\/blog\/form-dinamiche-in-angular-si-puo\/#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\/it\/blog\/form-dinamiche-in-angular-si-puo\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blexin.com\/it\/"},{"@type":"ListItem","position":2,"name":"Form dinamiche in Angular? Si pu\u00f2!"}]},{"@type":"WebSite","@id":"https:\/\/blexin.com\/it\/#website","url":"https:\/\/blexin.com\/it\/","name":"Blexin","description":"Con noi \u00e8 semplice","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blexin.com\/it\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"it-IT"},{"@type":"Person","@id":"https:\/\/blexin.com\/it\/#\/schema\/person\/0de430b61c8a48b0e9d81308817c1517","name":"Adolfo Arnold","image":{"@type":"ImageObject","inLanguage":"it-IT","@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\/it\/author\/adolfo-arnoldblexin-com\/"}]}},"jetpack_publicize_connections":[],"jetpack_shortlink":"https:\/\/wp.me\/pcyUBx-7xe","jetpack_sharing_enabled":true,"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","_links":{"self":[{"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/posts\/28968","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/users\/196716250"}],"replies":[{"embeddable":true,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/comments?post=28968"}],"version-history":[{"count":10,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/posts\/28968\/revisions"}],"predecessor-version":[{"id":28990,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/posts\/28968\/revisions\/28990"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/media\/28969"}],"wp:attachment":[{"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/media?parent=28968"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/categories?post=28968"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blexin.com\/it\/wp-json\/wp\/v2\/tags?post=28968"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}