InfomediaLtd / angular2-materialize

Angular 2 support for Materialize CSS framework.
https://infomedialtd.github.io/angular2-materialize/
MIT License
406 stars 139 forks source link

Form input bug #106

Open pjpsoares opened 8 years ago

pjpsoares commented 8 years ago

When you have an input text that has the label being pushed to the top, on focus, (http://angular2-materialize.surge.sh/#/forms the last name input), if you have an ngModel setting the value, it won't push automatically to the top, overlaping both the label and the input value.

ansemjo commented 8 years ago

+1

Mine may be a different issue but it sounds very similar. It is probably related to #93 too ..

I am building my form with a couple of *ngForm= directives and one field has a prefilled value. The values are bound to a common datastore service with [(ngModel)]= directives. I have four pages with routing. If the page with the prefilled formfield is the one to be loaded first, then everything is fine. If I then navigate to another page and back the form page, then all labels overlap the filled in values.

What I get: screenshot from 2016-09-15 14-25-11

What I expect (and get, after I clicked into the field at least once after pageload): screenshot from 2016-09-15 14-25-20 (The date field is a textfield here, for sake of simplicity.)

Some code to reproduce:

<div materialize *ngFor="let field of fields.group">
  <div class="input-field col" [ngClass]="field.type === 'area' ? 's12' : 's6'">
  <!-- show textarea or simple input field based on type -->
    <textarea *ngIf="field.type === 'area'"  [id]="'orderform-' + field.id" [(ngModel)]="field.data"
      (ngModelChange)="fieldstore.rehash()" class="materialize-textarea validate"></textarea>
    <input    *ngIf="field.type === 'field'" [id]="'orderform-' + field.id" [(ngModel)]="field.data"
      (ngModelChange)="fieldstore.rehash()" type="text" class="validate">
    <label [id]="'orderform-' + field.id" class="active"> {{ field.label.de }} </label>
  </div>
</div>

As stated in #93, manually setting the active class with an [ngClass]= doesn't work either ..

I am currently migrating my project to the Angular 2.0.0 release and did not have this issue with RC.3 before.

Only1MrAnderson commented 8 years ago

@pjpsoares Set the class on the label to active

gustavolira commented 7 years ago

I'm having the same problem that @ansemjo

ansemjo commented 7 years ago

I have moved to using FormControls and haven't had this problem since. They are also a lot more powerful than simple ngModels and actually the latter uses FormControls under the hood anyway.

Example Code:

<div
  class="input-field col s12"
  [ngClass]="field.textarea ? 'm12' : 'm6'"
>

  <!-- show textarea or simple input field based on type -->

  <textarea *ngIf=" area" [id]="id" [formControl]="formcontrol"
    class="validate materialize-textarea"></textarea>

  <input    *ngIf="!area" [id]="id" [formControl]="formcontrol"
    class="validate" type="text" />

  <!-- materialize animated label -->
  <label [id]="id"> {{ label.german }} </label>

</div>
import { Component, Input, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Order } from '@app/classes';

/** A single input field for orderform data. */
@Component({
  selector    : 'inputfield',
  templateUrl : 'inputfield.component.html'
})
export class OrderInputFieldComponent implements OnInit {

  /** orderdata field to map */
  @Input() field: Order.Field;

  /** formcontrol observable */
  readonly formcontrol = new FormControl();

  /* set initial value and subscribe to changes */
  async ngOnInit() {
    this.formcontrol.setValue(this.field.value);
    this.formcontrol.valueChanges
      .debounceTime(200)
      .subscribe(value => this.field.value = value);
  }

  /** unique DOM id */
  get id () { return `orderform-${this.field.uuid}`; }

  /** field label */
  get label () { return this.field.label; }

  /** field is a textarea */
  get area () { return this.field.flags.textarea; }

}

Hope that helps.

gustavolira commented 7 years ago

I have changed to ReactiveForm and still with the same problem

RadouaneRoufid commented 7 years ago

Did any one found a good solution for this problem ?

I'm actually using the following workaround :

declare var Materialize:any;

export class MyComponent implements OnInit, AfterViewChecked {
...
  ngAfterViewChecked() {
    Materialize.updateTextFields();
  }
}

The idea is to call Materialize.updateTextFields(); after the dom initialization.

aguilarguisado commented 7 years ago

@RadouaneRoufid proposal worked for me. Thank you very much.

Nevertheless, a cleaner solution is needed in order to not including this code in every single component with a form.

rubyboy commented 7 years ago

Duplicate: https://github.com/InfomediaLtd/angular2-materialize/issues/131

wnipper commented 7 years ago

Neither solution works for me. I have the label's active class set: image but when the component finishes loading, it is unset. image

I also tried @RadouaneRoufid's solution, but it generates an error. image

Even though it is clearly a function. image

Interestingly enough, the error only occurs on a refresh. If I navigate to the item from another route, it works fine. If I refresh the page on that route, either directly through the browser or via auto-refresh from ng on save, the error occurs.

edit: is it possible the issue is related to ngAfterViewChecked() on a new page load? Like, maybe the assets aren't being loaded before the view is checked? In the button example, you're calling updateTextFields() on a button click, presumably after all assets and views have been loaded.

choyno commented 7 years ago
$('.input-field label').addClass('active');
setTimeout(function(){ $('.input-field label').addClass('active'); }, 1);
RadouaneRoufid commented 7 years ago

Back to this problem !

The solution I proposed early works great but it's not good for performance. It's quite heavy because you have to implement AfterViewChecked lifecycle for each component and sometimes it does not work on first initialization.

Another solution which works is to execute updateTextFields on each route event on the main component AppComponent. :


declare var Materialize: any;
declare let $: any;

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

    public ngOnInit(): void {

        this.router.events.subscribe(evt => {

            $(document).ready(() =>
                setTimeout(() => {
                    Materialize.updateTextFields()
                }, 10)
            );

            if (!(evt instanceof NavigationEnd)) {
                return;
            }

            window.scrollTo(0, 0);
        });
    }

}