4

I have a basic form control which subscribed to the valueChanges observable.

@Component({
  selector: 'my-app',
  template: `
       <input [formControl]="control" />
       <div>{{ name$ | async | json }}</div>
   `
})
export class AppComponent implements OnInit {
  name$: Observable<string>;
  control;

  constructor(private builder: FormBuilder) {
    this.control = this.builder.control('');
  }

  ngOnInit() {
    this.name$ = this.control.valueChanges
      .pipe(
        map((name) => {
          console.log('fired', name)
          return name;
        })
      );


    this.control.setValue('2');
  }
}

When I call setValue the observer doesn't get any notification. It seems that the async pipe doesn't work. If I wrap the setValue in setTimeout, it's working. Why it behaves like that?

I also tried to add shareReplay() which also doesn't work.

4 Answers 4

3

this one solution for the problem

this.control.setValue('2'); // I move any changes at the top for startWith operater 
this.name$ = this.control.valueChanges
  .pipe(
    startWith(this.control.value),
    map((name) => {
      console.log('fired', name)
      return name;
    })
  );

Another way to make it work without the async operator this mean I have subscribe to value changes before the control value get changes to '2' and I have catch all the changes later

this.control.valueChanges
  .pipe(
    map((name) => {
      console.log('fired', name)
      return name;
    })
  )
  .subscribe ( v => this.value = v )

template

{{value}}

Why the example in the question with async doesn't work , async operator will subscribe for the observable but at the time of async subscribe the current value of the control will be '2' so there no changes that why first value will not emit as change for the form control,another thing observable is lazy so when you do subscribe you got the value any changes before will not be seens

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

2 Comments

What do u mean? The async pipe should subscribe and display the first value.
No if the subscribe happen after the value changes , observable is lazy when you do subscuibe he run in this case you problem is when the observable start working (subscribe) ethier you run the observable early or relay on async pipe the will create this with the async pipe run
2

The reason why it didn't work the view is not initialized yet (the html markup), when you call it from ngOnInit. You need to add it to ngAfterViewInit():

import AfterViewInit from '@angular/core';
export class AppComponent implements AfterViewInit{ 
   ngAfterViewInit() {
    this.name$ = this.control.valueChanges
      .pipe(
        map((name) => {
          console.log('fired', name)
          return name;
        })
      );


    this.control.setValue('2');
  }
}

you need to test it though, i didn't

Comments

1

General solution if you want an observable of a from control value in Angular :

this.value$ = defer(() => {
  return this.control.valueChanges.pipe(startWith(this.control.value));
});

By using defer, you make sure that your startWith value will be the actual value of your form when your observable is subscribed. If you didn't use defer, your startWith value would just have been the value of the form when your observable is created (not subscribed).

You can put that logic in an utils function if you want to reuse it in your app :)

// utils.ts
export function observeControlValue$(formControl: AbstractControl): Observable<any> {
    return defer(() => {
        return formControl.valueChanges.pipe(startWith(formControl.value));
    });
}

// component.ts
this.value$ = observeControlValue$(this.control);

Comments

-2

If you want to use reactive form, you should use FormControl instead of FormBuilder.

Your control should be initialized like this :

export class AppComponent implements OnInit {
public control = new FormControl('');

Html is : <input type="text" [formControl]="control">

Then you don't need to have a Observable<string> just use

<div>{{ control.value | json }}</div>

See ReactiveForm doc for details : https://angular.io/guide/reactive-forms

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.