Giter Site home page Giter Site logo

Comments (37)

fxck avatar fxck commented on May 31, 2024 36

Thanks for opening this. I'll get to it over the weekend. I have to refresh my memory for all the situations I encountered that I thought ngrx/forms would have helped with.


issues

  • so the first and obvious problem are async validators / using debouce operators, it's a big pita currently (angular/angular#6895), it would be nice to have validators as a chain of operators
  • another problem(rather in design, but solvable by ngrx/forms) is that it's impossible to trigger value change/validation on different event other than change.. like on blur(angular/angular#7113 (comment))
  • debouncing/throttling on value change is pain in the ass as well
  • validators depending on conditions / values from different controls would probably be much easier
  • submitted state is hard to access, as it's only accessible by exporting ngForm from the form element/directive, it's not accessible from FormControl, FormGroup
  • to make this.form.valueChanges usable inside say, combineLatest, you have to use .startWith(''), otherwise it won't resolve.. this usecase is even worse
  currentText$ = this.post$
    .filter(res => !!res)
    .switchMap(post => this.postContentForm 
      .get('text').valueChanges // this is actually filled using data from `this.post$`!!
      .startWith(post.text)
      .distinctUntilChanged()
    );
  • reactive forms do not have observable states(only values) like focused, touched, dirty
  • there is no way to type FormGroup structure, so you can't be sure that form.get('whatever') FormControl whatever actually exists
  • the whole idea of having to do this
this.someValue$.subscribe(value => this.someForm.patchValue(value))

to fill in data simply doesn't feel reactive enough and comes with its own problems, like that when it for example receives new data that for example websocket updated, while you are in middle of editing.. there are ways to deal with it, but by having the current values inside store, it would be much easier(I think?).

  • when you want to save, you have to access forms' sync value, something like
onSave() {
  if (this.form.valid) {
    this._store.next({ type: 'SAVE', payload: this.form.value })
  }
}

it would be much easier if validity and value were inside the store, you'd simply trigger the action and handle the rest inside effect or something

questions I asked myself when thinking about how to implement ngrx/forms

  • do we need similar concept to that of FormGroup / FormControl?
    • makes passing validators to elements easier
    • how to access the values?
      • via selector? using ngrx’s select() even? can we make it more straightforward to user wouldn’t need to to this._store.let(get$(‘some.key)) but just get$(‘some.key’) instead? if we add the "FormGroup / FormControl", could it be part of that
    • how to would updating work? provide action creators and let user use dispatcher or store/next, or again just use the FormGroup/Control class that would dispatch for you
  • what all needs to be saved in the store?
    • value
    • valid
    • active
    • errors
  • how to use angular’s value accessor for native elements?
    • we would need to set different selector, is it possible?
  • should be keep the sync value somewhere? will probably use it anyway to compare local state vs. store state
  • it should be possible to override default onChange of native elements and chose your own method, ie. blur instead of input, what would be the best way to do it?
  • do we need ngrxFormName directives, or just [ngrxForm]
  • how to handle nesting, for example when one element of group is invalid, set parent to invalid as well.. do it all inside reducer?

from core.

adharris avatar adharris commented on May 31, 2024 10

Complicated forms are the major pain point for my app, and it's most important feature (case management for schools, hundreds of fields across many forms, with complex conditional display and validation). While ngrx has been a boon for all other parts of my app, it hasn't helped much with my forms, which remain a highly stateful, custom, and untested mess.

I can best sum up my pain like this: It's difficult (and maybe silly) to put angular's reactive form state into the store, but I many times need to extend that state with additional data in order to render my view. I.e disable and hide this field when a second field has a specific value; change the options of the 'State/Province' field when 'Country' is changed...

I'd be very interested in helping create and using a solution that allows writing the rules to my forms be as simple and testable as ngrx makes selecting data for the view.

from core.

MrWolfZ avatar MrWolfZ commented on May 31, 2024 10

So, I've finally gotten around to releasing v1.0.0 of ngrx-forms. You can get the library on npm via

npm install ngrx-forms --save 

See the readme for a detailed user guide.

from core.

MrWolfZ avatar MrWolfZ commented on May 31, 2024 9

I've been working in a project that makes heavy use of forms. Since there was no proper solution available I've built my own version of @ngrx/forms (or rather I built two since version 1 was only a rough first draft trying to re-use reactive forms).

The project is very much still WIP, but I'd be happy if some of you could have a look and tell me your thoughts of whether this has any merit. Since I didn't get around to documenting this properly yet, I'll quickly outline the current state and open points below.

