Angular CanDeactivate + кнопка возврата браузера прерывает историю после двойной отмены навигацииJavascript

Форум по Javascript
Ответить
Anonymous
 Angular CanDeactivate + кнопка возврата браузера прерывает историю после двойной отмены навигации

Сообщение Anonymous »

У меня есть Angular (автономное) приложение с защитой CanDeactivate, которая предотвращает навигацию, когда форма загрязнена.
Этапы воспроизведения:
  • Перейдите к Управлению пользователями
  • Нажмите на пользователя (например, «Алиса»).
  • Нажмите Изменить роли.
  • Измените форму, чтобы она стала грязной.
  • Нажмите кнопку возврата браузера (Chrome).
Отобразится диалоговое окно подтверждения с помощью CanDeactivate Guard.
  • Если я нажму Отмена, навигация будет правильно заблокирована, и я останусь на странице.
  • Если я повторю то же действие (назад браузер → Отмена) еще раз, история браузера станет пустой.
  • После этого кнопка возврата браузера больше не будет работать (продолжительность истории равна 0).
Ожидаемое поведение:

Отмена навигации с помощью CanDeactivate не должна потреблять записи истории браузера, даже если это делается несколько раз.
Фактическое поведение:

Каждая отмененная обратная навигация браузера потребляет одну запись истории.
Guard реализация:
const canLeaveRoles: CanDeactivateFn = (component) => {
if (!component.dirty) return true;
return confirm('You have unsaved changes. Leave anyway?');
};

Примечания:
  • Кнопки возврата в приложении используют Location.back()
  • Проблема возникает только при использовании кнопки возврата браузера
  • Это постоянно происходит в Chrome
Вопрос:

Это ожидаемое поведение Angular Router/истории браузера?

Если да, то каков рекомендуемый способ предотвратить использование истории браузера при отмене навигации?
stackblitz demo
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import {
Routes,
provideRouter,
RouterOutlet,
RouterLink,
ActivatedRoute,
CanDeactivateFn,
} from '@angular/router';
import { Location, NgFor } from '@angular/common';
import { FormsModule } from '@angular/forms';

/* -------------------- App Root + Sidenav -------------------- */

@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, RouterLink],
template: `


Admin

User Management







`,
styles: [
`
.layout { display: flex; height: 100vh; font-family: Arial; }
.sidenav { width: 200px; padding: 16px; border-right: 1px solid #ddd; background: #f7f7f7; }
.sidenav a { display: block; padding: 8px 0; text-decoration: none; color: #333; }
.sidenav a.active { font-weight: bold; }
.content { padding: 16px; flex: 1; }
`,
],
})
class AppComponent {}

/* -------------------- Management -------------------- */

@Component({
standalone: true,
imports: [NgFor, RouterLink],
template: `
User Management

  • {{ user.name }}
`,
})
class ManagementComponent {
users = [
{ id: '1', name: 'Alice' },
{ id: '2', name: 'Bob' },
{ id: '3', name: 'Charlie' },
];
}

/* -------------------- User -------------------- */

@Component({
standalone: true,
imports: [RouterLink],
template: `
⬅ Back

User {{ userId }}


Roles
Edit roles

`,
})
class UserComponent {
userId = this.route.snapshot.paramMap.get('userId');

constructor(private route: ActivatedRoute, private location: Location) {}

back() {
this.location.back();
}
}

/* -------------------- Roles -------------------- */

@Component({
standalone: true,
imports: [FormsModule],
template: `
Edit Roles




Admin




Editor



Save
Cancel
`,
})
class RolesComponent {
dirty = false;

roles = {
admin: false,
editor: true,
};

constructor(private location: Location) {}

markDirty() {
this.dirty = true;
}

save() {
this.dirty = false;
this.location.back();
}

cancel() {
this.location.back();
}
}

/* -------------------- Guard -------------------- */

const canLeaveRoles: CanDeactivateFn = (component) =>
!component.dirty || confirm('You have unsaved changes. Leave anyway?');

/* -------------------- Routes (with titles) -------------------- */

const routes: Routes = [
{
path: '',
redirectTo: 'management',
pathMatch: 'full',
},
{
path: 'management',
component: ManagementComponent,
title: 'User Management',
},
{
path: 'management/:userId',
component: UserComponent,
title: (route) => `User ${route.paramMap.get('userId')}`,
},
{
path: 'management/:userId/roles',
component: RolesComponent,
title: 'Edit User Roles',
canDeactivate: [canLeaveRoles],
},
];

/* -------------------- Bootstrap -------------------- */

bootstrapApplication(AppComponent, {
providers: [provideRouter(routes)],
});


Подробнее здесь: https://stackoverflow.com/questions/798 ... ling-navig
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»