MDB Select is not working in Angular 2


Topic: MDB Select is not working in Angular 2

sreedhar pro asked 5 years ago

Select component is not working in Angular2. It is working fine without Angular 2. I was wondering material select component is compatible with Angular2.

Bartłomiej Malanowski staff pro premium answered 5 years ago

We don't have support for Angular 2. I actually never tried MDB with Angular 2. You can try it yourself and share your solution

pro answered 4 years ago

I'm using MDB select with Angular 2+, my solution was to add a directive which calls the JQuery function, as such:

declare let $ : any;

@Directive({
    selector: '[md-select]'
})
export class MDSelectDirective implements AfterViewInit {
    constructor(private logger:Logger,
                private el: ElementRef) {}

    ngAfterViewInit(): void {
        // this.logger.debug("After view init", this.el);
        $(this.el.nativeElement).material_select();
    }

}
Then, simply add the directive to your select element: <select md-select ...> ... </select>

Benny Bottema pro answered 4 years ago

That is a problematic solution as you don't have a way to read/write values to it from Angular. It won't work in a angular managed <form> either. Here's my version I made last week for my own needs that properly incorporates material select into Angular2+:
import {AfterViewInit, Component, ElementRef, EventEmitter, forwardRef, Input, OnDestroy, Output, ViewChild} from "@angular/core";
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from "@angular/forms";
declare var $: any;

const DROPDOWN_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => DropdownComponent),
    multi: true
};

@Component({
    selector: 'dropdown-input',
    template: '

		<select #select class="mdb-select">
			<option value="" disabled [attr.selected]="!value">{{placeholder}}</option>
			<option *ngFor="let option of options" [attr.value]="option.value" [attr.selected]="option.value == value">{{option.displayValue}}</option>
		</select>
		<label *ngIf="label">{{label}}</label>',
    providers: [DROPDOWN_VALUE_ACCESSOR],
})
export class DropdownComponent implements ControlValueAccessor, OnDestroy, AfterViewInit  {
    @ViewChild('select')
    private select: ElementRef;
    
    private onTouched: () => {};
    private onChange: (value: string) => void;
    
    @Input() options: Array<Option>;
    @Input() value: string;
    @Input() placeholder: string = 'Choose your option';
    @Input() label: string = '';
    
    @Output() dropdownChanged = new EventEmitter();
    
    ngAfterViewInit(): void {
        $(this.select.nativeElement).material_select();
        $(this.select.nativeElement).on('change', (e) => this.selectValue($(':selected', this.select.nativeElement).val()));
    }
    
    writeValue(value: string) {
        this.value = value;
        $(this.select.nativeElement).val(value);
        $(this.select.nativeElement).material_select();
    }
    
    registerOnChange(fn: any) {
        this.onChange = fn;
    }
    
    registerOnTouched(fn: any) {
        this.onTouched = fn;
    }
    
    selectValue(value: string) {
        this.value = value;
        this.onChange && this.onChange(this.value);
        this.onTouched && this.onTouched();
        this.dropdownChanged.next(this.value);
    }
    
    ngOnDestroy(): void {
    }
}

export class Option {
    constructor(readonly value: any, readonly displayValue: string) {
    }
}
Then to use it, here's an example of a country select:
<div class="card-block">
	<form #countryForm="ngForm" (ngSubmit)="save()">
		<div class="row">
			<div class="col-md-12">
				<div class="md-form">
					<dropdown-input [label]="'Country'" [options]="countries" [placeholder]="'Choose country'" name="inputCountry" [(ngModel)]="country"></dropdown-input>
				</div>
			</div>
		</div>
		<div class="row">
			<div class="col-md-12 text-center">
				<button type="submit" class="btn btn-primary" [disabled]="countryForm.form.pristine || countryForm.form.invalid">Save</button>
			</div>
		</div>
	</form>
</div>
The Component:
import {Component, ViewChild} from "@angular/core";
import {EditorComponent} from "../common/editor-component";
import {NgForm} from "@angular/forms";
import {Option} from "../common/dropdown-component";
import {Country} from "../../global/model/content/Country";
import {plainToClass} from "class-transformer";

@Component({
    selector: 'editor-author-address',
    template: require('./editor-author-address.template.html')
})
export class EditorAuthorAddressComponent extends EditorComponent {
    
    @ViewChild("countryForm")
    countryForm: NgForm;
    
    country: string; // assign initial value if you want, in my case an ISO code like "US"
    countries:Array<Option> = (<Array<Country>>plainToClass(Country, require('../../assets/countries.json')))
        .map((o:Country):Option => new Option(o.ISO, o.name));
    
    save(): void {
        // save country in persistence layer
        // reset form for reuse:
        this.countryForm.form.markAsPristine();
        this.countryForm.form.markAsUntouched();
        this.countryForm.form.updateValueAndValidity();
    }
}
Note there are some details regarding loading the countries, which you can ignore really. Just use your own values. Note the Option class is defined at the bottom of the dropdown component.