The general idea is to have the complete form state in the store, including all the flags like isDirty etc. Updates to the state therefore happen fully through reducers (which makes testing a breeze). There is one single directive so far, applied via [ngrxFormControlState]. With this directive I am completely side-stepping the whole template or reactive forms and I am only using some basic infrastructure of @angular/forms, namely the ControlValueAccessor mechanism. The directive is responsible for dispatching the proper actions to update the state. And of course it is always possible to manually update the state by dispatching actions on your own.

The reducers automatically recompute the whole state tree, i.e. if a parent group is disabled, all children get disabled, if a child is marked as dirty, all parents are marked as dirty etc. The group reducer also dynamically creates and removes child states based on the current value of the group which makes it very easy to add and remove controls. Have a look at the reducer.spec.ts to see all the scenarios covered.

The two missing things are:

  • another directive to track form submission state
  • validation infrastructure (however, this can in fact already be achieved via effects that subscribe to form state updates and set the errors via an action)

Please let me know your thoughts.

Usage examples:

reducer.ts

import { createFormGroupReducer } from '@ngrx/forms';

export interface MyFormValue {
  someTextInput: string;
  someCheckbox: boolean;
  nested: {
    someNumber: number;
  };
}

export const FORM_ID = 'some globally unique string';

export const myFormReducer = createFormGroupReducer<MyFormValue>(FORM_ID, {
  someTextInput: '',
  someCheckbox: false,
  nested: {
    someNumber: 0;
  },
});

component.ts

import { FormGroupState } from '@ngrx/forms';

@Component({ ... })
export class MyComponent {
  formState$: Observable<FormGroupState<MyFormValue>>;

  constructor(private store: Store<RootState>) {
    this.formState$ = store.select(s => s.path.to.form.state);
  }
}

component.html

<form novalidate>
  <input type="text"
         [ngrxFormControlState]="(formState$ | async).controls.someTextInput">

  <input type="checkbox"
         [ngrxFormControlState]="(formState$ | async).controls.someCheckbox">

  <input type="number"
         [ngrxFormControlState]="(formState$ | async).controls.nested.controls.someNumber">
</form>

module.ts

import { NgrxFormsModule } from '@ngrx/forms';

@NgModule({
  imports: [
    NgrxFormsModule,
  ],
})
export class MyModule{ }

from core.

MrWolfZ avatar MrWolfZ commented on May 31, 2024 9

Alright, I've implemented form submission tracking. I've also created an example application that showcases how the library manages form states.

from core.

nicolae536 avatar nicolae536 commented on May 31, 2024 8

Any news regarding this ? Right now I'm moving my application to ngrx and I really have troubles keeping the form state especially with DatePicker control for example the date picker input allows the used to insert values and it sends the value using onChangeCallback however this will trigger my store update and the store will update the component right when the user is typing, I will be willing to help with the implementation of reactive forms for ngrx I just don't have a good idea about how to use them with material design component, I need to decuple value change from write value some how. cause right now my date picker emits a value the store gets a new state the new state updates the form model and so on. Right now for any form I have I'm not able to create new state I mutate the state for that specific form which does not respect the over all pattern. Anyone has time to start a implementation I could help.

from core.

mfp22 avatar mfp22 commented on May 31, 2024 6

What's happening with this? I am very intersted.

from core.

the-destro avatar the-destro commented on May 31, 2024 5

Hi, I was wondering what movement there was on this and how to best help?

from core.

tashoecraft avatar tashoecraft commented on May 31, 2024 5

Do we have any more info on some basic design specs? I know @fxck said he saw an early document of it. If anyone has started something, or atleast has spent some time thinking through the design specs I'd love to get started working on it. I just know that this is quite large and there's a lot of parts that can go into it. So if we have a design spec to build off of to get a MVP, then maybe this community can start building out the pieces.

from core.

fxck avatar fxck commented on May 31, 2024 5

Not really. It doesn't really solve any of the problems mentioned in this issue.. The problem is that angular forms are "flawed" from the beginning.

from core.

johnchristopherjones avatar johnchristopherjones commented on May 31, 2024 5

I'm surprised nobody has mentioned this, so perhaps I'm missing something. If you want Store changes to update the form, I don't think can use any FormArrays with ReactiveForms.

The problem is that to you need to bind outgoing changes from the form to store events, and incoming store changes to patchValue the store. To prevent an infinite loop, you must use patchValue(formModel, {emitEvent: false}) to prevent the patch from emitting another valueChange.

However, to update a FormArray, you have to use setControl. I haven't been able figure out how to either disable setControl from emitting a valueChange or a reliable way to filter out the offending event. This could be fixed by either adding an options param to setControl give it parity with patchValue or make patchValue work on FormArray.

from core.

fxck avatar fxck commented on May 31, 2024 3

I haven't found a way to use 3rd party components built in angular (angular material, clarity, etc) because they aren't build with the idea of putting relevant information into the store.

