1

I have autocomplete form control:

@Component({
    selector: 'app-autocomplete',
    templateUrl: './app-autocomplete.view.html',
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AutoCompleteFilterComponent implements OnInit, OnDestroy, OnChanges {   
@Input() value: any;
@Output() onChanged = new EventEmitter();
autoCompleteControl: FormControl = new FormControl();
private autoCompleteControlSubscription: Subscription;
constructor() { }

ngOnInit(): void {
    this.autoCompleteControl.setValue(this.value, { emitEvent: false });
    this.autoCompleteControlSubscription = this.autoCompleteControl.valueChanges
        .pipe(
            skipUndefined(),
            filter(value => value.length >= 3),
            distinctUntilChanged(),
            debounceTime(350),
            map(value => {                    
                this.onChanged.emit(value.trim());
            })
        ).subscribe();
}

ngOnChanges(changes: SimpleChanges): void {
    if (!changes.value.firstChange) {
        this.autoCompleteControl.setValue(changes.value.currentValue);
    }
}

ngOnDestroy(): void {
    if (this.autoCompleteControlSubscription) {
        this.autoCompleteControlSubscription.unsubscribe();
    }
}

I get initial value from store and pass it as @Input variable:

this.value$ = this._store$.select(s=>s.value);
<app-autocomplete [value]="value$ | async"></app-autocomplete>

The problem that I ran into is:

  1. Component loads and I pass initial value from the store.
  2. User types something in input text field.
  3. User stops typing for 350ms (debounce time).
  4. I emit value to the parent and use an Action + Reducer to keep the value in the store.
  5. this.value$ Observable reacts on store change and triggers ngOnChange method.
  6. User continue typing.
  7. Value from the store overwrites what user has already typed.

For example user typed "stri", then made short pause, then typed "string", but "store" value overwrites his "string" value and he got "stri" value that I put into the "store" before. Has anyone come across this before? The only solution we come up with is to check the focus and don't set new value.

Alex Gurskiy
  • 346
  • 1
  • 7
  • 20

1 Answers1

1

You're subscribing to changes in ngOnInit:

this.autoCompleteControlSubscription = this.autoCompleteControl.valueChanges

And ngOnChanges too.

this.autoCompleteControl.setValue(changes.value.currentValue);

I'm going to take a shot at what you're trying to do:

  1. On init you may want to patchValue and then setup the subscription so they do not interfere with each other.

  2. If you want to patch value without triggering the form's valueChanges then patch without event:

    this.form.controls[control].patchValue(value, { emitEvent: false });

Take a look at how I'm doing this with my own ControlValueAccessor control component on StackBlitz. I set the initial control value (if there is any) with writeValue

Ben Racicot
  • 5,332
  • 12
  • 66
  • 130
  • Thanks for your answer, I made a small update. Could you please check and let me know :) I tried to explain the problem in detail. – Alex Gurskiy Jun 14 '19 at 19:59
  • Hey @AlexGurskiy just checking to see if you figured this out. I would remove your OnChanges code and see how the OnInit subscription is working. Then rearchitect your code with my answer's tools. Let me know if I can help more. – Ben Racicot Jun 15 '19 at 12:45
  • Greetings, if I remove ngOnChanges hook, for sure it will solve the problem. But the thing is I cannot remove it. We have 1000+ controls and a lot of large forms in the app that work like I said :( – Alex Gurskiy Jun 15 '19 at 14:06
  • You are setting initial value with ngOnChanges correct? You need to set initial values like I said and forego OnChanges. Either way you're updating the value twice which you don't want. ngOnChanges is not a good mechanism to update form values. Also, in a CVA control I set my initial values in `writeValue` synchronously. – Ben Racicot Jun 15 '19 at 21:10