Передача объекта в ngModel пользовательскому элементу управления Angular2

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

PersonSearchComponent:

  • DeveloperSearchControl
    • Name - some string input
    • Фамилия
    • ProffesionSelect - настраиваемый элемент управления, ожидающий ProffesionModel
  • BuildingSearchControl
    • Some custom controls here
  • CountryCustomControl - настраиваемый элемент управления, ожидающий CountryModel

  • PersonListComponent: -импортированные данные об элементах из PersonSearchComponent какой-либо службой

  • SomeOtherSearchComponent
    • DeveloperSearchControl - reusable

Итак, пока у меня есть рабочая версия, но я думаю, что сделал что-то плохое (возможно, мне стоит использовать FormBuilder):

Шаблон PersonSearch:

    <div>
            <developer-search-control [(ngModel)]="searchModel.developerSearchModel"></developer-search-control>
            <building-search-control [(ngModel)]="searchModel.buildingSearchModel"></building-search-control>
            <country-select [(ngModel)]="searchModel.countryModel"><country-select>
    </div>

ParentSearch component

...
export class PersonSearch {
  @Output() searchDataEmitter= new EventEmitter();//Exports data to above component which contains this, and listComponent
searchModel : PersonSearchModel : new PersonSearchModel();

    performSearch()
    {
        //gets data from service with searchModel and searchDataEmitter transpors it to above component
    }
}

Модели: PersonSearchModel:

developerSearchModel : DeveloperSearchModel = new DeveloperSearchModel();
buildingSearchModel: BuildingSearchModel = new BuildingSearchModel();
countryModel : CountryModel;

DeveloperSearchModel:

name : string
surname : string
proffesion : ProfessionModel

Шаблон developerSearchControl.component.html:

<div *ngIf="value">//This is the problem
     <input [(ngModel)]="value.name"></input>
     <input [(ngModel)]="value.surname"></input>
     <profesion-select [(ngModel)]="value.ProffesionSelect">
</div>

developerSearchControl.component:

...
@Component({
  selector: 'developer-search-control',
  templateUrl: './developerSearchControl.component.html',
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => DeveloperSearchControlComponent),
    multi: true,
  }],
})

export class DeveloperSearchControlComponent extends ElementBase<DeveloperSearchModel > {
  protected model: NgModel;

  constructor(
    @Optional() @Inject(NG_VALIDATORS) validators: Array<any>,
    @Optional() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<any>,
  ) {
    super(validators, asyncValidators);
  }
}

profesionSelect.component

    ...
@Component({
    selector: 'profesion-select',
    template: `
     <div>
        <label *ngIf="label">{{label}}</label>
        <dx-select-box 
            [dataSource]="data" 
            [(ngModel)]="value" 
            displayExpr="proffesionName" 
            [placeholder]="placeholder"
            [searchEnabled]="true"
            >
        </dx-select-box>
    </div>`,
        providers: [{
        provide: NG_VALUE_ACCESSOR,
        useExisting: ProfessionComponent,
        multi: true,
    }],
})

export class ProfessionComponent extends ElementBase<string> implements OnInit {
    private data: ProfessionModel[];
    private label: string = 'proffesion :';
    private placeholder: string = 'Select profession';
    @ViewChild(NgModel) model: NgModel;

    constructor(private proffesionService: ProfessionDataService,
        @Optional() @Inject(NG_VALIDATORS) validators: Array<any>,
        @Optional() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<any>,
    ) {
        super(validators, asyncValidators);
    }

    ngOnInit() {
        this.professionService.getDevelopersProfessions().subscribe(
            data => {
                this.data = data;
            }
        );
    }
}

ElementBase - это общий ControlValueAccessor с проверкой из: http://blog.rangle.io/angular-2-ngmodel-and-custom-form-components/

Итак, моя проблема в том, что когда я создаю шаблон для детей (developerSearch, buildingSearch), значение, переданное с помощью ngModel, не инициализируется для них, и я получаю:

EXCEPTION: Uncaught (in promise): Error: Error in ./DeveloperSearchControlComponent class DeveloperSearchControlComponent - inline template:2:33 caused by: Cannot read property 'name' of null
Error: Error in ./DeveloperSearchControlComponent class DeveloperSearchControlComponent - inline template:2:33 caused by: Cannot read property 'name' of null

Поскольку значение из ngModel при запуске равно нулю. Поэтому я должен использовать * ngFor = "value" в шаблонах дочерних компонентов, что выглядит плохо. Есть ли решение для инициализации объекта перед проверкой шаблона? или я делаю это очень неправильно?


person Mopa    schedule 07.03.2017    source источник
comment
вы пробовали оператор elvis везде, где вы используете значение? что-то вроде <input [(ngModel)]="value?.name"></input>. По крайней мере, это подавит исключение.   -  person mickdev    schedule 07.03.2017
comment
@mickdev Оператор безопасной навигации нельзя использовать в [(ngModel)];)   -  person AJT82    schedule 07.03.2017
comment
@ AJT_82 Я никогда не пробовал, но, на мой взгляд, это было возможно. Не понимаю, почему бы и нет ...   -  person mickdev    schedule 07.03.2017
comment
@mickdev Он выдает исключение, как сказал AJT_82. Уже пробовал :) но все равно в коде смотреться не очень красиво.   -  person Mopa    schedule 08.03.2017
comment
@ AJT_82, потому что вы не можете назначить что-то, что содержит ?., вам нужно разделить его на [ngModel]="foo?.bar" (ngModelChange)="foo && foo.bar = $event"   -  person Günter Zöchbauer    schedule 08.03.2017
comment
@ GünterZöchbauer Отлично, большое спасибо за информацию! Это очень полезно знать! :)   -  person AJT82    schedule 08.03.2017
comment
У меня это работает так в плунжере, но он не работает в моем немного продвинутом решении , попробую создать для него плукер. Но все же это хороший способ?   -  person Mopa    schedule 08.03.2017


Ответы (1)


Есть способ использовать двустороннюю привязку с оператором безопасной навигации:

<input [ngModel]="value?.name" (ngModelChange)="value?.name ? value.name = $event : null"> 

Реквизиты для: https://stackoverflow.com/a/36016472/5706293

person eko    schedule 08.03.2017
comment
Это ответ, мне также пришлось поработать с инициализацией моделей. - person Mopa; 10.03.2017