What is the ideal way to sort a FormArray object in Angular 2+?
Asked Answered
W

6

17

I have a formArray which consist of multiple form groups. I need to sort the array dynamically based on a boolean field present in each of the form group in the array.

The boolean field is a checkbox and at any given point of time only one checkbox can be checked (mimics radio button). So when a checkbox is clicked I need to sort the formArray based on the one that is selected.

I know the documentation suggests not to mess with the AbstractControls[] present in the formArray, so what would be the ideal way to dynamically sort the array ?

I have tried to slice the array and set the controls back into the formArray but I keep getting an error "Must supply a value for form control with name: 'primaryIndicator'."

const abstractControls = this.formArray.controls
          .slice()
          .sort((a, b) => {
            return (a as FormGroup).get('primaryIndicator').value ? -1 : (b as FormGroup).get('primaryIndicator').value ? 1 : 0;
          });
        this.formArray.setValue(abstractControls);

If this is not the right way, what would be the best approach to solve such a scenario ?

Wartow answered 8/3, 2018 at 3:18 Comment(0)
E
16

I do not think it would be the best way, but get the values from formarray, do whatever you want with it and patch them back.

var myArray = this.formArray.value;
//do sorting here with myArray.sort 
this.formArray.patchValue(myArray)

Do not change the length and schema of this.formArray. Patchvalue can only patch values, if they have same structure.

Eelgrass answered 16/3, 2018 at 15:26 Comment(3)
thanks, i did try it this too, it doesn't seem to work. Anyways I have changed my implementation to now have to loops one to look for the indicator true as the first element to be displayed, second loop for all the elements that are false. Its not an elegant solution, but thats the best I could come up with. Wish angular had a sorting predicate on the formArray too :)Wartow
I use it so in my projects.... But you are right, sorting in formarrays should be avaiable out of the box. Did you made an issue on github already?Eelgrass
could be tricky if the FormArray contains a FormGroup with yet another FormArray. The patch will fail if not all descendant FormArrays are the same. Yes you already mentioned this, but it's important for more complex form hierarchies not to forget to consider the children if they can very in length too.Conjoin
P
4

It worked for me. I did something like this with a one-liner

this.names.patchValue(this.names.value.sort((a, b) => a.name.localeCompare(b.name)));

Psychopath answered 30/12, 2020 at 20:12 Comment(0)
G
2

Sorting and Filtering

  sortArray(array: Array<any>, prop: string): Array<any> {
    return array.sort((a, b) => {
      prop.split('.').forEach(p => {
        a = a[p];
        b = b[p];
      });

      return (a > b) ? 1 : ((b > a) ? -1 : 0);
    });
  }

then the call

const cartItemsFormArr = this.cartForm.get('cartItemsCtrls') as FormArray;


this.cartForm.controls['cartItemsCtrls'] = this._fb.array(
  this.sortArray(cartItemsFormArr.controls.filter(ctrl => !ctrl.value['isChecked']), 'value.idShoppingCartDetail'));
Governance answered 3/4, 2018 at 13:30 Comment(0)
E
1

I already had an array of sorted values and use lodash's _.sortBy method like this:

const orderedTitles: Array<string> = ['title1', 'title2', 'title3'];
const pagesFormArray: FormArray = (this.form.get('pages') as FormArray);
pagesFormArray.controls = _.sortBy(pagesFormArray.controls, (control) => {
  return _.indexOf(orderedTitles, control.value.title'
});

The trouble I had with using @Lorfme's answer, was that I was extending the base Angular form controls and setting additional properties on my classes. Like this:

class MyNewFormArray extends FormArray {
  private additionalStuff;

  constructor(formArgs, additionalStuff) {
    super(formArgs);
    this.additional = additionalStuff;
  }

  someMethod() {
    return this.additionalStuff;
  }
}
Enyedy answered 3/10, 2019 at 11:56 Comment(0)
B
0

sortFormArray() {

let sortArray = this.formArray.value;
sortArray.sort((a, b) => {
  const A = a.primaryIndicator.toUpperCase();
  const B = b.primaryIndicator.toUpperCase();

  let comparison = 0;
  if (A > B) {
    comparison = 1;
  } else if (A < B) {
    comparison = -1;
  }
  return comparison;
});
this.appFormArrayData.setValue(sortArray);

}

Boatbill answered 17/2, 2021 at 11:15 Comment(0)
I
0

It`s better to clear previous FormArray and fill it with new sorted list of controls instead of just patch value on top of previous controls order.

updateForm(sortedList) {
  this.form.get('YOUR_KEY').clear();

  sortedList.forEach(entity => {
    this.form.get('YOUR_KEY').push(this.createFormGroup(entity));
  });
}
Intensify answered 6/3, 2023 at 9:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.