Integrate Select2 into the Angular2 app
Asked Answered
I

2

11

I'm trying to integrate Select2 into the Angular2 app I'm building. I managed to get select2 running and my multiple selects are transformed as expected. My problem now is how am I supposed to get the selected values and which event should I use for the binding. I tried binding (change) event on the select element but nothing happened. Maybe I should use some other event on the created by the plugin select2-container element?
The select2 plugin is integrated following this answer.
Anybody tried similar mix? Is it possible to get it work or I have to switch to ng2-select directive instead?

Update
Bonus question :) - Even if I give up select2 and use standard multiple select, how should I get its value? I tried binding it to a property with [(ngModel)]="_selectedValues" but it remains empty when I select any option. Is the multiple checkbox the only way for multiple choice?

Update 2
For the bonus question - the workaround I found was to use one way event binding like (change)="selectedValues=$event.target.selectedOptions". Then I added a setter for the selectedValues property like this:

public set selectedValues(value: Array<any>) {
    this._selectedValues.length = 0;
    for(let v of value){
        this._selectedValues.push(v.value);
    }
};
Intracranial answered 23/3, 2016 at 8:59 Comment(1)
As for ng2-select, I've fount it to have too immature so far for it to useful for anything other than really basic use cases.Siphonophore
I
31

The only working solution for the select2 I found, is getting the value with jQuery in the ngAfterViewInit method like this:

jQuery('.fields-select').on(
        'change',
        (e) => this._selectedValues = jQuery(e.target).val()
);

So my final code looks like this:

import {Component, AfterViewInit} from 'angular2/core';

@Component({
    selector: 'filters',
    templateUrl: 'template.html'
})
export class FiltersComponent implements AfterViewInit {

    private _availableFields: Array<any> = ['Value1', 'Value2', 'Value3','Value4'];
    private _selectedFields: Array<string> = [];

    ngAfterViewInit(){
        jQuery('.fields-select').select2();
        jQuery('.fields-select').on(
            'change',
            (e) => this._selectedFields = jQuery(e.target).val()
        );
    };
}

And in the template:

<select id="fields" class="form-control fields-select" multiple="multiple">
    <option *ngFor="#field of _availableFields" [value]="field">{{field}}</option>
</select>

Hope this will save someone's life some day :)

Intracranial answered 23/3, 2016 at 13:48 Comment(6)
how do you import the select2 and jquery at same time? I am having trouble with itClino
@Clino I just added them in the entry point of my app - in my case the index.html, like this: <script src="node_modules/jquery/dist/jquery.min.js"></script> <script src="node_modules/select2/select2.min.js"></script>Intracranial
@RadoslavStoyanov Thanks for your answer. Any idea how to make jQuery select 2 compatible with Angular http and observable?Priestcraft
@RadoslavStoyanov In the latest version of Angular it should be <option *ngFor="let field of _availableFields" [value]="field">{{field}}</option>Regeneration
@Regeneration Yeah, I know there are some differences between the versions of Angular, but the question and the answer were about Angular2, which used a syntax like this :) Thank you for the comment, though ;)Intracranial
Solution is not working for me, it would be helpful if you share in plnkr.coHarry
A
3

I've made my own implementation of it, with options, value, and available items as Input params of my component. This is my first version of it, it could be very flexible and augmented pretty easily.

import { Component, ElementRef, Input, Output, AfterViewInit, EventEmitter, ViewChild } from '@angular/core';
declare var $: any;

@Component({
    selector: 'custom-select',
    template: `<select #select class="form-control"></select>`
})

export class CustomSelectComponent implements AfterViewInit {
    @ViewChild('select') private select: ElementRef;
    @Input() items: any[];
    @Input() options: any = {};
    @Input() value: any;
    @Output() valueChange = new EventEmitter<string[]>();

    constructor() { }

    ngAfterViewInit() {
        let selectElement = $(this.select.nativeElement);

        if (this.isMultiple()) {
            selectElement.attr('multiple', true);
        }

        selectElement.select2({
            data: $.map(this.items, (obj: any) => {
                let item = {
                    id: this.options.identifier ?  obj[this.options.identifier] : obj.id,
                    text: this.options.name ? obj[this.options.name] : obj.name
                };

                if ($.trim(item.id) === '') { console.warn(`No 'id' value has been set for :`, obj); }
                if ($.trim(item.text) === '') { console.warn(`No 'name' value has been set for :`, obj); }

                return item;
            })
        });
        $(selectElement).val(this.value).trigger('change');

        selectElement.on('change', (e: any) => {
            this.valueChange.emit($(e.target).val());
        });

    };

    private isMultiple() {
        return !!(this.options && this.options.multiple === true);
    }
}

And you can call it like that

<custom-select [items]="array" [options]="{multiple:true}" (valueChange)="update($event)" [value]="valueVar"></custom-select>
Alanson answered 14/2, 2017 at 15:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.