Benny Bottema pro answered 4 years ago

That is a problematic solution as you don't have a way to read/write values to it from Angular. It won't work in a angular managed <form> either. Here's my version I made last week for my own needs that properly incorporates material select into Angular2+:
import {AfterViewInit, Component, ElementRef, EventEmitter, forwardRef, Input, OnDestroy, Output, ViewChild} from "@angular/core";
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from "@angular/forms";
declare var $: any;

const DROPDOWN_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => DropdownComponent),
    multi: true
};

@Component({
    selector: 'dropdown-input',
    template: '
		<select #select class="mdb-select">
			<option value="" disabled [attr.selected]="!value">{{placeholder}}</option>
			<option *ngFor="let option of options" [attr.value]="option.value" [attr.selected]="option.value == value">{{option.displayValue}}</option>
		</select>
		<label *ngIf="label">{{label}}</label>',
    providers: [DROPDOWN_VALUE_ACCESSOR],
})
export class DropdownComponent implements ControlValueAccessor, OnDestroy, AfterViewInit  {
    @ViewChild('select')
    private select: ElementRef;
    
    private onTouched: () => {};
    private onChange: (value: string) => void;
    
    @Input() options: Array<Option>;
    @Input() value: string;
    @Input() placeholder: string = 'Choose your option';
    @Input() label: string = '';
    
    @Output() dropdownChanged = new EventEmitter();
    
    ngAfterViewInit(): void {
        $(this.select.nativeElement).material_select();
        $(this.select.nativeElement).on('change', (e) => this.selectValue($(':selected', this.select.nativeElement).val()));
    }
    
    writeValue(value: string) {
        this.value = value;
        $(this.select.nativeElement).val(value);
        $(this.select.nativeElement).material_select();
    }
    
    registerOnChange(fn: any) {
        this.onChange = fn;
    }
    
    registerOnTouched(fn: any) {
        this.onTouched = fn;
    }
    
    selectValue(value: string) {
        this.value = value;
        this.onChange && this.onChange(this.value);
        this.onTouched && this.onTouched();
        this.dropdownChanged.next(this.value);
    }
    
    ngOnDestroy(): void {
    }
}

export class Option {
    constructor(readonly value: any, readonly displayValue: string) {
    }
}
Then to use it, here's an example of a country select:
<div class="card-block">
	<form #countryForm="ngForm" (ngSubmit)="save()">
		<div class="row">
			<div class="col-md-12">
				<div class="md-form">
					<dropdown-input [label]="'Country'" [options]="countries" [placeholder]="'Choose country'" name="inputCountry" [(ngModel)]="country"></dropdown-input>
				</div>
			</div>
		</div>
		<div class="row">
			<div class="col-md-12 text-center">
				<button type="submit" class="btn btn-primary" [disabled]="countryForm.form.pristine || countryForm.form.invalid">Save</button>
			</div>
		</div>
	</form>
</div>
The Component:
import {Component, ViewChild} from "@angular/core";
import {EditorComponent} from "../common/editor-component";
import {NgForm} from "@angular/forms";
import {Option} from "../common/dropdown-component";
import {Country} from "../../global/model/content/Country";
import {plainToClass} from "class-transformer";

@Component({
    selector: 'editor-author-address',
    template: require('./editor-author-address.template.html')
})
export class EditorAuthorAddressComponent extends EditorComponent {
    
    @ViewChild("countryForm")
    countryForm: NgForm;
    
    country: string; // assign initial value if you want, in my case an ISO code like "US"
    countries:Array<Option> = (<Array<Country>>plainToClass(Country, require('../../assets/countries.json')))
        .map((o:Country):Option => new Option(o.ISO, o.name));
    
    save(): void {
        // save country in persistence layer
        // reset form for reuse:
        this.countryForm.form.markAsPristine();
        this.countryForm.form.markAsUntouched();
        this.countryForm.form.updateValueAndValidity();
    }
}
Note there are some details regarding loading the countries, which you can ignore really. Just use your own values. Note the Option class is defined at the bottom of the dropdown component.

Benny Bottema pro answered 4 years ago

I'm trying to post a post with code of a material_select as proper Angular component, but this board isn't letting me format it properly, so here's the 'unfixed' version (if I fix that tilde which is screwing up this post, the board doesn't add the post anymore without error). What a terrible forum board this is. --------------------------------------------------------- Original post: That is a problematic solution as you don't have a way to read/write values to it from Angular. It won't work in a angular managed <form> either. Here's my version I made last week for my own needs that properly incorporates material select into Angular2+:
import {AfterViewInit, Component, ElementRef, EventEmitter, forwardRef, Input, OnDestroy, Output, ViewChild} from "@angular/core";
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from "@angular/forms";
declare var $: any;

const DROPDOWN_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => DropdownComponent),
    multi: true
};

@Component({
    selector: 'dropdown-input',
    template: 
<select #select class="mdb-select"> <option value="" disabled [attr.selected]="!value">{{placeholder}}</option> <option *ngFor="let option of options" [attr.value]="option.value" [attr.selected]="option.value == value">{{option.displayValue}}</option> </select> <label *ngIf="label">{{label}}</label>`, providers: [DROPDOWN_VALUE_ACCESSOR], }) export class DropdownComponent implements ControlValueAccessor, OnDestroy, AfterViewInit { @ViewChild('select') private select: ElementRef; private onTouched: () => {}; private onChange: (value: string) => void; @Input() options: Array<Option>; @Input() value: string; @Input() placeholder: string = 'Choose your option'; @Input() label: string = ''; @Output() dropdownChanged = new EventEmitter(); ngAfterViewInit(): void { $(this.select.nativeElement).material_select(); $(this.select.nativeElement).on('change', (e) => this.selectValue($(':selected', this.select.nativeElement).val())); } writeValue(value: string) { this.value = value; $(this.select.nativeElement).val(value); $(this.select.nativeElement).material_select(); } registerOnChange(fn: any) { this.onChange = fn; } registerOnTouched(fn: any) { this.onTouched = fn; } selectValue(value: string) { this.value = value; this.onChange && this.onChange(this.value); this.onTouched && this.onTouched(); this.dropdownChanged.next(this.value); } ngOnDestroy(): void { } } export class Option { constructor(readonly value: any, readonly displayValue: string) { } }` Then to use it, here's an example of a country select:
<div class="card-block">
	<form #countryForm="ngForm" (ngSubmit)="save()">
		<div class="row">
			<div class="col-md-12">
				<div class="md-form">
					<dropdown-input [label]="'Country'" [options]="countries" [placeholder]="'Choose country'" name="inputCountry" [(ngModel)]="country"></dropdown-input>
				</div>
			</div>
		</div>
		<div class="row">
			<div class="col-md-12 text-center">
				<button type="submit" class="btn btn-primary" [disabled]="countryForm.form.pristine || countryForm.form.invalid">Save</button>
			</div>
		</div>
	</form>
</div>
The Component:
import {Component, ViewChild} from "@angular/core";
import {NgForm} from "@angular/forms";
import {Option} from "../common/dropdown-component";
import {Country} from "../../global/model/content/Country"; // my own model of a country (ISO, name)
import {plainToClass} from "class-transformer"; // optional helper library to convert my JSON to a TS type

@Component({
    selector: 'edit-address',
    template: require('./edit-address.template.html')
})
export class EditAddressComponent {
    
    @ViewChild("countryForm")
    countryForm: NgForm;
    
    country: string; // assign initial value if you want, in my case an ISO code like "US"
    countries:Array<Option> = (<Array<Country>>plainToClass(Country, require('../../assets/countries.json')))
        .map((o:Country):Option => new Option(o.ISO, o.name));
    
    save(): void {
        // save country in persistence layer
        // reset form for reuse:
        this.countryForm.form.markAsPristine();
        this.countryForm.form.markAsUntouched();
        this.countryForm.form.updateValueAndValidity();
    }
}
Note there are some details regarding loading the countries, which you can ignore really. Just use your own values. Note the Option class is defined at the bottom of the dropdown component.

Benny Bottema pro answered 4 years ago

I should note that I think it only works with string keys, even though the Option class accepts any as key. That's because we are managing the options manually and we don't have advanced string-key-to-object-backing-list management as Angular natively does with regular <select> elements. /edit: I updated the Option class, but Ithe forum board won't let me edit my post anymore to reflect this. Really this board is behind times.
Hello, In response to your issue, I wanted to let you know about just released MDBootstrap Angular kit, that may be a sufficient solution to this or any other issues you have had with Angular integration so far. The kit can be found at the following link: https://mdbootstrap.com/angular/ It contains all the components you could find in MDB jQuery version. We encourage you to try it out and report any bugs or issues at support@mdbootstrap.com with [Angular] prefix or create a thread on this forum. Have a great day!

ubbasel pro commented 4 years ago

Hi, I use the Angular Bootstrap with Material Design Version 4.3.7 Pro. But the problem remains the same. After trying the example as eplained here https://mdbootstrap.com/angular/advanced/material-select/#multiple the selection list is still empty. Any ideas? Franck

Dawid Adach pro commented 4 years ago

Dear , Apparently, we had a small bug in documentation - it should be mdb-select and not mdb-ng-select , I have already fixed docs. Please update your code accordingly.

HAYK TUNYAN answered 3 years ago

<mdb-select [options]="optionsSelect" placeholder="Choose your option"></mdb-select>
<label>Example label</label>

Please insert min. 20 characters.
Status

Resolved

Specification of the issue
  • User: Pro
  • Premium support: No
  • Technology: MDB Angular
  • MDB Version: -
  • Device: -
  • Browser: -
  • OS: -
  • Provided sample code: No
  • Provided link: No