File
Implements
Index
Properties
|
|
Methods
|
|
HostBindings
|
|
Accessors
|
|
Methods
navigateBack
|
navigateBack()
|
|
|
navigateNext
|
navigateNext()
|
|
|
triggerCustomAction
|
triggerCustomAction(customAction: CustomAction)
|
|
|
Readonly
defaultBackLabel
|
Default value : $localize`:@@page-action.back-button-label:Back`
|
|
Readonly
defaultCancelLabel
|
Default value : $localize`:@@page-action.cancel-button-label:Cancel`
|
|
Readonly
defaultNextLabel
|
Default value : $localize`:@@page-action.next-button-label:Next`
|
|
Accessors
nextButtonLabel
|
getnextButtonLabel()
|
|
backButtonLabel
|
getbackButtonLabel()
|
|
cancelButtonLabel
|
getcancelButtonLabel()
|
|
pageActionConfig
|
getpageActionConfig()
|
|
import {
BackLinkAdapterService,
BackLinkConfigElement,
BackLinkUtilsService
} from '@allianz/taly-common/web-components';
import {
CHANNEL,
CHANNEL_TOKEN,
CustomAction,
PageActionConfig,
TalyBusinessEventService,
TalyPageDataService
} from '@allianz/taly-core';
import { Component, DestroyRef, HostBinding, inject, OnInit } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { LocalizeFn } from '@angular/localize/init';
import { TalyFrameNavigationService } from '../../services/taly-frame-navigation-service';
import { PageActionStatus } from './model';
declare let $localize: LocalizeFn;
@Component({
selector: 'frame-actions',
templateUrl: './actions.component.html',
styleUrls: ['./actions.component.scss'],
standalone: false
})
export class ActionsComponent implements OnInit {
private backLinkData: BackLinkConfigElement | undefined;
pageActionStatus: PageActionStatus | undefined;
private talyPageDataService = inject(TalyPageDataService);
private backLinkUtilsService = inject(BackLinkUtilsService);
private businessEventService = inject(TalyBusinessEventService);
private frameNavigationService = inject(TalyFrameNavigationService, { optional: true });
private backLinkAdapterService = inject(BackLinkAdapterService, { optional: true });
private _channel = inject(CHANNEL_TOKEN);
private destroyRef = inject(DestroyRef);
readonly defaultNextLabel = $localize`:@@page-action.next-button-label:Next`;
readonly defaultBackLabel = $localize`:@@page-action.back-button-label:Back`;
readonly defaultCancelLabel = $localize`:@@page-action.cancel-button-label:Cancel`;
@HostBinding('class.expert-layout')
get isExpert() {
return this._channel === CHANNEL.EXPERT;
}
@HostBinding('class.retail-layout')
get isRetail() {
return this._channel === CHANNEL.RETAIL;
}
ngOnInit() {
this.frameNavigationService?.pageActionStatus$
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((status) => {
this.pageActionStatus = status;
this.backLinkData = this.getBackLinkData();
});
}
private getBackLinkData(): BackLinkConfigElement | undefined {
const backLinkFeatureActiveForActions =
this.backLinkUtilsService.isBackLinkFeatureActiveForActions(
Boolean(this.pageActionStatus?.isLastPage)
);
return backLinkFeatureActiveForActions ? this.backLinkAdapterService?.backLinkData : undefined;
}
get nextButtonLabel() {
if (this.backLinkData?.nextButtonLabel) {
return this.backLinkData.nextButtonLabel;
}
return this.pageActionConfig?.nextButtonLabel;
}
get backButtonLabel() {
return this.pageActionConfig?.backButtonLabel;
}
get cancelButtonLabel() {
return this.pageActionConfig?.cancelButtonLabel;
}
get pageActionConfig(): PageActionConfig | undefined {
return this.talyPageDataService.pageData?.pageActionConfig;
}
get pageId$() {
return this.talyPageDataService.pageId$;
}
navigateBack() {
// The Back button has two event listeners attached: One from Angular, one from Trackify.
// Apparently the browser waits for the first event handler (Angular) to complete before triggering the next one.
// This leads to the Trackify event ("back button clicked") being fired after the PFE triggers the
// pageChanged event. This leads to a weird chain of events for the tracking experts.
// See https://github.developer.allianz.io/gcj/ngx-pfe/issues/681 for details.
// The slightly hacky solution here is to postpone the Angular event by a single tick, using setTimeout.
// As an alternative we could trigger the Trackify event manually, but this would require us to
// add the Trackify library or PFE as a direct dependency to this component (see this commit in PFE:
// https://github.developer.allianz.io/gcj/ngx-pfe/commit/71d080b32c3ebb5cf5c7548ecde9e3edf5cfaac2).
window.setTimeout(() => {
this.frameNavigationService?.navigateBack();
});
}
navigateNext() {
// The Next button has two event listeners attached: One from Angular, one from Trackify.
// Apparently the browser waits for the first event handler (Angular) to complete before triggering the next one.
// This leads to the Trackify event ("next button clicked") being fired after the PFE triggers the
// pageChanged event. This leads to a weird chain of events for the tracking experts.
// See https://github.developer.allianz.io/gcj/ngx-pfe/issues/681 for details.
// The slightly hacky solution here is to postpone the Angular event by a single tick, using setTimeout.
// As an alternative we could trigger the Trackify event manually, but this would require us to
// add the Trackify library or PFE as a direct dependency to this component (see this commit in PFE:
// https://github.developer.allianz.io/gcj/ngx-pfe/commit/71d080b32c3ebb5cf5c7548ecde9e3edf5cfaac2).
window.setTimeout(() => {
if (this.backLinkData) {
window.location.href = this.backLinkData.path;
return;
}
this.frameNavigationService?.navigateNext();
});
}
saveOffer() {
this.frameNavigationService?.saveOffer();
}
cancel() {
this.frameNavigationService?.cancel();
}
triggerCustomAction(customAction: CustomAction) {
this.businessEventService.handleBusinessEvent({
handlerType: customAction.handlerType,
config: customAction.config
});
}
}
<!-- TODO: Provide an option to override the default templates. -->
<ng-container *ngTemplateOutlet="isExpert === true ? defaultExpertTemplate : defaultRetailTemplate">
</ng-container>
<ng-template #defaultRetailTemplate>
<div class="action-button-container">
@if (!pageActionStatus?.nextHidden) {
<button
data-testid="retail-next-button"
class="button"
nxButton="block primary"
(click)="navigateNext()"
[attr.trackId]="'btn_Nav'"
[attr.trackValue]="'forward'"
[disabled]="pageActionStatus?.nextDisabled"
*aclTag="(pageId$ | async) + '/next-button'"
>
{{ (nextButtonLabel | conditionalLabel | async) || defaultNextLabel }}
</button>
} @if (pageActionStatus?.showSaveOfferButton) {
<button
data-testid="retail-saveOffer-button"
class="button"
nxButton="block secondary"
(click)="saveOffer()"
[attr.trackId]="'btn_SaveOffer'"
[attr.trackValue]="'saveOffer'"
i18n="@@page-action.save-offer-button-label"
*aclTag="(pageId$ | async) + '/save-offer-button'"
>
Save and email proposal
</button>
} @if (pageActionStatus?.customActions?.length) { @for (customAction of
pageActionStatus?.customActions; track customAction.id) {
<button
[attr.data-testid]="'retail-' + customAction.id + '-button'"
class="button"
nxButton="block secondary"
(click)="triggerCustomAction(customAction)"
[attr.trackId]="'btn_' + customAction.id"
[attr.trackValue]="customAction.id"
*aclTag="(pageId$ | async) + '/' + customAction.id"
>
@if (customAction.icon) {
<nx-icon [name]="customAction.icon" nxIconPositionStart></nx-icon>
}
{{ customAction.label }}
</button>
} } @if (!pageActionStatus?.backHidden) {
<button
data-testid="retail-back-button"
class="button"
nxPlainButton
(click)="navigateBack()"
[attr.trackId]="'btn_Nav'"
[attr.trackValue]="'backward'"
[disabled]="pageActionStatus?.backDisabled"
*aclTag="(pageId$ | async) + '/back-button'"
>
<nx-icon name="arrow-left" nxIconPositionStart></nx-icon
>{{ (backButtonLabel | conditionalLabel | async) || defaultBackLabel }}
</button>
}
</div>
</ng-template>
<ng-template #defaultExpertTemplate>
<div class="buttons-container">
<div class="extra-button-container">
@if (pageActionStatus?.showCancelButton) {
<button
data-testid="expert-cancel-button"
nxPlainButton
(click)="cancel()"
[attr.trackId]="'btn_Cancel'"
[attr.trackValue]="'cancel'"
*aclTag="(pageId$ | async) + '/cancel-button'"
>
<nx-icon name="close" nxIconPositionStart></nx-icon>
{{ (cancelButtonLabel | conditionalLabel | async) || defaultCancelLabel }}
</button>
} @if (pageActionStatus?.showSaveOfferButton) {
<button
data-testid="expert-saveOffer-button"
class="button"
nxPlainButton
(click)="saveOffer()"
[attr.trackId]="'btn_SaveOffer'"
[attr.trackValue]="'saveOffer'"
*aclTag="(pageId$ | async) + '/save-offer-button'"
>
<nx-icon name="save-o" nxIconPositionStart></nx-icon>
<ng-container i18n="@@page-action.save-offer-button-label">
Save and email proposal
</ng-container>
</button>
} @if (pageActionStatus?.customActions?.length) { @for (customAction of
pageActionStatus?.customActions; track customAction) {
<button
[attr.data-testid]="'expert-' + customAction.id + '-button'"
class="button"
nxPlainButton
(click)="triggerCustomAction(customAction)"
[attr.trackId]="'btn_' + customAction.id"
[attr.trackValue]="customAction.id"
*aclTag="(pageId$ | async) + '/' + customAction.id"
>
@if (customAction.icon) {
<nx-icon [name]="customAction.icon" nxIconPositionStart></nx-icon>
}
{{ customAction.label }}
</button>
} }
</div>
<div class="action-button-container">
@if (!pageActionStatus?.nextHidden) {
<button
data-testid="expert-next-button"
class="button"
nxButton="primary small-medium"
(click)="navigateNext()"
[attr.trackId]="'btn_Nav'"
[attr.trackValue]="'forward'"
[disabled]="pageActionStatus?.nextDisabled"
*aclTag="(pageId$ | async) + '/next-button'"
>
{{ (nextButtonLabel | conditionalLabel | async) || defaultNextLabel }}
</button>
} @if (!pageActionStatus?.backHidden) {
<button
data-testid="expert-back-button"
class="button"
[nxButton]="
pageActionStatus?.backButtonUseTertiaryStyle
? 'tertiary small-medium'
: 'secondary small-medium'
"
(click)="navigateBack()"
[attr.trackId]="'btn_Nav'"
[attr.trackValue]="'backward'"
[disabled]="pageActionStatus?.backDisabled"
*aclTag="(pageId$ | async) + '/back-button'"
>
{{ (backButtonLabel | conditionalLabel | async) || defaultBackLabel }}
</button>
}
</div>
</div>
</ng-template>
@use '../../../styles/breakpoints.scss' as *;
:host {
display: block;
padding-block: var(--vertical-outer-section-spacing);
max-width: 100%;
width: var(--grid-max-width);
.action-button-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 32px;
width: 100%;
@media (min-width: $breakpoint-m) {
margin-inline: auto;
min-width: 256px;
width: max-content;
}
}
}
:host-context(.is-stacked),
:host-context(.is-centered) {
:host {
margin-inline: auto;
}
}
:host-context(.is-stacked) {
:host {
width: auto;
}
}
// removes padding-bottom if no buttons are visible
:host(.expert-layout):has(.buttons-container .extra-button-container:empty):has(
.buttons-container .action-button-container:empty
) {
padding-bottom: 0;
}
:host(.retail-layout):has(.action-button-container:empty) {
padding-bottom: 0;
}
:host(.expert-layout) {
.buttons-container {
display: flex;
justify-content: space-between;
}
.extra-button-container {
display: flex;
gap: 32px;
width: 100%;
}
.action-button-container {
justify-content: flex-start;
align-items: flex-end;
flex-direction: row-reverse;
gap: 16px;
margin-left: 80px;
}
.button {
min-width: 128px;
}
}
.button {
margin: 0;
min-width: 256px;
}
:host(:empty) {
padding: 0;
}
Legend
Html element with directive