Доброго времени суток!
Изучаю реактивные формы. Разъясните, пожалуйста, что именно в ресете форм контрола работает не совсем так, как ожидаемо?
Имеется следующий компонент:
@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), ну и данные не с сервера приходят, а замоканы (но данные будут получены прежде, чем пользователь начнет там что-то тыкать...). Почему тогда тут работает как ожидалось?