Модель преобразования Angular2 для пользовательского выбора компонента

В моем приложении angular2 я хотел бы иметь повторно используемый компонент выбора, который в первом черновике выглядит так:

import {Component, Input, Output, EventEmitter} from "@angular/core";
@Component({
    selector: 'my-select',
    template: `
        <select [(ngModel)]="selectedValue" (ngModelChange)="selectionChanged()">
            <option disabled="disabled" selected="selected" name="choose" value="choose">choose ...</option>
            <option *ngFor="let opt of selectModel" [ngValue]="opt">
                {{opt}}
            </option>
        </select>
    `
})
export class SelectComponent {

    @Output()
    changed: EventEmitter<any> = new EventEmitter();

    @Input()
    selectModel: any[] = [];

    selectedValue: any = 'choose';

    selectionChanged() {
        this.changed.emit(this.selectedValue);
    }
}

К сожалению, это работает только с массивом строк в качестве входного параметра, так как

{{ opt }}

будет распечатывать только [Object object] для других типов. И, таким образом, EventEmitter будет генерировать только строки.
Теперь я хотел бы иметь компонент, который я мог бы использовать примерно так:

import {Component} from "@angular/core";

export class Foo {
    bar: string;
    id: number;
    userFriendlyString: string = `id=${this.id}|bar=${this.bar}`;

    constructor(bar: string, id: number) {
        this.bar = bar;
        this.id = id;
    }
}

@Component({
    template: `<my-select [selectModel]="model" (changed)="handle($event)"></my-select>`
})
export class AppComponent {
    model: Foo[] = [new Foo('first', 1), new Foo('second', 2)];
    handle(foo: Foo): void {/* ... */}
}

Мои намерения:

  1. сообщить компоненту my-select, что отображаемые значения должны быть свойством userFriendlyString компонента Foo. Я не хочу жестко кодировать это, поскольку другие компоненты также должны иметь возможность использовать my-select с другими классами моделей. Я не представляю, как это сделать. Мой первый подход состоял в том, чтобы иметь функцию обратного вызова как @Input() для компонента my-select, но это не работает и не должно выполняться в соответствии с этот ответ. Второй подход состоял в том, чтобы переопределить toString в Foo. Тоже не работает (я предполагаю что-то вроде отсутствия динамической отправки в any...?!).
  2. получить работу EventEmitter как «ожидаемую»: должна быть возможность иметь правильный foo: Foo в функции дескриптора.

Так есть ли надежда для меня? :)


person matt    schedule 27.03.2017    source источник


Ответы (2)


Я думаю, что я бы создал общий интерфейс, в который компоненты могли бы отображать свои объекты, например:

export interface SelectListObject{
    key: string;
    value: any;
}

Затем используйте это как тип ввода в компоненте списка выбора:

@Component({
    selector: 'my-select',
    template: `
        <select [(ngModel)]="selectedValue" (ngModelChange)="selectionChanged()">
            <option disabled="disabled" selected="selected" name="choose" value="choose">choose ...</option>
            <option *ngFor="let opt of selectModel" [ngValue]="opt.value">
                {{opt.key}}
            </option>
        </select>
    `
})
export class SelectComponent {

    @Output()
    changed: EventEmitter<any> = new EventEmitter();

    @Input()
    selectModel: SelectListObject[] = [];

    selectedValue: any = 'choose';

    selectionChanged() {
        this.changed.emit(this.selectedValue);
    }
}

Таким образом, все ваши компоненты могут использовать общий интерфейс, а использование ngValue в элементе option позволяет выдавать целые объекты в качестве значений.

Вы лучше знаете свои другие модели данных, но вы можете поместить функцию карты в свой @Input компонента select, чтобы каждому компоненту не приходилось преобразовывать свои данные перед отправкой их в SelectComponent:

internalSelectModel: SelectListObject[] = [];
@Input('selectModel')
set selectModel(value){
    this.internalSelectModel = value.map(x => <SelectListObject>{key: value.name, value: value};
}

Функция handle будет уникальной для каждого компонента, использующего SelectComponent, и поэтому значение может быть любого типа, который вам нужен (отправлен).

Надеюсь, это поможет.

person Tyler Jennings    schedule 27.03.2017
comment
работает как шарм :) Я тоже имел в виду что-то подобное, но две части интерфейса, key и value, отсутствовали. - person matt; 27.03.2017

Не могли бы вы сделать что-то подобное, используйте $event, чтобы вы могли вернуть значение и все, что хотите, и установить ввод для получения массива объектов с отображением и значением, возможно: -

import {Component, Input, Output, EventEmitter} from "@angular/core";

interface selectModel {
    display: string;
    value: any;
}

@Component({
selector: 'my-select',
template: `
    <select [(ngModel)]="selectedValue" (ngModelChange)="selectionChanged($event)">
        <option disabled="disabled" selected="selected" name="choose" value="choose">choose ...</option>
        <option *ngFor="let opt of selectModel" [ngValue]="opt.value">
            {{opt.display}}
        </option>
    </select>
`
})
    export class SelectComponent {

    @Output()
    changed: EventEmitter<any> = new EventEmitter();

    @Input()
    selectModel: selectModel[];

    selectedValue: any = 'choose';

    selectionChanged(event) {
        let response = {
            value: event.target.value,
            foo: "something else"
        }
        this.changed.emit(response);
    }
}
person Joe Keene    schedule 27.03.2017
comment
Вроде того же, что и выше... Но мне нравится общий интерфейс для ввода и вывода - person matt; 27.03.2017
comment
Ах, я думаю, что ответил примерно через 2 минуты после него :) Рад, что вы решили свою проблему. - person Joe Keene; 27.03.2017