Tell me about it, the concept of stateless components seem to be alien to almost everyone in the angular world. I'm trying to get ngrx/store friendly dialog component in material2 as we speak(angular/components#2853 (comment)). Dialog is one of the components where it makes the biggest sense, but it would be handy in other components as well, like the sidenav(https://github.com/angular/material2/tree/master/src/lib/sidenav) for example.

from core.

MikeRyanDev avatar MikeRyanDev commented on May 31, 2024 1

@fxck I'm also doing a lot of form intensive development over the next month. Let's capture as many issues here in that time period and work on a design for potential solutions after that.

from core.

abdulhaq-e avatar abdulhaq-e commented on May 31, 2024 1

Hello everyone.

@fxck That is a good list of issues to think about.

Since ngrx tools combines rxjs and redux, I reviewed the redux side of the upcoming @ngrx/forms by reading the docs of redux-form which seems to be popular in the React community (http://redux-form.com/). I then made a gist in which I attempted to write pseudo Angular code that imitates the way redux-form work. Of course, the core of redux-forms was left as "magic" because the code is heavily based on React. That core I believe is where Rxjs will come into play for @ngrx/forms.

Here is the gist: https://gist.github.com/abdulhaq-e/b0abc305c16f6c14b499ccfcd56459ed

from core.

NetanelBasal avatar NetanelBasal commented on May 31, 2024 1

I think it will be easier to assist if someone will write a design document for the requirements.
We can also learn from angular-redux/forms.

from core.

fxck avatar fxck commented on May 31, 2024 1

Reactive forms are always better since they are.. reactive. Recent changes in angular forms make it a little easier to just create some sort of bridge, but I still think ngrx/forms would be a better solution. I couldn't quite get in touch with @MikeRyanDev to check what's their latest opinion on this.

from core.

sdargaud avatar sdargaud commented on May 31, 2024 1

@marcelnem The link you posted may not solve all your problems. For example, it initializes the form from the store, but any subsequent change will not be taken into effect (the .take(1)).
I haven't tested without it but you may face circular calls (i.e. you update the store, which updates the form, which updates the form etc.).
Has anyone tackled this problem ?

from core.

fxck avatar fxck commented on May 31, 2024 1

@szebrowski angular/angular#18577 and afaik there's something in works for angular/angular#6895 as well (https://docs.google.com/document/d/1dlJjRXYeuHRygryK0XoFrZNqW86jH4wobftCFyYa1PA/edit#heading=h.r6gn0i8f19wz)

@marcelnem there's currently no lib that syncs forms state to the store, which is what this issue was created for.. what is described in that article barely scratches the surface.. the only advantage reactive forms over template driven forms is that they provider observable state changes, so it makes them easier to map to ngrx observables

from core.

james-schwartzkopf avatar james-schwartzkopf commented on May 31, 2024 1

To prevent an infinite loop, you must use patchValue(formModel, {emitEvent: false})

Careful with that, we found it causes issues with material2 components (placeholders don't move, etc). We ended up creating a diff object between the store and the current form values and then (if it's not empty) using that with patch value and emitEvent true.

from core.

fxck avatar fxck commented on May 31, 2024

@MikeRyan52 added couple of things I remembered and couple of question I had written down from a couple of months ago

from core.

MarkPieszak avatar MarkPieszak commented on May 31, 2024

@MikeRyan52 Excited about this idea as well! If there's anyway I can help chip in, let me know!

from core.

bortexz avatar bortexz commented on May 31, 2024

Would love to see this!

from core.

philjones88 avatar philjones88 commented on May 31, 2024

Adding interest was redirected here by an issue I added to store.

Happy to help if I can.

from core.

fxck avatar fxck commented on May 31, 2024

I think @MikeRyan52 is currently a bit busy with work, what would help is if you all who said would be interested in this project described your use cases or issues you have with @angular/forms(unless they are 1:1 with what someone else already wrote). The more the better.

from core.

tashoecraft avatar tashoecraft commented on May 31, 2024

@fxck Your writeup had a lot of my pain points but I will try to add some more, with the main focus being the reactive forms section.

The default @angular/forms library essentially creates it's own state of the formGroup/array where the status and validity of your form lives. So if you want to put any of this into the store, you're working with statusChanges and valueChanges, submitting actions for each and reducing them. But the form will always be referencing it's own controllers, not the state. So the truth isn't in the store, but in the formGroup.

I haven't found a way to use 3rd party components built in angular (angular material, clarity, etc) because they aren't build with the idea of putting relevant information into the store. I've pretty much given up on full featured solutions and just implemented the individual ones I need, and using semantic to style. If a 3rd party component library was built off of ngrx/forms, with the ability for it to work for projects not using ngrx, then ngrx can get a big push with having the broader angular community working with it rather then creating incompatible packages (this idea is fully thought out, maybe I'll try to code something up this weekend).

