Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing object in ngModel to a custom control Angular2

I want to create custom control which contains other custom controls and use ngModel that connects all of them. Like :

PersonSearchComponent :

  • DeveloperSearchControl
    • Name - some string input
    • Surname
    • ProffesionSelect - custom control expecting ProffesionModel
  • BuildingSearchControl
    • Some custom controls here
  • CountryCustomControl - custom control expecting CountryModel

  • PersonListComponent : -imported data about items from PersonSearchComponent by some service

  • SomeOtherSearchComponent
    • DeveloperSearchControl - reusable

So for now I have working version, but I think i have made something bad (maybe I should use FormBuilder):

PersonSearch Template :

    <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
    }
}

Models : PersonSearchModel :

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

DeveloperSearchModel :

name : string
surname : string
proffesion : ProfessionModel

Template 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 is a generic ControlValueAccessor with validation from : http://blog.rangle.io/angular-2-ngmodel-and-custom-form-components/

So my problem is that when I create the template for children (developerSearch, buildingSearch) the value passed with ngModel is not initialized for them and i get :

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

Because the value from ngModel is null at start. So I have to use *ngFor="value" in templates of child components which looks bad. Is there any solution to initialize the object before template verification? or Im doing this very wrong?

like image 652
Mopa Avatar asked Mar 07 '17 13:03

Mopa


People also ask

What is [( ngModel )]?

The ngModel directive is a directive that is used to bind the values of the HTML controls (input, select, and textarea) or any custom form controls, and stores the required user value in a variable and we can use that variable whenever we require that value. It also is used during form validations.

Can we pass function to ngModel?

Yeah, you can use a function. In which case the ng-model will be the value returned by the function.

How to connect custom control to ngmodel in Angular 2?

Angular 2: Connect your custom control to ngModel with Control Value Accessor. NOTE: Please note that this article refers to an old version of Angular. Core concepts and ideas may still be useful but not all examples will work with current Angular without changes.

What is the use of ngmodel?

The ngmodel directive binds the value of HTML controls (input, select, textarea) to application data. With the ng-model directive you can bind the value of an input field to a variable created in Angular. The binding goes both ways. If the user changes the value inside the input field, the Angular property will also change its value.

What are custom form controls in angular?

Custom form controls/inputs are a typical pattern in complex Angular applications. It’s common to want to encapsulate HTML, CSS, and accessibility in an input component to make it easier to use in forms throughout the application. Common examples of this are datepickers, switches, dropdowns, and typeaheads.

Does ngmodel work with reactive forms/Form Builder?

We can see our custom app-switch works seamlessly with the Reactive Forms/Form Builder API just like any other text input. NgModel allows us to bind to an input with a two-way data binding syntax similar to Angular 1.x. We can use this same syntax when using a custom form control.


1 Answers

There's a way to use two-way binding with safe-navigation operator:

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

Props to: https://stackoverflow.com/a/36016472/5706293

like image 142
eko Avatar answered Sep 20 '22 03:09

eko