Skip to content

[NAE-1907] - Prompt user before leaving/reloading site when data does not need to be saved #208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: release/6.4.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion projects/nae-example-app/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
SnackBarVerticalPosition,
ViewService,
ProfileModule,
Dashboard
Dashboard,
NAE_SAVE_DATA_INFORM
} from '@netgrif/components-core';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {FlexLayoutModule, FlexModule} from '@angular/flex-layout';
Expand Down Expand Up @@ -246,6 +247,7 @@ export function HttpLoaderFactory(http: HttpClient) {
},
{provide: NAE_SNACKBAR_VERTICAL_POSITION, useValue: SnackBarVerticalPosition.TOP},
{provide: NAE_SNACKBAR_HORIZONTAL_POSITION, useValue: SnackBarHorizontalPosition.LEFT},
{provide: NAE_SAVE_DATA_INFORM, useValue: true},
ResourceProvider,
TranslateService,
TranslatePipe,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Component, Inject, Input, Optional} from '@angular/core';
import {AbstractDataFieldComponent} from '../models/abstract-data-field-component';
import {NAE_INFORM_ABOUT_INVALID_DATA} from '../models/invalid-data-policy-token';
import {I18nField} from './models/i18n-field';
import {NAE_SAVE_DATA_INFORM} from '../models/save-data-inform-token';

