@cocomuffin

Как правильно сбросить FormControl?

Доброго времени суток!
Изучаю реактивные формы. Разъясните, пожалуйста, что именно в ресете форм контрола работает не совсем так, как ожидаемо?

Имеется следующий компонент:

@Component({
  selector: 'my-app',
  template: `
<form [formGroup]="addUserForm">
 <fieldset>
   <label for="user" class="lbl">Add user</label><br>
   <select id="user" (change)="saveName($event.target)"
                *ngIf="notMembers && notMembers.length !== 0"
                [formControl]="addUserForm.controls['userID']">
     <option *ngFor="let user of notMembers"
                value="{{user.id}}">{{user.name}}</option>
    </select>
    <div *ngIf="bool">
      <label for="userRoleID" class="lbl">With role:</label><br>
      <select id="userRoleID" [formControl]="addUserForm.controls['userRoleID']">
        <option value="1">Admin</option>
        <option value="2">Writer</option>
        <option value="3">Reader</option>
      </select>
     </div>
     <div *ngIf="members && members.length !== 0">
       <p class="lbl">Chosen users:</p>
         <div id="usersArray">
            <span class="addedTopicUser" *ngFor="let member of members">{{member.key.name}}<span (click)=deleteUserFromMembers(member.key.id)> (X) </span><br></span>
         </div>
      </div>
    </fieldset>
</form>
`
})
class AppComponent {
  addUserForm: FormGroup;
  notMembers = [{ name: 'User1', id: 1 }, { name: 'User2', id: 2 }, { name: 'User3', id: 3 }];
  members = [];
  bool = false;
  tmpName: string;
  
  constructor() {}
  
  ngOnInit() {
    this.initForm();
  }
  
  initForm() {
    this.addUserForm = new FormGroup({
      userID: new FormControl(null),
      userRoleID: new FormControl(null)
    });
    
    const userID$ = this.addUserForm.controls['userID'].valueChanges;
    const userRoleID$ = this.addUserForm.controls['userRoleID'].valueChanges;
    
    userID$.subscribe(state => {
      this.bool = !!state;
    });

    userRoleID$
      .pipe(
        withLatestFrom(userID$)
      )
      .subscribe(res => {
        const [roleId, userId] = res;
        if (userId === null || roleId === null) {
          return;
        }
        this.addUser(userId, roleId);
        this.addUserForm.controls['userID'].reset();
        this.addUserForm.controls['userRoleID'].reset();
      });
  }
  
  addUser(userId: number, roleId: number) {
    const newUser = {
      key: {
        id: +userId
        name: this.tmpName,
      },
      value: {
        id: +roleId,
      },
    };
    this.notMembers = this.notMembers.filter((member: User) => +member.id !== +userId);
    this.members.push(newUser);
  }
  
  saveName(target) {
    const {innerHTML: name} = target.selectedOptions[0];
    this.tmpName = name;
  }
  
  deleteUserFromMembers(id) {
    const memberToDelete = this.members.find(member => +member.key.id === +id);
    this.members = this.members.filter(member => +member.key.id !== +id);
    this.notMembers.push(memberToDelete.key);
  }
}


Суть сводится к тому, что пользователь выбирает из списка юзера, затем выбирает для этого юзера роль и пара "юзер айди - роль айди" записывается в массив. Далее выбранный пользователь должен убираться из массива notMembers (т.е. в select выбора пользователя на один option тоже меньше) и добавляться в массив members, а контролы выбора пользователя и роли должны вернуться в начальное состояние null. С массивами всё ок, данные перемещаются правильно, а вот с контролом незадача - после reset() select не пустой, а имеет значение первого option, при этом сам контрол имеет классы ng-valid ng-untouched ng-pristine - т.е. он вроде как и сброшен, но в то же время и не null.

Всё работает, как ожидалось, только в случае, если вот этот кусок кода
this.addUserForm.controls['userID'].reset();
this.addUserForm.controls['userRoleID'].reset();

поменять на вот этот:
setTimeout(() => this.addUserForm.controls['topicUserId'].reset(), 0);
setTimeout(() => this.addUserForm.controls['topicUserRole'].reset(), 0);


Очевидно, что дело в очередности выполнения кода, но что именно выполняется раньше, и раньше какого действия, не пойму. Если я правильно всё понимаю, то контрол сначала ресетается, а потом, т.к. с option динамические, и зависят от массива notMembers, они перерисовываются уже после того, как контролы ресетнулись.

Однако воспроизвести не получается, тут всё нормально работает, как и ожидаемо. Различие только в том, что на кодпене только интересующий кусок компонента и связанного с этим куском кода (т.е. сам компонент больше где-то раза в 2), ну и данные не с сервера приходят, а замоканы (но данные будут получены прежде, чем пользователь начнет там что-то тыкать...). Почему тогда тут работает как ожидалось?
  • Вопрос задан
  • 270 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы