2

When clicking on a button, an element with the class of emails should be cloned.

HTML:

<form id="add-user">
  <div class="email-container">
    <div class="emails">
        <mat-form-field class="email full-width">
            <input matInput class="email-field" type="email" required placeholder="E-Mail">
        </mat-form-field>
    </div>
  </div>
    <i class="material-icons icon" id="addEmailField" (click)="clone()">add_box</i>
</form>

TS:

public clone(): void {
   const clone = document.querySelector('.emails').cloneNode(true); // clone the selector .emails
   document.querySelector('.email-container').appendChild(clone); // append it to .email-container
}

The cloning part works but the problem is the placeholder of the cloned element. If I type something into the text field, it won't disappear.

This is how it looks if I type into a cloned element:

enter image description here

What am I doing wrong? Run and test it here.

Reza Saadati
  • 5,018
  • 4
  • 27
  • 64
  • It looks like after the first one, you're cloning multiple textboxes for each input that are overlaying eachother. This is probably because of `document.querySelector('.emails').cloneNode(true); ` - this will grab all `.emails`, even if you have 2, 3 or 4 - and clone them all. – Lewis Jul 23 '18 at 12:33
  • 2
    Suggest you use array and `ngFor` instead of manipulating the dom yourself – charlietfl Jul 23 '18 at 12:34
  • 1
    I don't know material, but it looks like it's not an ordinary placeholder. It's a label that moves up on focus, so I guess its functionality is initiated on page load, so you need to look for a way to re-initiate the material functionality on the new fields. – Programmer Jul 23 '18 at 12:42

4 Answers4

1

cloneNode() clones DOM - it does not clone Angular objects. You must clone Angular components the "Angular" way. See this post: https://stackoverflow.com/a/41921531/9226213.

G. Tranter
  • 16,766
  • 1
  • 48
  • 68
1

Creating a template with ng-template solves this problem. Show the template whenever the button is clicked and also show it when the page gets loaded with ngOnInit().

HTML:

<form id="add-user">
    <ng-template #email>
        <mat-form-field class="email full-width">
            <input matInput class="email-field" type="email" required placeholder="E-Mail">
        </mat-form-field>
    </ng-template>
    <div #furtherEmails></div>
</form>
<i class="material-icons icon" id="addEmailField" (click)="clone()">add_box</i>

TS:

@ViewChild('email') email;
@ViewChild('furtherEmails', {read: ViewContainerRef}) furtherEmails;

public clone(): void {
    this.furtherEmails.createEmbeddedView(this.email);
}

ngOnInit() {
    this.clone();
}
Reza Saadati
  • 5,018
  • 4
  • 27
  • 64
0

Have you imported the material theming in your styles.css?

@import '@angular/material/prebuilt-themes/deeppurple-amber.css';
Shaunlgs
  • 59
  • 5
0

You could bind the input to a variable in the ts file and then create a mat placeholder instead and bind the ngModelChange property of the input field to a function that will disable or enable the mat placeholder. Depending on if the input field is empty.

HTML:

<mat-form-field class="email full-width">
        <mat-placeholder *ngIf="showPlaceholder">E-mail</mat-placeholder>
        <input matInput class="email-field" type="email" [(ngModel)]="email" (ngModelChange)="togglePlacholder()" required />
</mat-form-field>

TS:

email: string = ''
showPlaceholder: boolean = true

togglePlaceholder() {
    if(this.email !== '') { 
        this.showPlaceholder = false
    }else {
        this.showPlaceholder = true
    }
}