GithubHelp home page GithubHelp logo

Comments (7)

MrWolfZ avatar MrWolfZ commented on August 27, 2024

This issue isn't really caused by ngrx-forms. Have a look a this article.

The issue here is that by dispatching that action you are updating the application state which in turn updates the bindings. And since the form control state is part of a larger state tree, that tree gets updated as well, which causes bindings on the parent component to be updated, which in turn leads to the error you observe.

I had to deal with similar issues in my application and I have the following suggestion on how to fix it (although I am not 100% sure that it will work for you). Usually I would simply recommend to move the dispatch of the action into the constructor, but since you need the bound values that is not possible. However, you can dispatch the action as part of updating the binding by using a setter property (which if I remember correctly does not cause the "change after check" error). This has the upside of making your directive react to changes in the values it is provided (i.e. if the values binding is updated, a new action is dispatched), but it has the downside of having a bit more complex code to deal with the arbitrary order of binding initialization. Therefore, I propose the following code:

import { Directive, Input, OnInit } from "@angular/core";
import { FormControlState, SetUserDefinedPropertyAction } from "ngrx-forms";
import { ReferenceData } from "../../services/reference-data/reference-data.model";
import { ActionsSubject } from "@ngrx/store";

@Directive({
    selector: 'app-autocomplete[ngrxFormControlState][values]',
})
export class AutocompleteValuesDirective {
    private formControlStateId: string;
    private refData: ReferenceData[];

    @Input() set ngrxFormControlState(state: FormControlState<string>) {
        const prevStateId = this.formControlStateId;
        this.formControlStateId = state.id;

        // deal with the case that values is bound initially before the state is bound
        // or if the state's ID changes while refData is set
        if (state.id !== prevStateId && !!this.refData) {
            this.updateAutocompleteValues();
        }

    }

    @Input() values(refData: ReferenceData[]) {
        this.refData = refData;

        if (!!this.formControlStateId) {
            this.updateAutocompleteValues();
        }
    }

    constructor(private actionsSubject: ActionsSubject) { }

    updateAutocompleteValues() {
        this.actionsSubject.next(new SetUserDefinedPropertyAction(this.formControlStateId, 'AUTOCOMPLETE_VALUES', this.refData));
    }
}

from ngrx-forms.

MrWolfZ avatar MrWolfZ commented on August 27, 2024

@sylvaingirardbe did this resolve your issue?

from ngrx-forms.

sylvaingirardbe avatar sylvaingirardbe commented on August 27, 2024

I haven't been able to get to this issue again. I'll try to take a look at it this week.

from ngrx-forms.

sylvaingirardbe avatar sylvaingirardbe commented on August 27, 2024

The proposed solution didn't work... The reason I'm trying this is because I have an autocomplete control and I was trying to use the values in the user defined property to validate the user input against since nothing prohibits him from entering something completely random.

When I was first looking into this, I wasn't completely sure that, even with the values in the user defined property, it was going to work. Do you maybe have some piece of example code how to validate input against a property (values) on a control?

from ngrx-forms.

MrWolfZ avatar MrWolfZ commented on August 27, 2024

@sylvaingirardbe Sorry for the late response.

Validating a control based on a user defined property should be straight forward, something like this:

interface FormValue {
  searchTerm: string;
}

const initialState = createFormGroupState<FormValue>('some ID', {
  searchTerm: '',
});

const groupReducer = createFormGroupReducerWithUpdate<FormValue>({
  searchTerm: searchTerm => {
    if (searchTerm.userDefinedProperties.allowedValues.indexOf(searchTerm.value) >= 0) {
      return searchTerm;
    }

    return setErrors({
      invalidValue: {
        actualValue: searchTerm.value,
        allowedValues: searchTerm.userDefinedProperties.allowedValues,
      }, 
    }, searchTerm);
  };
});

The trick is of course in getting the allowedValues set on the control. The way you are doing it with an additional directive seems just fine. Alternatively you could of course just dispatch the action inside your component. However, my preferred way would be to do it in a reducer. I guess you are loading the allowed values for the autocomplete from the server, right? In that case you'd have an action that sets the allowed values somewhere in the store. You could simply write a reducer for the form state that reacts to that action and sets the user defined property. Something like this:

const customFormGroupReducer = (state: FormGroupState<FormValue>, action: Action) => {
  if (action.type === 'your action that sets the allowed values') {
    return updateGroup<FormValue>({
      searchTerm: setUserDefinedProperty('allowedValues', action.payload.values),
    }, state);
  }

  return formGroupReducer(state, action);
};

from ngrx-forms.

MrWolfZ avatar MrWolfZ commented on August 27, 2024

@sylvaingirardbe have you resolved your issue by now? May I close this issue?

from ngrx-forms.

sylvaingirardbe avatar sylvaingirardbe commented on August 27, 2024

I'm replacing the autocompletes with plain old dropdowns since they weren't very user friendly so the immediate need for this is gone. I suspect it'll pop up somewhere down the line again. Until then I'll close it and consider it resolved.

Thanks!

from ngrx-forms.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.