0

my custom text input :

import { Component, Inject, Injector, Input, Optional, ViewChild, Output, 
EventEmitter } from '@angular/core';
import { NG_VALUE_ACCESSOR, NgModel } from '@angular/forms';
import { ValueAccessorBase } from '../base-elements/value-accessor';

@Component({
selector: 'sm-input',
templateUrl: './sm-input.component.html',
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: SmInputComponent,
multi: true,
  }],
     styleUrls: ['./sm-input.component.scss'],
  })
 export class SmInputComponent extends ValueAccessorBase<string> {
 constructor(injector: Injector) {
 super(injector);
 } 
} 

sm-input html: (i removed what not was necessary)

<div>
  <div *ngIf="label">
    <label>
       {{label}} 
    </label>
  </div>
  <div>
    <input  
      type="text" 
      pInputText
      [(ngModel)]="value"
    />
  </div>
</div>

my form:

import { HttpModule, Http } from '@angular/http';
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';

@Component({
  selector: 'sm-input-example-in-reactive-from',
  templateUrl: './sm-input-example-in-reactive-from.component.html',
  styleUrls: ['./sm-input-example-in-reactive-from.component.scss']
})
export class SmInputExampleInReactiveFromComponent {

  public firstName: string = "Bob";
  myReactiveForm: FormGroup;
  constructor(fb: FormBuilder, private http: Http) {
    this.myReactiveForm = fb.group ({
      myField: [this.firstName, [Validators.required]],
    });
  }
  onSubmit(value) {
    console.log(`Submit: ${JSON.stringify(value)}`);
  }
}

html form

<p-panel header="Reactive Form">
  <form action="" [formGroup]="myReactiveForm" (ngSubmit)="onSubmit(myReactiveForm.value)">
    <div class="ui-grid-row">
      <sm-input
        label="First Name"
        formControlName="myField">
      </sm-input> 
  </div>
  <div class="ui-grid-row">
      <div class="ui-g-2 ui-g-offset-5">
          <button type="Submit" class="" pButton [disabled]="!myReactiveForm.valid">Submit</button>
        </div> 
  </div>
</form>
</p-

in sm-input html i used in [(ngModel)]="value".

it's working. but i don't want to use in [(ngMode)]="value"

because reactive form not need to work with ngMode. i read this post Two way binding in reactive forms

and its not a good idea to mix between driven form and reactive form.

angular doc: https://angular.io/guide/reactive-forms

"...For this reason, the ngModel directive is not part of the ReactiveFormsModule".

what should i do?

thank you.

GuyBiton7
  • 1
  • 1
  • 1
  • 3
    What's the problem you have? If you don't want to use ngModel then don't use it. – yurzui Jan 25 '18 at 18:09
  • you seem to know about data binding, you use it in the example, so just use it once again on the inputs value or where ever you need it ... – Kristian Jan 25 '18 at 18:12
  • "so just use it once again on the inputs value or where ever you need it". i need to bind the formControler (firstName) to my custom component. in host component(form) i created instance of my custom component (sm-input) with formControlName bind to firstName in form.ts. how can i bind the formControler (firstName) to my custom input component without [(ngModel)]. – GuyBiton7 Jan 25 '18 at 18:29

2 Answers2

0

You are looking for the .setValue() method. As the name suggests, it allows you to programatically set the value of your reactive form control.

Here is a link to the relevant section in Angular's guides: https://angular.io/guide/reactive-forms#populate-the-form-model-with-setvalue-and-patchvalue

And here is a link to the method in the API: https://angular.io/api/forms/FormControl#setValue


First Approach (Use a BehaviorSubject to represent the current input value on your custom component) To get the value of your custom input component, you could make a stream on your custom text input which represents it's value over time.

inputValue$ = new BehaviorSubject('');

And listen to the input event on that input field.

<input (input)="onInputChange($event)">

Then update your inputValue$ stream with the most recent value in your input.

onInputChange(mostRecentInputValue: string) {
  this.inputValue$.next(mostRecentInputValue);
}

Now you have a stream on your custom input that you can subscribe to in the parent.

Finally, in your form component you can use the @ViewChild decorator to get ahold of your custom input component and thereby access it's public properties (which include the inputValue$:

@ViewChild('sm-input') myCustomInput: SmInputComponent;

ngAfterViewInit() {
  this.myCustomInput.inputValue$.subscribe(mostRecentInput => {
     this.myReactiveForm.get('myField').setValue(mostRecentInput);
  }
}

Second Approach (Use @Output)

On your custom input:

<input (input)="onInputChange($event)">

Then use an EventEmitter with @Output decorator to send that change to the parent;

@Output inputChange = new EventEmitter<string>();

onInputChange(mostRecentInputValue: string) {
  this.inputChange.emit(mostRecentInputValue);
}

Then in your parent html:

<sm-input (inputChange)="onSmInputChange($event)">

And your component:

onSmInputChange(recentValue: string) {
  this.myReactiveForm.get('myField').setValue(recentValue);
}
vince
  • 7,808
  • 3
  • 34
  • 41
  • 1
    thanks, i know about 'setValue' and 'patchValue'. but it's still not the solution. how can i bind the 'formControl' (firstName) from my fromGroup to custom input text component (sm-input). – GuyBiton7 Jan 26 '18 at 20:30
  • Updated my answer – vince Jan 26 '18 at 22:04
0

solved, thanks all!

Custom text:

import { Component, Inject, Injector, Input, Optional, ViewChild, Output, 
EventEmitter } from '@angular/core';
import { NG_VALUE_ACCESSOR, NgModel } from '@angular/forms';
import { ValueAccessorBase } from '../base-elements/value-accessor';

@Component({
selector: 'sm-input',
templateUrl: './sm-input.component.html',
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: SmInputComponent,
multi: true,
  }],
     styleUrls: ['./sm-input.component.scss'],
  })
 export class SmInputComponent extends ValueAccessorBase<string> {
 constructor(injector: Injector) {
 super(injector);
 } 

  doChange($event) {
   this.value = $event.target.value;
  }
} 

sm-input html:

<div>
  <div *ngIf="label">
    <label>
       {{label}} 
    </label>
  </div>
  <div>
    <input  
      type="text" 
      [value]"innerValue"
      (change)="doChange($event)"
    />
  </div>
</div>

(innerValue defined in ValueAccessorBase)

thanks all!

GuyBiton7
  • 1
  • 1
  • 1