@Component({
selector: 'ncc-abstract-i18n-field',
Expand All @@ -11,7 +12,8 @@ export abstract class AbstractI18nFieldComponent extends AbstractDataFieldCompon

@Input() dataField: I18nField;

constructor(@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null) {
super(informAboutInvalidData);
constructor(@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null,
@Optional() @Inject(NAE_SAVE_DATA_INFORM) saveDataInform: boolean | null = false) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default value in inheritance chain

super(informAboutInvalidData, saveDataInform);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {DataField} from './abstract-data-field';
import {FormControl} from '@angular/forms';
import {Component, Inject, Input, OnDestroy, OnInit, Optional} from '@angular/core';
import {Component, HostListener, Inject, Input, OnDestroy, OnInit, Optional} from '@angular/core';
import {NAE_INFORM_ABOUT_INVALID_DATA} from './invalid-data-policy-token';
import {NAE_SAVE_DATA_INFORM} from './save-data-inform-token';

/**
* Holds the common functionality for all DataFieldComponents.
Expand All @@ -24,8 +25,19 @@ export abstract class AbstractDataFieldComponent implements OnInit, OnDestroy {
*/
protected _formControl: FormControl;

protected constructor(@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) protected _informAboutInvalidData: boolean | null) {
this._formControl = new FormControl('', { updateOn: 'blur' });
@HostListener('window:beforeunload', ['$event'])
beforeUnloadEventHandler(event) {
if (this._saveDataInform && this.dataField.isFocused()) {
this.dataField.unsetFocus();
(document.activeElement as HTMLElement).blur();
return false;
}
return true;
}

protected constructor(@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) protected _informAboutInvalidData: boolean | null,
@Optional() @Inject(NAE_SAVE_DATA_INFORM) protected _saveDataInform: boolean | null = false) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default value doesn't work

this._formControl = new FormControl('', {updateOn: 'blur'});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ export abstract class DataField<T> {
* */
private _input: ElementRef;

private _focused = false;

/**
* @param _stringId - ID of the data field from backend
* @param _title - displayed title of the data field from backend
Expand Down Expand Up @@ -347,6 +349,18 @@ export abstract class DataField<T> {
this._input = value;
}

public setFocus() {
this._focused = true;
}

public unsetFocus() {
this._focused = false;
}

public isFocused() {
return this._focused;
}

/**
* This function resolve type of component for HTML
* @returns type of component in string
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {InjectionToken} from '@angular/core';

/**
* Whether invalid data values should be sent to backend or not. Invalid data is NOT set to backend by default.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bad doc

* You can use this InjectionToken to override this behavior in a specific application scope.
*
* This token is ultimately injected by individual data fields, so this option can be in theory applied at a very low level of granularity.
* The library implementation doesn't allow access to such low level components, so a custom implementation is necessary to provide this
* token at such low level. Applying the token to individual task views is achievable with the default implementation.
*/
export const NAE_SAVE_DATA_INFORM = new InjectionToken<boolean>('NaeSaveDataInform');
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {NumberField} from './models/number-field';
import {AbstractDataFieldComponent} from '../models/abstract-data-field-component';
import {TranslateService} from '@ngx-translate/core';
import {NAE_INFORM_ABOUT_INVALID_DATA} from '../models/invalid-data-policy-token';
import {NAE_SAVE_DATA_INFORM} from '../models/save-data-inform-token';

@Component({
selector: 'ncc-abstract-number-field',
Expand All @@ -13,8 +14,9 @@ export abstract class AbstractNumberFieldComponent extends AbstractDataFieldComp
@Input() public dataField: NumberField;

protected constructor(protected _translate: TranslateService,
@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null) {
super(informAboutInvalidData);
@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null,
@Optional() @Inject(NAE_SAVE_DATA_INFORM) saveDataInform: boolean | null = false) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default value in inheritance chain

super(informAboutInvalidData, saveDataInform);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export * from './task-ref-field/model/task-ref-dashboard-tile';
export * from './models/boolean-label-enabled-token';
export * from './models/invalid-data-policy-token';
export * from './filter-field/models/filter-field-injection-token';
export * from './models/save-data-inform-token';

/* Enums */
export * from './models/template-appearance';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Component, Inject, Input, Optional} from '@angular/core';
import {TextField} from './models/text-field';
import {AbstractDataFieldComponent} from '../models/abstract-data-field-component';
import {NAE_INFORM_ABOUT_INVALID_DATA} from '../models/invalid-data-policy-token';
import {NAE_SAVE_DATA_INFORM} from '../models/save-data-inform-token';

@Component({
selector: 'ncc-abstract-text-field',
Expand All @@ -11,7 +12,8 @@ export abstract class AbstractTextFieldComponent extends AbstractDataFieldCompon

@Input() dataField: TextField;

protected constructor(@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null) {
super(informAboutInvalidData);
protected constructor(@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null,
@Optional() @Inject(NAE_SAVE_DATA_INFORM) saveDataInform: boolean | null = false) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default value in inheritance chain

super(informAboutInvalidData, saveDataInform);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import {TranslateService} from '@ngx-translate/core';
import {take} from 'rxjs/operators';
import {CdkTextareaAutosize} from '@angular/cdk/text-field';
import {AbstractTextErrorsComponent} from '../abstract-text-errors.component';
import {TextAreaField} from '../models/text-area-field';

@Component({
selector: 'ncc-abstract-text-area-field',
template: ''
})
export abstract class AbstractTextareaFieldComponent extends AbstractTextErrorsComponent implements AfterViewInit {

@Input() textAreaField: TextField;
@Input() textAreaField: TextAreaField;
@Input() formControlRef: FormControl;
@Input() showLargeLayout: WrappedBoolean;
@ViewChild('dynamicTextArea') dynamicTextArea: CdkTextareaAutosize;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import {Component, Inject, Optional} from '@angular/core';
import {AbstractI18nFieldComponent, NAE_INFORM_ABOUT_INVALID_DATA} from '@netgrif/components-core';
import {
AbstractI18nFieldComponent,
NAE_INFORM_ABOUT_INVALID_DATA,
NAE_SAVE_DATA_INFORM
} from '@netgrif/components-core';

@Component({
selector: 'nc-i18n-field',
Expand All @@ -8,7 +12,8 @@ import {AbstractI18nFieldComponent, NAE_INFORM_ABOUT_INVALID_DATA} from '@netgri
})
export class I18nFieldComponent extends AbstractI18nFieldComponent {

constructor(@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null) {
super(informAboutInvalidData);
constructor(@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null,
@Optional() @Inject(NAE_SAVE_DATA_INFORM) saveDataInform: boolean | null) {
super(informAboutInvalidData, saveDataInform);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
[placeholder]="textI18nField.placeholder ? textI18nField.placeholder : ''"
[required]="textI18nField.behavior.required"
[(ngModel)]="currentValue[selectedLanguage]"
(blur)="setSelectedValue()">
(focusout)="setSelectedValue(); textI18nField.unsetFocus()"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider to use dedicated function for multiple functions call on output event.

(focusin)="textI18nField.setFocus()">
<button mat-icon-button (click)="toggleFilled()"
[matTooltip]="(filledShown ? 'dataField.i18n.hideTranslations' : 'dataField.i18n.showTranslations') | translate">
<mat-icon color="warn">{{filledShown ? 'arrow_drop_up' : 'arrow_drop_down'}}</mat-icon>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
[placeholder]="numberField.placeholder"
[required]="numberField.behavior.required"
(focusout)="onFocusOut($event)"
(focusin)="onFocusIn()">
(focusin)="onFocusIn()"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

working correctly ? because there are assigned two different functions two times, isn't it better to assigned it like (focusin)="onFocusIn();numberField.setFocus()" ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider to use dedicated function for multiple functions call on output event.

(focusout)="numberField.unsetFocus()"
(focusin)="numberField.setFocus()">
<mat-hint>{{numberField.description}}</mat-hint>
<mat-error *ngIf="numberField.isInvalid(formControlRef)">{{getErrorMessage()}}</mat-error>
</mat-form-field>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
type="number"
[formControl]="formControlRef"
[placeholder]="numberField.placeholder"
[required]="numberField.behavior.required">
[required]="numberField.behavior.required"
(focusout)="numberField.unsetFocus()"
(focusin)="numberField.setFocus()">
<mat-hint>{{numberField.description}}</mat-hint>
<mat-error *ngIf="numberField.isInvalid(formControlRef)">{{getErrorMessage()}}</mat-error>
</mat-form-field>
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import {Component, Inject, Optional} from '@angular/core';
import {AbstractNumberFieldComponent, NAE_INFORM_ABOUT_INVALID_DATA} from '@netgrif/components-core';
import {
AbstractNumberFieldComponent,
NAE_INFORM_ABOUT_INVALID_DATA,
NAE_SAVE_DATA_INFORM
} from '@netgrif/components-core';
import {TranslateService} from '@ngx-translate/core';

@Component({
Expand All @@ -9,7 +13,8 @@ import {TranslateService} from '@ngx-translate/core';
})
export class NumberFieldComponent extends AbstractNumberFieldComponent {
constructor(translate: TranslateService,
@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null) {
super(translate, informAboutInvalidData);
@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null,
@Optional() @Inject(NAE_SAVE_DATA_INFORM) saveDataInform: boolean | null) {
super(translate, informAboutInvalidData, saveDataInform);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<div class="height-100" #quillContainer>
<quill-editor theme="snow" *ngIf="!formControlRef.disabled" [(ngModel)]="textAreaField.value"
[modules]="quillModules" [bounds]="quillContainer" placeholder="{{ textAreaField.placeholder |translate}}"></quill-editor>
[modules]="quillModules" [bounds]="quillContainer" placeholder="{{ textAreaField.placeholder |translate}}"
(focusin)="textAreaField.setFocus()" (focusout)="textAreaField.unsetFocus()"></quill-editor>
<mat-error *ngIf="textAreaField.isInvalid(formControlRef)">{{getErrorMessage()}}</mat-error>
<input type="hidden" [formControl]="formControlRef">
<div *ngIf="formControlRef.disabled" class="ql-snow user-select-auto" [ngClass]="{'border' : textAreaField.materialAppearance === 'outline'}">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
[placeholder]="passwordTextField.placeholder"
[required]="passwordTextField.behavior.required"
[formControl]="formControlRef"
[type]="hide ? 'password' : 'text'">
[type]="hide ? 'password' : 'text'"
(focusin)="passwordTextField.setFocus()"
(focusout)="passwordTextField.unsetFocus()">
<button mat-icon-button matSuffix type="button"
(click)="hide = !hide"
(keypress)="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export class EasymdeWrapperComponent implements OnDestroy, AfterViewInit, Contro
this._easyMDE = new EasyMDE(this.options);
this._easyMDE.value(this.textAreaField.value);
this._easyMDE.codemirror.on('change', this._onChange);
this._easyMDE.codemirror.on('focus', () => this.textAreaField.setFocus());
this._easyMDE.codemirror.on('blur', () => this.textAreaField.unsetFocus());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was tested ? is it working ?

this.formControlRef.valueChanges.subscribe(value => {
if (this._easyMDE) {
if (!this._fromEditor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
<input matInput
[placeholder]="textField.placeholder"
[required]="textField.behavior.required"
[formControl]="formControlRef">
[formControl]="formControlRef"
(focusin)="textField.setFocus()"
(focusout)="textField.unsetFocus()">
<mat-hint>{{textField.description}}</mat-hint>
<mat-error *ngIf="textField.isInvalid(formControlRef)">{{getErrorMessage()}}</mat-error>
</mat-form-field>
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import {Component, Inject, Optional} from '@angular/core';
import {AbstractTextFieldComponent, NAE_INFORM_ABOUT_INVALID_DATA, TextFieldComponent as TextFieldComponentEnum} from '@netgrif/components-core';
import {
AbstractTextFieldComponent,
NAE_INFORM_ABOUT_INVALID_DATA,
NAE_SAVE_DATA_INFORM,
TextFieldComponent as TextFieldComponentEnum
} from '@netgrif/components-core';

@Component({
selector: 'nc-text-field',
Expand All @@ -10,7 +15,8 @@ export class TextFieldComponent extends AbstractTextFieldComponent {

textFieldComponentEnum = TextFieldComponentEnum;

constructor(@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null) {
super(informAboutInvalidData);
constructor(@Optional() @Inject(NAE_INFORM_ABOUT_INVALID_DATA) informAboutInvalidData: boolean | null,
@Optional() @Inject(NAE_SAVE_DATA_INFORM) saveDataInform: boolean | null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default value in inheritance chain

super(informAboutInvalidData, saveDataInform);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
#textArea
[placeholder]="textAreaField.placeholder"
[required]="textAreaField.behavior.required"
[formControl]="formControlRef"
[(ngModel)]="textAreaField.value"
[ngStyle]="{'min-height': getHeight()+'px', 'height': getHeight()+'px'}"
#dynamicTextArea="cdkTextareaAutosize"
cdkTextareaAutosize
cdkAutosizeMinRows="1"></textarea>
cdkAutosizeMinRows="1"
(focusin)="textAreaField.setFocus()"
(focusout)="textAreaField.unsetFocus()"></textarea>
<input type="hidden" [formControl]="formControlRef">
<mat-hint>{{textAreaField.description}}</mat-hint>
<mat-error *ngIf="textAreaField.isInvalid(formControlRef)">{{getErrorMessage()}}</mat-error>
</mat-form-field>
Expand Down