Home > database >  Angular two way binding returns to initial value
Angular two way binding returns to initial value

Time:01-21

I have 2 components: users-list and users-form in a module.

The file structure looks like this:

src
├── ...
│
├── users
│   ├── users-list
│   │      ├── users-list.component.html
│   │      └── users-list.component.ts
│   ├── users-form
│   │      ├── users-form.component.html
│   │      └── users-form.component.ts
│   └── users.module.ts
│
└── ...

The users-list component gets the users and lists them in a table with the possibility to edit the user. This will call the function editUser(id: number).

@Component({
  selector: 'app-users-list',
  templateUrl: './users-list.component.html',
  styleUrls: ['./users-list.component.scss']
})
export class UsersListComponent implements OnInit {
  @ViewChild(UsersFormComponent, { static: true }) usersFormComponent!: UsersFormComponent;

  users: User[] = [];
  users$: Subscription = new Subscription();

  user!: User;
  user$: Subscription = new Subscription();

  constructor(private adminUserService: UserService) { }

  ngOnInit(): void {
    this.getUsers();
  }

  ngOnDestroy(): void {
    this.users$.unsubscribe();
    this.user$.unsubscribe();
  }

  getUsers() {
    this.users$ = this.adminUserService.getUsers().subscribe(
      result => {
        this.users= result;
      },
      error => {
        console.log('ERROR: ', error);
      }
    );
  }



  editUser(id: number): void {

    # Find the user by its id and put this value in this.user. 
    # Then call the toggleModal() function from the user-form component.

    this.user


$ = this.adminUserService.getUserById(id).subscribe(
      result => {
        this.user= result;
      },
      error => {
        console.log('ERROR: ', error);
      }
    )
    this.usersFormComponent.toggleModal();
  }
}

My users-form component is implemented in users-list.component.html:

<app-users-form title="Edit user" [user]="user"></app-users-form>

The users-form.component.ts looks as follows:

@Component({
  selector: 'app-users-form',
  templateUrl: './users-form.component.html',
  styleUrls: ['./users-form.component.scss']
})
export class UsersFormComponent implements OnInit {
  @Input() title!: string;
  @Input() user!: User;

  showModal: boolean = false;

  putUser$: Subscription = new Subscription();

  constructor(private adminUserService: UserService) { }

  ngOnInit(): void {
  }

  ngOnDestroy(): void {
    this.putUser$.unsubscribe();
  }

  toggleModal() {
    this.showModal = !this.showModal;
  }

  onSubmit() {
    this.putUser$ = this.adminUserService.putUser(this.user.id, this.user).subscribe(
      result => {
        console.log(result);
      },
      error => {
        console.log("ERROR:", error);
      }
    );
  }
}

My users-form.component.html contains a form where the input fields are filled in using two way data binding:

<div *ngIf="showModal">
   <form (ngSubmit)="onSubmit()" #userForm="ngForm">
                    <div>
                        <div>
                            <div>
                                <label for="firstname">Voornaam</label>
                                <input type="text" name="firstname" id="firstname" [(ngModel)]="user.firstname" (ngModelChange)="myFunction()" #firstname="ngModel">
                            </div>
            
                            <div>
                                <label for="surname">Surname</label>
                                <input type="text" name="surname" id="surname" [(ngModel)]="user.surname" #surname="ngModel">
                            </div>
            
                            <div>
                                <label>Email</label>
                                <p>{{ user.email }}</p>
                            </div>
                        </div>
                    </div>
                    <div>
                        <button type="submit">
                            Save
                        </button>
                    </div>
                </form>
</div>

So the problem I encounter is that when I type something in the input-field (which is two way data binded). The input field will update to the given text and 2 seconds later go back to the initial text. I think this might has to do with me passing the user value from the users-list component to the users-form component.

Example of the bug: https://gyazo.com/02536c8acbeb80333f215ed165bff7b8

CodePudding user response:

When the edit function's http call returns

this.adminUserService.getUserById(id).subscribe(
  result => {
    this.user= result;
  }
  ...

it is likely setting this.user when the async call returns.

You can change your markup from this:

<div *ngIf="showModal">

to this

<div *ngIf="showModal && user">

which will prevent the modal from showing until the user has loaded.

But to ensure that it won't show until it returns you should explicitly set this.user to undefined before making the http call.

There are other options but they would all involve using the observables and more rxjs without subscribing in the component and more in depth than what you're looking for.

NOTE: Also make sure that you are saving your subscriptions so that you can unsubscribe when the component is destroyed.

CodePudding user response:

In my UserService I defined a timer before executing the method so I could test my skeleton loading. Little did I know the second parameter is the refresh time...

  getUsers(): Observable<User[]> {
    return timer(1000, 3000).pipe(switchMap(() => this.httpClient.get<User[]>(`${environment.baseApiUrl}/admin/users`)));
  }

So do not include a second parameter in timer() :

  getUsers(): Observable<User[]> {
    return timer(1000).pipe(switchMap(() => this.httpClient.get<User[]>(`${environment.baseApiUrl}/admin/users`)));
  }
  •  Tags:  
  • Related