Forms is definitely a struggle point when implementing ngrx for a form intensive app. I was planning on building something like a ngrx/forms, but work has taken too much of the time.

from core.

JBusch avatar JBusch commented on May 31, 2024

Sounds great!

from core.

born2net avatar born2net commented on May 31, 2024

Thanks guys for raising the issue, I find myself doing a lot of busy work to connect and ngrx to the form and would love to assist in this new path...
I am wondering what is the current best practice until a full blown solution is adopted?
Is this what users are doing in the meanwhile ?

@Component({
  template: `<router-outlet [form]="form" [data]="data$ | async"></router-outlet>`
})
class SmartComponent {
  public data$ = this._store.let(someSelector$());

  public form = new FormGroup({
   foo: new FormControl(''),
   bar: new FormControl('')
  });

  constructor(private _store: Store<IState>) {}
}

@Component({
  template: `
    <form [formGroup]="form">
      <input type="text" formControlName="foo" />
      <input type="text" formControlName="bar" />
    </form>
  `
})
class DumbComponent {
  @Input()
  public form: FormGroup;

  @Input()
  set data(v) {
    if (v) {
      this.form.patchValue(v);
    }
  };
}

Regards,

Sean

from core.

abdulhaq-e avatar abdulhaq-e commented on May 31, 2024

@born2net That's exactly what I do (except that I haven't seen data passed to `routerOutlet before) and I tend to use ng2-dynamic-forms to generate the form.

from core.

born2net avatar born2net commented on May 31, 2024

ok tx!

from core.

sebelga avatar sebelga commented on May 31, 2024

Hi,
Happy to see I am not the only one having doubt on how to best deal with form data coming from the store. :) And having to put back to the store the data from the forms.
Here is how we deal with the problem, I would really appreciate advice is the solution does not seem to adhere to the redux way of doing :)

@Component({
    selector: 'my-component',
    template: `
        <form [formGroup]="userForm" (ngSubmit)="saveUser()">
            <input formControlName="name"> 
            <input formControlName="lastname"> 
            <button type="submit" >Save</button>
        </form>
    `
})
export class MyComponent implements OnInit {
    private user: User;

    constructor(private store: Store<State>) {}

    ngOnInit() {
        this.store.select(stateSelector)
                    .take(1)
                    .subscribe((user) => this.user = user);

        this.userForm = this.fb.group({
            name: [this.user.name, Validators.required],
            lastname: [this.user.lastname, Validators.required],
        });
    }
    
    // Called on submit to update the store
    saveUser() {
        this.store.dispatch({type: 'UPDATE_USER', payload: this.userForm.value})
    }

from core.

fxck avatar fxck commented on May 31, 2024

related http://stackoverflow.com/questions/43217078/angular-redux-ngrx-state-update-vs-forms

from core.

otienoanyango avatar otienoanyango commented on May 31, 2024

I found this approach interesting. Still needs a little tweaking to fit exact needs but is a great starting point.
https://netbasal.com/connect-angular-forms-to-ngrx-store-c495d17e129

from core.

marcelnem avatar marcelnem commented on May 31, 2024

Is there currently a recommendation / best practice on how to go about forms in angular with ngrx and one-way data binding? Is it currently better to use template driven angular forms or reactive angular forms with ngrx? I understand that this thread is searching for a solution close to optimal, but what is a recommended solution until we get there? Eventually, is it better to not use ngrx with forms for now and only use it for other parts of the application? Or keep using two-way data binding and update store somehow? I used React/Redux before. I am new to Angular, trying to use Angular2 + ngrx for our enterprise project.

from core.

szebrowski avatar szebrowski commented on May 31, 2024

Recent changes in angular forms make it a little easier to just create some sort of bridge

can you write a little about what changes you are talking? :)

from core.

marcelnem avatar marcelnem commented on May 31, 2024

So I understand that reactive Angular forms are recommended to be used with ngrx (instead of template-driven Angular forms). The recommendation comes from @fxck two comments up.

Now the question is how to use reactive Angular forms with ngrx. For angular-redux there is angular-redux/forms with a brief description on how it enables us to keep forms state in redux store.

I found this (not tried yet) article describing how to use reactive Angular forms with ngrx: https://netbasal.com/connect-angular-forms-to-ngrx-store-c495d17e129

Would you recommend using the approach or are there any other approaches/guides?

from core.

jayordway avatar jayordway commented on May 31, 2024

Has this been abandoned? I have a use case as to where there is a need for many large - very large - reactive forms. The composition or content of these forms changes dynamically depending on the user's roles and entitlements. I'd prefer to handle this within the reducers - reactive forms and ngrx don't still mix well yet though, correct?

from core.

fxck avatar fxck commented on May 31, 2024

The discussion moved to ngrx/platform#431

from core.

Related Issues (19)

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.