Как добавить класс "Active", когда поле ввода не пустое

Я пытаюсь создать директиву Angular 4, которая добавит class="active" на метку, когда текстовое поле не пустое

<div class="md-form">
    <input appMdbInputInit type="text" name="" id="FirstName" 
         class="form-control"   formControlName="firstName">
    <label  for="FirstName" >First Name</label>
 </div>

Результат, который я ожидаю, когда текстовое поле не пустое

 <div class="md-form">
    <input  appMdbInputInit  type="text" name="" id="FirstName" 
         class="form-control"   formControlName="firstName">
    <label class="Active"  for="FirstName" >First Name</label>
 </div>

Как я могу это сделать?

Спасибо большое

Ответ 1

Вы можете создать директиву и вставить экземпляр FormControlName, чтобы прослушать изменения значений. Добавляйте или удаляйте активный класс ярлыков при изменении значения.

Директива

import { Directive, OnInit, OnDestroy, ElementRef } from '@angular/core';
import { FormControlName } from '@angular/forms';

import { Subscription } from 'rxjs/Subscription';

@Directive({
    selector: '[setLabelActive]'
})
export class SetLabelActiveDirective implements OnDestroy {

    valueSub: Subscription; 

    constructor(
        private el: ElementRef,
        private formControlName: FormControlName // Inject FormControlName
    ) {

    }

    ngOnInit() {
        // Listen value changes
        this.valueSub = this.formControlName.valueChanges.subscribe(value => {

            // Get label
            const inputId = this.el.nativeElement.getAttribute('id'),
                label = document.querySelector(`label[for="${inputId}"]`);

            // Toggle `active` class
            if (label) {
                label.classList.toggle('active', value);
            }
        });
    }

    ngOnDestroy() {
        // Unlisten value changes
        this.valueSub.unsubscribe();
    }

}

Использование

<form [formGroup]="myForm">

    <div>
        <input setLabelActive type="text" id="FirstName" formControlName="firstName">
        <label for="FirstName">First Name</label>
    </div>

</form>

Ответ 2

Вам не нужна специальная директива для этого. Пример:

  <form [formGroup]="group">
    <input appMdbInputInit type="text" name="" id="FirstName" 
       class="form-control" formControlName="firstName">
    <label [class.Active]="group.get('firstName').value.length" for="FirstName">First Name</label>
  </form>

Демо: http://plnkr.co/edit/SUmIVCaWnJzjU7j0XHwj?p=preview

Ответ 3

Самый простой способ - использовать ссылочную переменную шаблона (см. мой другой ответ для способа сделать это с помощью директивы):

  • Определите ссылочную переменную шаблона в элементе input с свойством #myInput

    <input type="text" #myInput>
    
  • Определить привязку свойства условного класса с [class.active] = "myInput.value" с условием, являющимся значением элемента #myInput:

    <label [class.active]="myInput.value"  for="FirstName" >First Name</label>
    
  • Определите обработчик (keyup) или (изменить) mock для элемента, чтобы вызвать обнаружение изменений Angular.

    • Событие "change" активирует класс при событии размытия элемента, тогда как (keyup) позволит активировать его. это ваш выбор, который выберите в соответствии с вашими потребностями:

там вы идете!


полный пример кода:

 <div class="md-form">
    <input type="text" #myInput (keyup)="true"
         class="form-control"   formControlName="firstName">
    <label [class.active]="myInput.value"  for="FirstName" >First Name</label>
 </div>

Ответ 4

На самом деле существует более простая встроенная директива Angular [class.className] (см. мой предыдущий ответ), но если вы настаиваете на выполнении этого с помощью директивы, вот как я сделает это:

Это ваша директива makeactive.directive.ts(не забудьте добавить ее в файл app.module.ts в качестве объявления):

import {Directive, ElementRef, Input, Renderer2} from '@angular/core';

@Directive({
    selector: '[makeActive]'
})
export class ActiveDirective
{
    input;

    @Input() set makeActive(input) {
        this.input = input;
    }

    ngDoCheck() {
        let fn = (this.input && this.input.value) ? 'addClass' : 'removeClass';
        this.renderer[fn](this.el.nativeElement, 'active');
    }

    constructor(private el: ElementRef, private renderer: Renderer2) {}
}
  • в вашем шаблоне app.component.html, вам нужно добавить свойство директивы к элементу, к которому вы хотите применить класс (элемент <label>):

    <label [makeActive]="myInput">First Name</label>

  • Затем вам нужно объявить ссылочную переменную для ввода текста:

    <input type="text" #myInput>

  • Вам также необходимо зарегистрировать событие "keyup" или "change", чтобы вызвать обнаружение изменений Angular (либо при размытии, либо при нажатии клавиши вверх, как в моем предыдущем ответе):

    <input type="text" #myInput (keyup)="true">

Полный код шаблона:

<div class="md-form">
  <input type="text" #myInput (keyup)="true">
  <label [makeActive]="myInput">First Name</label>
</div>

Ответ 5

Вы можете использовать ngClass для этого созданного plunker.

<form [formGroup]="group">
<input appMdbInputInit type="text" name="" id="FirstName" 
   class="form-control" formControlName="firstName">
<label [ngClass]="{'active': group.get('firstName').value.length}" for="FirstName">First Name</label>

Ответ 6

Как упоминалось здесь, вам действительно не нужна отдельная директива для такой мелочи, но если вы этого хотите,... ну, вот оно.

UPDATE

Собственно, это можно сделать еще проще. Я обновил код.

app.module.ts

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';

import {AppComponent} from './app.component';
import {AppMdbInputInitDirective} from './app-mdb-input-init.directive';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';

@NgModule({
    declarations: [
        AppComponent,
        AppMdbInputInitDirective
    ],
    imports: [
        BrowserModule,
        FormsModule,
        ReactiveFormsModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule {
}

app.component.ts

import {Component} from '@angular/core';
import {FormBuilder, FormGroup} from '@angular/forms';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    formGroup: FormGroup;
    activeFlag$: Observable<boolean>;
    constructor(fb: FormBuilder) {
        this.formGroup = fb.group({
            firstName: fb.control('')
        });
        this.activeFlag$ = this.formGroup.get('firstName').valueChanges.map(v => !!v);
    }
}

app.component.css

.active {
    color: red;
}

app.component.html

<div class="md-form" [formGroup]="formGroup">
    <input type="text" name="" id="FirstName" class="form-control" formControlName="firstName">
    <label for="FirstName" [appMdbInputInit]="activeFlag$ | async" labelClassName="active">First Name</label>
</div>

И, наконец, самая интересная часть - app-mdb-input-init.directive.ts

import {Directive, ElementRef, Input, Renderer2} from '@angular/core';

@Directive({
    selector: '[appMdbInputInit]'
})
export class AppMdbInputInitDirective {

    @Input()
    labelClassName: string;

    @Input()
    set appMdbInputInit(val: boolean) {
        if (val) {
            this.renderer.addClass(this.elementRef.nativeElement, this.labelClassName);
        } else {
            this.renderer.removeClass(this.elementRef.nativeElement, this.labelClassName);
        }
    }

    constructor(private elementRef: ElementRef, private renderer: Renderer2) {
    }

}