17

I want to have a template consisting of a label and textbox. This template can be reused at different places in my page. to do that i have created a MyTextComponent. I want to know how to i bind different values to the components input.

for example:

MyTextComponent

import {Component, Input} from 'angular2/core';
@Component({
    selector: 'myText',
    template:`<label>{{someLabel}}</label>
                 <input id=myId type="text" [(ngModel)]=bindModelData>
                 `
})
export class MyTextComponent {
    @Input() myId : string;
    @Input() bindModelData: any;
}

MyPageComponent

import {Component} from 'angular2/core';
import {MyTextComponent} from './MyTextComponent.component'

@Component({
    template:`<myText myId="id1" bindModelData="myString1"></myText>
              <myText myId="id2" bindModelData="myString2"></myText>
              `,
    directives:[MyTextComponent]
})
export class MyPageComponent{
    myString1: string;
    myString2: string;
}

How do i bind MyTextComponent input's ngModel to MyPageComponent local variable?

Edit: Tried after adding @Output, but it didnt work :( the interpolation in the MyPageComponent is blank as well as the log is printing undefined. However the interpolation is working for the mytextComponent. Any ideas

import {Component} from 'angular2/core';
import {MyTextComponent} from './myText.component'

@Component({
    template:`<myText myId="id1" [(bindModelData)]="myString1"></myText>
              <myText myId="id2" [(bindModelData)]="myString2"></myText>
              {{myString1}}
              {{myString2}}

              <button (click)="clicked()">Click Me</button>
              `,
    directives:[MyTextComponent],
    selector:'myPage'
})
export class MyPageComponent{
    myString1: string;
    myString2: string;

    clicked(){
        console.log(this.myString1+'--'+this.myString2);
    }
}


import {Component,Input,Output,EventEmitter} from 'angular2/core';
@Component({
    selector: 'myText',
    template:`<label>{{someLabel}}</label>
                 <input id=myId type="text" [ngModel]=bindModelData (ngModelChange)="updateData($event)">
                 modeldata - {{bindModelData}}
                 `
})
export class MyTextComponent {
    @Input() myId : string;
    @Input() bindModelData: any;
    @Output() bindModelDataChanged: any = new EventEmitter();
    updateData(event) {
       console.log('some random text');
      this.bindModelData = event;
      this.bindModelDataChanged.emit(event);
    }
}

2 Answers 2

39

Your myText is missing an @Output()

import {Component, Input, Output} from 'angular2/core';
@Component({
    selector: 'myText',
    template:`<label>{{someLabel}}</label>
                 <input id=myId type="text" [ngModel]=bindModelData (ngModelChange)="updateData($event)">
                 `
})
export class MyTextComponent {
    @Input() myId : string;
    @Input() bindModelData: any;

    // note that this must be named as the input name + "Change"
    @Output() bindModelDataChange: any = new EventEmitter();              

    updateData(event) {
      this.bindModelData = event;
      this.bindModelDataChange.emit(event);
    }
}

then you can use it like

import {Component} from 'angular2/core';
import {MyTextComponent} from './MyTextComponent.component'

@Component({
    template:`<myText myId="id1" [(bindModelData)]="myString1"></myText>
              <myText myId="id2" [(bindModelData)]="myString2"></myText>
              `,
    directives:[MyTextComponent]
})
export class MyPageComponent{
    myString1: string;
    myString2: string;
}

Plunker example

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you! It really helps. Do you know, how can I detect changes in MyPageComponent in that case without passing additional function into MyTextComponent?
I guess adding ngOnChanges() {} angular.io/docs/ts/latest/guide/lifecycle-hooks.html is what you are looking for. It is called when an @Input() value changes.
Is there any way use ngModel as the bound input? e.g. <my-text [(ngModel)]="person.name"></my-text> with @Input() ngModel: string;
The component needs to implement ControlValueAccessor, them it can be used with [(ngModel)]. There are several answers on SO that demonstrate it.
Your example looks much more intuitive than using ngModel and implementing ControlValueAccessor. With RC3 and using new forms API I also have problems with ControlalueAccessor - is it working for you under that condition? I tryed also the checkbox code from angular-material, and it also seems to have problems with new forms api.
|
1

I think this link will answer your question. http://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel

1- My component (tmg.input.ts)

import { Component, forwardRef , Input} from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';

const noop = () => {
};

export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => TmgInput),
    multi: true
};

@Component({
    template: `<md-input-container>
                <input mdInput id="id" name="name" placeholder="placeholder" [(ngModel)]="value" />
               </md-input-container>`,
    selector: 'tmg-input',
    templateUrl: './tmg.input.html',
    providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class TmgInput implements ControlValueAccessor {

    @Input() id: string;
    @Input() name: string;
    @Input() placeholder: string;

    //The internal data model
    private innerValue: any = '';

    //Placeholders for the callbacks which are later providesd
    //by the Control Value Accessor
    private onTouchedCallback: () => void = noop;
    private onChangeCallback: (_: any) => void = noop;

    //get accessor
    get value(): any {
        return this.innerValue;
    };

    //set accessor including call the onchange callback
    set value(v: any) {
        if (v !== this.innerValue) {
            this.innerValue = v;
            this.onChangeCallback(v);
        }
    }

    //Set touched on blur
    onBlur() {
        this.onTouchedCallback();
    }

    //From ControlValueAccessor interface
    writeValue(value: any) {
        if (value !== this.innerValue) {
            this.innerValue = value;
        }
    }

    //From ControlValueAccessor interface
    registerOnChange(fn: any) {
        this.onChangeCallback = fn;
    }

    //From ControlValueAccessor interface
    registerOnTouched(fn: any) {
        this.onTouchedCallback = fn;
    }

}

2- Then you can use it like this

<tmg-input [id]="'test'" [name]="'test'" [placeholder]="'Test'" [(ngModel)]="model.test"></tmg-input>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.