Как восстановить форму переменной длины из строки запроса? - PullRequest
0 голосов
/ 17 февраля 2020

У меня есть форма поиска, которая обновляет строку запроса и URL-адрес с записью пользователя. Таким образом, пользователи могут поделиться URL-адресом с кем-то еще, и что кто-то другой получит те же результаты поиска и форму поиска, заполненную таким же образом.

Кроме того, часть формы может иметь много Записи: пользователь может нажать кнопку, чтобы добавить произвольное количество дополнительных пар полей ввода, чтобы добавить ограничения для определенного аспекта поиска.

Моя борьба с успешным повторным заполнением пар полей ввода с переменным количеством из строки запроса.

Если я использую URL localhost:4200/search?type-0=foo&value-0=bar&type-1=fun&value-1=lala&type-2=&value-2=, я бы хотел, чтобы компонент имел два элемента в searchForm.assignments: {type: 'foo', value: 'bar'} и {type: 'fun', value: 'lala'}.

Код во фрагменте очень некрасиво, и все равно не работает. Я не могу найти хороший способ заполнить форму из строки запроса. Я борюсь с множеством ошибок "объект, возможно, ноль" (отсюда и все // @ts-ignore на данный момент), а также с ошибкой ERROR Error: "Must supply a value for form control with name: 'type'." при попытке setValue (см. [0]).

Это компонент:

export class SearchComponent implements OnInit {
  public searchForm: FormGroup;
  // `| undefined` so that TS doesn't explode when it's not assigned in the
  // constructor
  public assignments: FormArray | undefined;
  public results: [] | IssueEntity.AsObject[] = [];
  public dueFrom: string = '';
  public dueTo: string = '';
  // the inputs on the page have 
  // [value]="this.searchFormValues.dueDateRange.dueFrom" etc
  // to populate them from query string. Probably not the right 
  // way of doing it.
  public searchFormValues: { [key: string]: string | [] } = {};
  // this keeps track of how many times the "add fields" button 
  // was clicked, to not add too many sets
  public extraAssignmentsSets: number = 0;

  constructor(
    private formBuilder: FormBuilder,
    // service talking to the API and doing the search
    private issuesService: IssuesService,
    private router: Router,
    private route: ActivatedRoute,
  ) {
    this.searchForm = this.formBuilder.group({
      dueDateRange: this.formBuilder.group(
        { due_from: '', due_to: '' },
        { validator: this.DateRangeValidator },
      ),
      refIds: [],
      refTypes: [],
      assignments: this.formBuilder.array([this.createAss()]),
    });
  }

  public ngOnInit(): void {
    // on loading the component, check if there are any params,
    // and if so populate the form, and execute the search
    this.route.queryParams.subscribe(params => {
      this.rehydrateSearchForm(params);
      this.search(params);
    });
  }

  // my nightmare method. tries to figure out how many 
  // extra fieldsets are required and add them. Then wrangles 
  // the type-\d and value-\d parameters to format them
  // back into a form that fits this.searchForm.
  public rehydrateSearchForm(params: { [key: string]: string }): void {
    this.searchFormValues = params;
    // @ts-ignore
    const assSetsInParamsCount = Object.keys(this.searchFormValues).filter(k =>
      k.match(/^value-\d+$/),
    ).length;
    // the page comes with one ass set on load. So to get 
    // the current count, we add one => 1 already on 
    // page + extraAssignmentsSets added = total count
    const assSetsOnPageCount = this.extraAssignmentsSets + 1;
    const requiredAssSetsCount = assSetsInParamsCount - assSetsOnPageCount;
    this.addAss(requiredAssSetsCount);

    // there are constraints in the query string, so we must
    // attempt to restore their value into the form
    if (assSetsInParamsCount > 0) {
      // extract the relevant keys' names amongst all the 
      // params from the query string
      const assData = Object.keys(this.searchFormValues).filter(k =>
        k.match(/^value-\d+$/),
      );
      // and then for each relevant key name, extract its index 
      // so we know which set to update in this.searchForm
      assData.forEach(k => {
        // @ts-ignore
        const assIndex = parseInt(k.match(/^(value|type)-(\d+)$/)[2], 10);
        // keep the old value so we add the pair to the rest, 
        // rather than overwrite everything
        // @ts-ignore
        const oldValue = this.searchForm.get('assignments').value;
        const newValue = [...oldValue];
        // and add the extra set of {type, value}
        newValue[assIndex] = params[k];
        // finally update the form (this blows up and never works)
        // @ts-ignore
        this.searchForm.get('assignments').setValue(newValue); // [0] error here ?
      });
    }
  }

  // create a new field pair
  public createAss(): FormGroup {
    return this.formBuilder.group(
      { type: '', value: '' },
      { validator: this.AssValidator },
    );
  }

  // add one or more fieldsets to the existing form
  public addAss(count: number = 1): void {
    this.assignments = this.searchForm.get('assignments') as FormArray;
    // supports adding more than one field at once, useful 
    // when restoring search from query string.

    for (let i = 0; i < count; i++) {
      this.extraAssignmentsSets += 1;
      this.assignments.push(this.createAss());
    }
  }

  // on submit, update the query params and redirect to the 
  // search route but with the updated query params. onInit 
  // will perfrom a search based on these new query params 
  // and show the result.
  public updateQueryParams(query: {
    [key: string]: string | [] | object;
  }): void {
    const queryParams = this.buildQueryParams(query);

    this.router.navigate(['/search'], {
      queryParams,
      queryParamsHandling: 'merge',
    });
  }

  public search(query: {
    refIds?: string;
    refTypes?: string;
    assignments?: { [key: string]: string }[];
    dueDateRange?: { due_from: string; due_to: string };
  }): void {
    const filters = mergeAll([
      this.buildDueDateFilter(query),
      this.buildRefFilter(query),
      this.buildAssignmentsFilter(query),
    ]);
    this.issuesService.getIssueEntities(filters).subscribe(issues => {
      this.results = issues;
    });
  }
}

Каким был бы разумный и эффективный способ добиться этого?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...