UNPKG

@ibenvandeveire/ngx-utils

Version:

A series of abstracts, utils, pipes and services for Angular applications, created by Iben Van de Veire.

789 lines (770 loc) 34.4 kB
import * as i0 from '@angular/core'; import { input, output, HostListener, Directive, inject, Injectable, Pipe, InjectionToken } from '@angular/core'; import { NgxWindowService } from '@ibenvandeveire/ngx-core'; import { EMPTY, fromEvent, Subject, NEVER, BehaviorSubject, ReplaySubject, filter, map, tap, takeUntil, take } from 'rxjs'; import { DomSanitizer } from '@angular/platform-browser'; import { ActivatedRoute, Router } from '@angular/router'; class FocusClickDirective { // Allow the button to ignore click events when set to true disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : [])); // Allow the function passed by the host to be executed // when the emit() method gets called /** * This directive replaces the default `click` directive and allows the user to execute * the `click` event by clicking the mouse **and** by using the `enter` key on focus. * * A tabindex of `0` gets added to the host. * * @memberof FocusClickDirective */ focusClick = output(); // Add eventhandler to the click event isClicked(event) { if (!this.disabled()) { this.focusClick.emit(event); } } // Add eventhandler to keydown event When enter is pressed and the event // isn't blocked, execute the click function of the host isEntered() { if (!this.disabled()) { this.focusClick.emit(); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: FocusClickDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.1.2", type: FocusClickDirective, isStandalone: true, selector: "[focusClick]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { focusClick: "focusClick" }, host: { listeners: { "click": "isClicked($event)", "keydown.enter": "isEntered()" }, properties: { "attr.tabIndex": "0" } }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: FocusClickDirective, decorators: [{ type: Directive, args: [{ selector: '[focusClick]', standalone: true, host: { '[attr.tabIndex]': '0', }, }] }], propDecorators: { isClicked: [{ type: HostListener, args: ['click', ['$event']] }], isEntered: [{ type: HostListener, args: ['keydown.enter'] }] } }); const Directives = [FocusClickDirective]; /** * A service that wraps the BroadCastChannel API and provides an Observable based implementation to the channel messages. * * For more information: * https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel */ class NgxBroadcastChannelService { windowService = inject(NgxWindowService); /** * A record holding all the broadcast channels */ broadcastChannel = {}; /** * initChannel * * The initChannel method initializes a new BroadcastChannel instance. * * @param args{ConstructorParameters<typeof BroadcastChannel>} - The arguments to pass to the BroadcastChannel constructor. */ initChannel(...args) { // Iben: Only run when in browser this.windowService.runInBrowser(() => { const [channelName] = args; if (!channelName) { console.error('NgxUtils: There was an attempt to initialize a BroadcastChannel without providing a name.'); return; } if (!this.broadcastChannel[channelName]) { this.broadcastChannel[channelName] = new BroadcastChannel(...args); } }); } /** * closeChannel * * The closeChannel method closes a selected BroadcastChannel instance. * * @param channelName{string} - The name of the Broadcast Channel. */ closeChannel(channelName) { if (!channelName || !this.broadcastChannel[channelName]) { return; } this.broadcastChannel[channelName].close(); delete this.broadcastChannel[channelName]; } /** * postMessage * * The postMessage method sends a message to a selected BroadcastChannel instance. * * @param channelName{string} - The name of the Broadcast Channel. * @param message{any} - The payload to send through the channel. */ postMessage(channelName, message) { if (!channelName || !this.broadcastChannel[channelName]) { console.error('NgxUtils: There was an attempt to post a message to a channel without providing a name or the selected channel does not exist. The included message was:', message); return; } this.broadcastChannel[channelName].postMessage(message); } /** * selectChannelMessages * * The selectChannelMessages method subscribes to the `message` (bc.onmessage) event of a selected BroadcastChannel instance. * * @param channelName{string} - The name of the Broadcast Channel. * @returns Observable<MessageEvent> - The message event of the channel wrapped in an observable. */ selectChannelMessages(channelName) { if (!channelName || !this.broadcastChannel[channelName]) { console.error("NgxUtils: There was an attempt to select a BroadcastChannel's messages without providing a name or the selected channel does not exist."); return EMPTY; } return fromEvent(this.broadcastChannel[channelName], 'message'); } /** * selectChannelMessageErrors * * The selectChannelMessageErrors method subscribes to the `messageerror` (bc.onmessageerror) event of a selected BroadcastChannel instance. * * @param channelName{string} - The name of the Broadcast Channel. * @returns Observable<MessageEvent> - The messageerror event of the channel wrapped in an observable. */ selectChannelMessageErrors(channelName) { if (!channelName || !this.broadcastChannel[channelName]) { console.error("NgxUtils: There was an attempt to select a BroadcastChannel's message errors without providing a name or the selected channel does not exist."); return EMPTY; } return fromEvent(this.broadcastChannel[channelName], 'messageerror'); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxBroadcastChannelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxBroadcastChannelService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxBroadcastChannelService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }] }); /** * A service that provides a SSR-proof Observable based approach to the session- and localStorage. */ class NgxStorageService { windowService = inject(NgxWindowService); /** * A record to hold the properties in the sessionStorage */ sessionStorageRecord = {}; /** * A record to hold the properties in the localStorage */ localStorageRecord = {}; /** * A subject to hold the events of the storage */ storageEventSubject = new Subject(); /** * An observable that emits whenever the session- or the localStorage was updated */ storageEvents$ = this.storageEventSubject.asObservable(); constructor() { const windowService = this.windowService; // Iben: Get the initial values of the session and the local storage windowService.runInBrowser(() => { this.setupStorage(sessionStorage, this.sessionStorageRecord); this.setupStorage(localStorage, this.localStorageRecord); }); } /** * A localStorage implementation using observables */ get localStorage() { return { getItem: (key) => this.getItem(key, localStorage), getItemObservable: (key) => this.getItemObservable(key, this.localStorageRecord), removeItem: (key) => this.removeItem(key, localStorage, this.localStorageRecord, 'local'), setItem: (key, item) => this.setItem(key, item, localStorage, this.localStorageRecord, 'local'), clear: () => this.clearStorage(localStorage, this.localStorageRecord, 'local'), }; } /** * A sessionStorage implementation using observables */ get sessionStorage() { return { getItem: (key) => this.getItem(key, sessionStorage), getItemObservable: (key) => this.getItemObservable(key, this.sessionStorageRecord), removeItem: (key) => this.removeItem(key, sessionStorage, this.sessionStorageRecord, 'session'), setItem: (key, item) => this.setItem(key, item, sessionStorage, this.sessionStorageRecord, 'session'), clear: () => this.clearStorage(sessionStorage, this.sessionStorageRecord, 'session'), }; } getItem(key, storage) { return this.parseValue(storage.getItem(key)); } /** * Returns an observable version of the storage value * * @param key - The key of the storage value * @param record - The storage record */ getItemObservable(key, record) { // Iben: Return NEVER when not in browser if (!this.windowService.isBrowser()) { return NEVER; } // Iben: If the subject already exists, we return the observable if (record[key]) { return record[key].asObservable(); } // Iben: If no subject exits, we create a new one record[key] = new BehaviorSubject(undefined); // Iben: Return the observable return this.getItemObservable(key, record); } /** * Sets an item in the storage * * @param key - The key of the item * @param item - The item in the storage * @param storage - The storage in which we want to save the item * @param record - The corresponding storage record */ setItem(key, item, storage, record, type) { // Iben: Early exit when we're in the browser if (!this.windowService.isBrowser()) { return undefined; } // Iben: Check if there's already a subject for this item. If not, we create one let subject = record[key]; if (!subject) { subject = new BehaviorSubject(undefined); storage[key] = subject; } // Iben: Store the current value of the subject const oldValue = subject.getValue(); // Iben: Set the item in the storage storage.setItem(key, typeof item === 'string' ? item : JSON.stringify(item)); // Iben: Update the subject in the record subject.next(item); // Iben: Create the storage event const event = { key, newValue: item, oldValue, storage: type, type: 'set', }; // Iben: Emit the storage event this.storageEventSubject.next(event); // Iben: Return the storage event return event; } /** * Remove an item from the storage and emit a remove event * * @param key - The key of the item * @param storage - The storage we wish to remove the item from * @param record - The record with the subject * @param type - The type of storage */ removeItem(key, storage, record, type) { // Iben: Early exit when we're not in the browser if (!this.windowService.isBrowser()) { return undefined; } // Iben: Get the old item const oldValue = this.parseValue(storage.getItem(key)); // Iben: Remove the item from the storage storage.removeItem(key); // Iben Update the subject if it exists record[key]?.next(undefined); // Iben: Create the event and return and emit it const event = { oldValue, storage: type, key, type: 'remove', }; this.storageEventSubject.next(event); return event; } /** * Clears the storage, completes all subjects and emits a clear event * * @param storage - The storage we wish to clear * @param record - The record with the subjects * @param type - The type of storage */ clearStorage(storage, record, type) { // Iben: Early exit when we're not in the browser if (!this.windowService.isBrowser()) { return undefined; } // Iben: Clear the storage storage.clear(); // Iben: Clear the record and complete all subjects Object.entries(record).forEach(([key, subject]) => { subject.next(undefined); subject.complete(); record[key] = undefined; }); // Iben: Create and emit event const event = { type: 'clear', storage: type, }; this.storageEventSubject.next(event); return event; } /** * Grabs the existing storage and updates the record * * @private * @param {Storage} storage - The current state of the storage * @param {NgxStorageRecord} record * @memberof NgxStorageService */ setupStorage(storage, record) { Object.entries(storage).forEach(([key, value]) => { record[key] = new BehaviorSubject(this.parseValue(value)); }); } /** * Parses a string value from the storage to an actual value * * @param value - The provided string value */ parseValue(value) { // Iben: If the value does not exist, return the value if (!value) { return value; } // Iben: If the value is either true or false, return a boolean version of the value if (value === 'true' || value === 'false') { return value === 'true'; } // Iben: If the value is a number, return the parsed number if (value.match(/^[0-9]*[,.]{0,1}[0-9]*$/)) { return Number(value); } // Iben: If the value is an object, return the parsed object if (value.match(/{(.*:.*[,]{0,1})*}/)) { return JSON.parse(value); } // Iben: Return the string value as is return value; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxStorageService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxStorageService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [] }); /** * A service that can be used to track media queries and their changes. It exposes a method * to register media queries, which takes an array of tuples with the id of the media query * and the query itself. The service will then emit the id of the media query that has * changed when subscribed to the `getMatchingQuery$` method. */ class NgxMediaQueryService { windowService = inject(NgxWindowService); /** * A map of media queries that are registered with the service. */ queryListMap = new Map(); /** * A map of the registered media queries with their id. */ queryIdMap = new Map(); /* * A map of listeners that are registered with the service. * They are saved to be able to remove them when the service is destroyed. */ mediaQueryListenerMap = new Map(); /** * A subject that emits the id of the media query that has changed. */ queryChangedSubject = new ReplaySubject(); /** * Register a list of media queries that need to be tracked by the service. * * @param queries - A list of media queries that should be registered with the service. */ registerMediaQueries(...queries) { this.windowService.runInBrowser(({ browserWindow }) => { for (const [id, query] of queries) { // Wouter: Warn if the id has already been registered. if (this.queryIdMap.get(id)) { return console.warn(`NgxMediaQueryService: Media query with id '${id}' already exists and is defined by '${this.queryIdMap.get(id)}'`); } // Wouter: If the query has already been registered, throw an error to prevent duplicate subscriptions if ([...this.queryIdMap].some(([_, value]) => value === query)) { const duplicateQuery = [...this.queryIdMap].find(([_, value]) => value === query); throw new Error(`NgxMediaQueryService: Query of ['${id}', ${query}] already exists and is defined by ['${duplicateQuery[0]}', ${duplicateQuery[1]}]`); } // Wouter: save the id and query this.queryIdMap.set(id, query); // Wouter: For each query, create a MediaQueryList object const matchedQuery = browserWindow.matchMedia(query); // Wouter: Save the query this.queryListMap.set(id, matchedQuery); // Wouter: Emit the id of the query that has changed this.queryChangedSubject.next(id); // Wouter: Create a listener for the query. This is done separately to be // able to remove the listener when the service is destroyed const listener = (queryChangedEvent) => { this.queryListMap.set(id, queryChangedEvent.currentTarget); // Wouter: Emit the id of the query that has changed this.queryChangedSubject.next(id); }; // Wouter: Register the listener to the query matchedQuery.addEventListener('change', listener); // Wouter: Save the listener this.mediaQueryListenerMap.set(id, listener); } }); } /** * Pass the id of the query whose changes need to be listened to. * * @param id - The id of the media query that should be checked. * @returns An observable that emits a boolean value whenever the requested media query changes. */ getMatchingQuery$(id) { // Wouter: Throw an error if the query has not been registered if (!this.queryIdMap.has(id)) { throw new Error(`NgxMediaQueryService: No media query with id '${id}' has been registered. Please register the media query first using the 'registerMediaQueries' method.`); } return this.queryChangedSubject.asObservable().pipe( // Wouter: Filter the query that has changed. // This will make sure only the [id] streams are triggered. filter((queryId) => queryId === id), map(() => this.queryListMap.get(id).matches)); } /** * Unregister all media query subscriptions from the service. */ ngOnDestroy() { this.windowService.runInBrowser(() => { // Wouter: Remove all eventListeners for (const [id, query] of this.queryListMap) { query.removeEventListener('change', this.mediaQueryListenerMap.get(id)); } // Wouter: Complete subscriptions this.queryChangedSubject.next(null); this.queryChangedSubject.complete(); // Wouter: Clear maps this.queryListMap.clear(); this.mediaQueryListenerMap.clear(); }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxMediaQueryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxMediaQueryService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxMediaQueryService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); class BtwPipe { /** * Converts a BTW number to the correct format * * @param value - The value we wish to convert */ transform(value) { if (!value) { // Denis: if the value is falsy, return it without transform. return value; } const addCharAtIndex = (original, char, index) => { return original.slice(0, index) + char + original.slice(index); }; // Iben: Convert to string if it's a number value = value.toString(); if (value.replace(/\./g, '').length === 9) { value = '0' + value; } // Iben: Format: xxxx.xxx.xxx if (value.charAt(4) !== '.') { value = addCharAtIndex(value, '.', 4); } if (value.charAt(8) !== '.') { value = addCharAtIndex(value, '.', 8); } return value; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: BtwPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.2", ngImport: i0, type: BtwPipe, isStandalone: true, name: "btw" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: BtwPipe, decorators: [{ type: Pipe, args: [{ name: 'btw', standalone: true, }] }] }); class HasObserversPipe { transform(output) { return output && output.observers.length > 0; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: HasObserversPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.2", ngImport: i0, type: HasObserversPipe, isStandalone: true, name: "hasObservers" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: HasObserversPipe, decorators: [{ type: Pipe, args: [{ name: 'hasObservers', standalone: true, }] }] }); class IbanPipe { transform(value = '') { value = value.replace(/\s/g, ''); // replace all spaces let reformat = value.replace(/(.{4})/g, function (match) { return match + ' '; // reformat into groups of 4 succeeded with a space }); reformat = reformat.trim(); // remove trailing space return reformat; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: IbanPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.2", ngImport: i0, type: IbanPipe, isStandalone: true, name: "IBAN" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: IbanPipe, decorators: [{ type: Pipe, args: [{ name: 'IBAN', standalone: true, }] }] }); /** The configuration token for the NgxReplaceElementsPipe */ const NgxReplaceElementsConfigurationToken = new InjectionToken('NgxReplaceElementsConfigurationToken'); /** * A pipe that allows to replace text elements with a WebComponent */ class NgxReplaceElementsPipe { configuration = inject(NgxReplaceElementsConfigurationToken); sanitizer = inject(DomSanitizer); /** * Replaces all matches of a specific selector with provided WebComponents * * @param value - The original string value * @param items - The items we wish to replace */ transform(value, items) { // Iben: If the value isn't a string we early exit and warn the user if (typeof value !== 'string') { console.warn('NgxReplaceElements: A non string-value was provided to the NgxReplaceElementsPipe'); return ''; } // Iben: If no items were provided to replace, we just return the value if (!items || items.length === 0) { return value; } // Iben: set up a new instance of the DOMParser and parse the value as text/html. // This will return a Document which we can work with to find/replace elements. const parser = new DOMParser(); const body = parser.parseFromString(value, 'text/html'); // Iben: Loop over all items we wish to replace items.forEach((item) => { // Iben: Get the selector and the element we want to replace the target with const { selector, element, includeInnerHtml } = this.configuration[item.elementId]; // Iben: Select the target const target = body.querySelector(selector.replace('{{id}}', item.id)); // Iben: If no target was found, early exit if (!target) { return; } // Iben: Create a new element within the Document based on the provided selector. // The selector can be any native or custom web component (not an Angular component). // Keep in mind that the element will need to have a lowercase input prop for the reference. const replacement = body.createElement(element); // Iben: If the item included data, we set these attributes if (item.data) { Object.entries(item.data).forEach(([key, value]) => { replacement.setAttribute(key, value); }); } // Iben: Copy the innerHtml of the target element to the new element if needed. if (includeInnerHtml) { replacement.innerHTML = target.innerHTML; } // Iben: Replace the target with the new element within the Document. target.replaceWith(replacement); }); // Iben: sanitize the document and mark it as trusted HTML before returning it to the template. return this.sanitizer.bypassSecurityTrustHtml(body.documentElement.innerHTML); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxReplaceElementsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.2", ngImport: i0, type: NgxReplaceElementsPipe, isStandalone: true, name: "ngxReplaceElements" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxReplaceElementsPipe, decorators: [{ type: Pipe, args: [{ name: 'ngxReplaceElements', standalone: true, }] }] }); /** * A pipe to pass a transformer function to. By using this setup, we can use functions without causing rerender issues */ class TransformPipe { /** * Transforms a value based on a provided transform function * * @param value - The provided value we wish to transform * @param transformer - A provided transform function */ transform(value, transformer) { // Iben: If no transformer is passed, we return the original value if (!transformer) { return value; } // Iben: Transform the value and return return transformer(value); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: TransformPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.2", ngImport: i0, type: TransformPipe, isStandalone: true, name: "transform" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: TransformPipe, decorators: [{ type: Pipe, args: [{ name: 'transform', standalone: true, }] }] }); const Pipes = [BtwPipe, HasObserversPipe, IbanPipe, TransformPipe, NgxReplaceElementsPipe]; class NgxQueryParamFormSyncComponent { route = inject(ActivatedRoute); router = inject(Router); destroyed$ = new Subject(); /** * The form in which we will save the queryParam data */ form; /** * The query params we wish to form */ queryParams$ = this.route .queryParams; ngOnInit() { //Iben: Warn the user if one of the two methods isn't provided if ((!this.scrambleParams && this.unscrambleParams) || (this.scrambleParams && !this.unscrambleParams)) { console.error(`NgxUtils: NgxQueryParamFormSyncComponent detected the use of the parameter scrambling but is missing an implementation for the ${this.scrambleParams ? 'unscrambleParams' : 'scrambleParams'} method. Please provide this method in order for this flow to work correctly.`); } // Iben: Setup the form for the data this.form = this.initForm(); // Iben: Listen to the form changes this.form.valueChanges .pipe(tap((data) => { // Iben: Update the route params this.setDataInRoute(data); // Iben: Handle the route data changes if (this.handleDataChanges) { this.handleDataChanges(data); } }), takeUntil(this.destroyed$)) .subscribe(); // Iben: Listen to the initial query param update so we can set the data in the form if we navigate to a link with the params this.queryParams$ .pipe(take(1), filter(Boolean), tap((data) => { // Iben: Convert the route data properties to the actual data let value = Object.keys(data || {}).reduce((previous, current) => { return { ...previous, [current]: data[current] ? JSON.parse(data[current]) : undefined, }; }, {}); // Iben: In case the unscrambleParams method is provided, we unscramble the data if (this.unscrambleParams) { value = this.unscrambleParams(value); } //Iben: If the entire object is empty, we early exit and do not set the form if (Object.keys(value).length === 0) { return; } // Iben: Set the current form value this.form.setValue(value); }), takeUntil(this.destroyed$)) .subscribe(); } ngOnDestroy() { this.destroyed$.next(); this.destroyed$.complete(); this.clearData(); } /** * Clears the data in the form */ clearData() { this.form.reset(); } /** * Sets the provided data in the route, so the filtered view can be shared by url * * @param data - The provided data */ setDataInRoute(data) { // Iben: If no data was provided, we simply unset the current params if (Object.keys(data || {}).length === 0) { this.router.navigate([], { relativeTo: this.route, queryParams: {}, }); return; } // Iben: In case a scrambleParams function was provided, we scramble the params first const parsedData = this.scrambleParams ? this.scrambleParams(data) : data; // Iben: Stringify all properties of the data const queryParams = Object.keys(parsedData || {}).reduce((previous, current) => { return { ...previous, [current]: JSON.stringify(parsedData[current]), }; }, {}); // Iben: Add the queryParams to the route this.router.navigate([], { relativeTo: this.route, queryParamsHandling: 'merge', queryParams, }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxQueryParamFormSyncComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.2", type: NgxQueryParamFormSyncComponent, isStandalone: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.2", ngImport: i0, type: NgxQueryParamFormSyncComponent, decorators: [{ type: Directive }] }); /** * Provides the configuration for the NgxReplaceElements directive * * @param configuration - The required configuration */ const provideNgxReplaceElementsConfiguration = (configuration) => { return { provide: NgxReplaceElementsConfigurationToken, useValue: configuration, }; }; /** * Method checks if given value has changed * @param {SimpleChange} value: value to check * @returns {boolean} whether or not if the given input has changed */ const simpleChangeHasChanged = (value) => { return value && JSON.stringify(value.previousValue) !== JSON.stringify(value.currentValue); }; /* * Public API Surface of utils */ /** * Generated bundle index. Do not edit. */ export { BtwPipe, Directives, FocusClickDirective, HasObserversPipe, IbanPipe, NgxBroadcastChannelService, NgxMediaQueryService, NgxQueryParamFormSyncComponent, NgxReplaceElementsConfigurationToken, NgxReplaceElementsPipe, NgxStorageService, Pipes, TransformPipe, provideNgxReplaceElementsConfiguration, simpleChangeHasChanged }; //# sourceMappingURL=ibenvandeveire-ngx-utils.mjs.map