3

Note: I have seen several questions like my title, but they don't solve my problem. Like:

Situation
I have several template driven forms inside my angular application, with multiple date fields. I have made a special component (DatePickerComponent) that I insert as a child component in al these forms.

Problem
The variables inside the components are shared. Angular probably instantiates the same object, but I want different instances! I am note sure how to do this.

What I think is the solution
It probably has something to do with where you declare the DatepickerComponent. I declared it in the app.module.ts, but this creates one instance for the whole application. I don't know how to make sure that my parent component is forced to use two different child components.

Reasoning
I am doing this in a seperate component, because I don't have a date picker yet. Right now its a textbox with a pattern form validation. Later on I will make something better (a real date picker), and I don't want to copy paste code everywhere and make mistakes. With using 1 component, this is easily done.

Concrete code

import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core";
import {ControlContainer, NgForm} from "@angular/forms";

@Component({
  selector: 'app-datepicker-component',
  templateUrl: './datepicker.component.html',
  // Add viewProviders, so the input will be treated part of the form in the parent.
  // https://stackoverflow.com/questions/39242219/angular2-nested-template-driven-form
  viewProviders: [ { provide: ControlContainer, useExisting: NgForm } ]
})
export class DatepickerComponent implements OnInit { // stuff }

HTML file:

<div class="form-group">
  <label for="date" class="col-sm-4 control-label">{{ label }}</label>
  <div class="col-sm-8">
    <input type="text" name="date" id="date" class="form-control" [ngModel]="date" (ngModelChange)="dateChange.emit($event)"
           #inputdate="ngModel" [required]="required" pattern="^(?:(?:31(\/|-|\.)(?:0[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0[1,3-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)02\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)\d{2})$"/>
    <span class="help-block" *ngIf="required && inputdate.invalid && inputdate.errors.required && (inputdate.dirty || inputdate.touched || triedSubmit)">Datum is verplicht.</span>
    <span class="help-block" *ngIf="inputdate.invalid && inputdate.errors.pattern && (inputdate.dirty || inputdate.touched || triedSubmit)" >Datum moet formaat dd-MM-yyyy hebben.</span>
  </div>
</div>

In some other page I use:

<form>
  <app-datepicker-component label="Actief vanaf" [(date)]="date1" [required]="true" [triedSubmit]="triedSubmit"></app-datepicker-component>

  <app-datepicker-component label="Actief tot" [(date)]="date2" [required]="false" [triedSubmit]="triedSubmit"></app-datepicker-component>
</form>

The AppModule:

@NgModule({
  declarations: [
    AppComponent,
    // stuff
    DatepickerComponent
  ],
  imports: [
    // stuff
  ],
  providers: [
    // stuff],
  bootstrap: [AppComponent]
})
export class AppModule { }

Things that go wrong
The date variable is shared because the same instance is used, which I don't want. Also if the validation triggers for one of the components, it will think the other components are invalid (which they aren't).

Rico
  • 604
  • 1
  • 9
  • 16

1 Answers1

5

Actually, it's not using the same instance. The problem is that they both belong to the same form, and are using the same value for name. This causes the underlying angular form object to treat them as the same.

Your options are to have them within separate forms, or use different name values, possibly by passing the name in as an input

user184994
  • 17,791
  • 1
  • 46
  • 52
  • If date1 is not null and date2 is null, and I add ngOnInit { console.log(this.date) }, I get two logs, date1 and then date2. However, both input textboxes are empty. When i fill date2 with a date, it fills both textboxes with date 2. How is changing the name going to solve that? – Rico Apr 08 '18 at 10:59
  • 1
    Because Angular stores the values within a map. It uses the name property as the key for that map. If the name values are the same, they will override each other. Here is a stackblitz showing that it works if you give different name properties https://stackblitz.com/edit/angular-mv8dkb?file=app%2Fapp.component.html – user184994 Apr 08 '18 at 11:11
  • Magic! Thank you very much, the example was very helpful. – Rico Apr 08 '18 at 11:20