@rxap/form-system
Version:
This package provides a set of directives, decorators, mixins, and validators to simplify the creation of dynamic forms in Angular applications. It offers features such as automatic control creation, data source integration, component customization, and h
815 lines (649 loc) • 28.8 kB
HTML
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>angular-form-system</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="../images/favicon.ico">
<link rel="stylesheet" href="../styles/style.css">
<link rel="stylesheet" href="../styles/dark.css">
</head>
<body>
<script>
// Blocking script to avoid flickering dark mode
// Dark mode toggle button
var useDark = window.matchMedia('(prefers-color-scheme: dark)');
var darkModeState = useDark.matches;
var $darkModeToggleSwitchers = document.querySelectorAll('.dark-mode-switch input');
var $darkModeToggles = document.querySelectorAll('.dark-mode-switch');
var darkModeStateLocal = localStorage.getItem('compodoc_darkmode-state');
function checkToggle(check) {
for (var i = 0; i < $darkModeToggleSwitchers.length; i++) {
$darkModeToggleSwitchers[i].checked = check;
}
}
function toggleDarkMode(state) {
if (window.localStorage) {
localStorage.setItem('compodoc_darkmode-state', state);
}
checkToggle(state);
const hasClass = document.body.classList.contains('dark');
if (state) {
for (var i = 0; i < $darkModeToggles.length; i++) {
$darkModeToggles[i].classList.add('dark');
}
if (!hasClass) {
document.body.classList.add('dark');
}
} else {
for (var i = 0; i < $darkModeToggles.length; i++) {
$darkModeToggles[i].classList.remove('dark');
}
if (hasClass) {
document.body.classList.remove('dark');
}
}
}
useDark.addEventListener('change', function (evt) {
toggleDarkMode(evt.matches);
});
if (darkModeStateLocal) {
darkModeState = darkModeStateLocal === 'true';
}
toggleDarkMode(darkModeState);
</script>
<div class="navbar navbar-default navbar-fixed-top d-md-none p-0">
<div class="d-flex">
<a href="../" class="navbar-brand">angular-form-system</a>
<button type="button" class="btn btn-default btn-menu ion-ios-menu" id="btn-menu"></button>
</div>
</div>
<div class="xs-menu menu" id="mobile-menu">
<div id="book-search-input" role="search"><input type="text" placeholder="Type to search"></div> <compodoc-menu></compodoc-menu>
</div>
<div class="container-fluid main">
<div class="row main">
<div class="d-none d-md-block menu">
<compodoc-menu mode="normal"></compodoc-menu>
</div>
<!-- START CONTENT -->
<div class="content directive">
<div class="content-data">
<ol class="breadcrumb">
<li class="breadcrumb-item">Directives</li>
<li class="breadcrumb-item" >ControlHideShowDirective</li>
</ol>
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item">
<a href="#info"
class="nav-link"
class="nav-link active"
role="tab" id="info-tab" data-bs-toggle="tab" data-link="info">Info</a>
</li>
<li class="nav-item">
<a href="#source"
class="nav-link"
role="tab" id="source-tab" data-bs-toggle="tab" data-link="source">Source</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade active in" id="info">
<p class="comment">
<h3>File</h3>
</p>
<p class="comment">
<code>src/lib/directives/control-hide-show.directive.ts</code>
</p>
<p class="comment">
<h3>Extends</h3>
</p>
<p class="comment">
<code><a href="../classes/ExtractControlFromParentMixin.html" target="_self" >ExtractControlFromParentMixin</a></code>
<code><a href="../classes/ExtractFormDefinitionMixin.html" target="_self" >ExtractFormDefinitionMixin</a></code>
</p>
<p class="comment">
<h3>Implements</h3>
</p>
<p class="comment">
<code>OnInit</code>
<code>OnDestroy</code>
</p>
<section data-compodoc="block-metadata">
<h3>Metadata</h3>
<table class="table table-sm table-hover metadata">
<tbody>
<tr>
<td class="col-md-3">Selector</td>
<td class="col-md-9"><code>[rxapControlHideShow]</code></td>
</tr>
<tr>
<td class="col-md-3">Standalone</td>
<td class="col-md-9"><code>true</code></td>
</tr>
</tbody>
</table>
</section>
<section data-compodoc="block-index">
<h3 id="index">Index</h3>
<table class="table table-sm table-bordered index-table">
<tbody>
<tr>
<td class="col-md-4">
<h6><b>Properties</b></h6>
</td>
</tr>
<tr>
<td class="col-md-4">
<ul class="index-list">
<li>
<span class="modifier">Public</span>
<a href="#control" >control</a>
</li>
</ul>
</td>
</tr>
<tr>
<td class="col-md-4">
<h6><b>Methods</b></h6>
</td>
</tr>
<tr>
<td class="col-md-4">
<ul class="index-list">
<li>
<span class="modifier">Protected</span>
<a href="#extractControlFromParent" >extractControlFromParent</a>
</li>
</ul>
</td>
</tr>
<tr>
<td class="col-md-4">
<h6><b>Inputs</b></h6>
</td>
</tr>
<tr>
<td class="col-md-4">
<ul class="index-list">
<li>
<a href="#rxapControlHideShow" >rxapControlHideShow</a>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</section>
<section data-compodoc="block-constructor">
<h3 id="constructor">Constructor</h3>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<code>constructor(renderer: Renderer2, elementRef: ElementRef, parent: ControlContainer | null, ngControl: NgControl | null)</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="85" class="link-to-prism">src/lib/directives/control-hide-show.directive.ts:85</a></div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div>
<b>Parameters :</b>
<table class="params">
<thead>
<tr>
<td>Name</td>
<td>Type</td>
<td>Optional</td>
</tr>
</thead>
<tbody>
<tr>
<td>renderer</td>
<td>
<code>Renderer2</code>
</td>
<td>
No
</td>
</tr>
<tr>
<td>elementRef</td>
<td>
<code>ElementRef</code>
</td>
<td>
No
</td>
</tr>
<tr>
<td>parent</td>
<td>
<code>ControlContainer | null</code>
</td>
<td>
No
</td>
</tr>
<tr>
<td>ngControl</td>
<td>
<code>NgControl | null</code>
</td>
<td>
No
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</section>
<section data-compodoc="block-inputs">
<h3 id="inputs">Inputs</h3>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="rxapControlHideShow"></a>
<b>rxapControlHideShow</b>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/string" target="_blank" >string</a></code>
</td>
</tr>
<tr>
<td class="col-md-2" colspan="2">
<div class="io-line">Defined in <a href="" data-line="81" class="link-to-prism">src/lib/directives/control-hide-show.directive.ts:81</a></div>
</td>
</tr>
</tbody>
</table>
</section>
<section data-compodoc="block-methods">
<h3 id="methods">
Methods
</h3>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="extractControlFromParent"></a>
<span class="name">
<span class="modifier">Protected</span>
<span ><b>extractControlFromParent</b></span>
<a href="#extractControlFromParent"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<span class="modifier-icon icon ion-ios-reset"></span>
<code>extractControlFromParent(parent: ControlContainer, controlPath: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/string" target="_blank">string</a>)</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Inherited from <code><a href="../classes/ExtractControlFromParentMixin.html" target="_self" >ExtractControlFromParentMixin</a></code>
</div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <code><a href="../classes/ExtractControlFromParentMixin.html#source" target="_self" >ExtractControlFromParentMixin:12</a></code>
</div>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-description">
<b>Parameters :</b>
<table class="params">
<thead>
<tr>
<td>Name</td>
<td>Type</td>
<td>Optional</td>
</tr>
</thead>
<tbody>
<tr>
<td>parent</td>
<td>
<code>ControlContainer</code>
</td>
<td>
No
</td>
</tr>
<tr>
<td>controlPath</td>
<td>
<code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/string" target="_blank" >string</a></code>
</td>
<td>
No
</td>
</tr>
</tbody>
</table>
</div>
<div class="io-description">
<b>Returns : </b> <code>AbstractControl</code>
</div>
<div class="io-description">
</div>
</td>
</tr>
</tbody>
</table>
</section>
<section data-compodoc="block-properties">
<h3 id="inputs">
Properties
</h3>
<table class="table table-sm table-bordered">
<tbody>
<tr>
<td class="col-md-4">
<a name="control"></a>
<span class="name">
<span class="modifier">Public</span>
<span ><b>control</b></span>
<a href="#control"><span class="icon ion-ios-link"></span></a>
</span>
</td>
</tr>
<tr>
<td class="col-md-4">
<i>Type : </i> <code>RxapFormControl</code>
</td>
</tr>
<tr>
<td class="col-md-4">
<div class="io-line">Defined in <a href="" data-line="83" class="link-to-prism">src/lib/directives/control-hide-show.directive.ts:83</a></div>
</td>
</tr>
</tbody>
</table>
</section>
</div>
<div class="tab-pane fade tab-source-code" id="source">
<pre class="line-numbers compodoc-sourcecode"><code class="language-typescript">import {
Directive,
ElementRef,
Inject,
Input,
OnDestroy,
OnInit,
Optional,
Renderer2,
Self,
} from '@angular/core';
import {
AbstractControl,
FormDefinition,
RxapFormControl,
} from '@rxap/forms';
import {
ControlContainer,
NgControl,
} from '@angular/forms';
import { Mixin } from '@rxap/mixin';
import {
of,
Subscription,
} from 'rxjs';
import {
catchError,
startWith,
tap,
} from 'rxjs/operators';
import { ExtractControlFromParentMixin } from '../mixins/extract-control-from-parent.mixin';
import { ExtractFormDefinitionMixin } from '../mixins/extract-form-definition.mixin';
import {
getMetadata,
setMetadataMap,
} from '@rxap/reflect-metadata';
export const RXAP_FORM_SYSTEM_SHOW_METADATA = 'rxap/form-system/show-function';
export const RXAP_FORM_SYSTEM_HIDE_METADATA = 'rxap/form-system/hide-function';
export const RXAP_FORM_SYSTEM_HIDE_SHOW_OPTIONS = 'rxap/form-system/hide-show-options';
export type ControlHideShowFunction<T = any> = (this: T, control: AbstractControl) => boolean | null;
export interface ControlHideShowOptions {
/**
* Weather the hide/showFunction should be called on root group changes
*/
rootChange?: boolean;
}
export function UseHideFunction(hideFunction: ControlHideShowFunction, options: ControlHideShowOptions = {}) {
return function (target: any, propertyKey: string) {
setMetadataMap(propertyKey, hideFunction, RXAP_FORM_SYSTEM_HIDE_METADATA, target);
if (Object.keys(options).length) {
setMetadataMap(propertyKey, options, RXAP_FORM_SYSTEM_HIDE_SHOW_OPTIONS, target);
}
};
}
export function UseShowFunction(showFunction: ControlHideShowFunction, options: ControlHideShowOptions = {}) {
return function (target: any, propertyKey: string) {
setMetadataMap(propertyKey, showFunction, RXAP_FORM_SYSTEM_SHOW_METADATA, target);
if (Object.keys(options).length) {
setMetadataMap(propertyKey, options, RXAP_FORM_SYSTEM_HIDE_SHOW_OPTIONS, target);
}
};
}
export interface ControlHideShowDirective extends ExtractControlFromParentMixin,
ExtractFormDefinitionMixin {
}
@Mixin(ExtractControlFromParentMixin, ExtractFormDefinitionMixin)
@Directive({
selector: '[rxapControlHideShow]',
standalone: true,
})
export class ControlHideShowDirective implements OnInit, OnDestroy {
@Input('rxapControlHideShow')
public controlId?: string;
public control!: RxapFormControl;
private subscription = new Subscription();
constructor(
@Inject(Renderer2)
private readonly renderer: Renderer2,
@Inject(ElementRef)
private readonly elementRef: ElementRef,
@Optional()
@Inject(ControlContainer)
private readonly parent: ControlContainer | null,
@Self()
@Optional()
@Inject(NgControl)
private readonly ngControl: NgControl | null,
) {
}
public ngOnInit() {
if (!this.ngControl && !this.parent) {
throw new Error('Easier the ControlContainer or NgControl must be injectable');
}
if (!this.ngControl && !this.controlId) {
throw new Error('If the NgControl is not injectable then the control id must be defined!');
}
const control = this.ngControl?.control ?? this.extractControlFromParent(this.parent!, this.controlId!);
if (!(control instanceof RxapFormControl)) {
throw new Error('The control is not an instance of RxapFormControl');
}
this.control = control;
const formDefinition = this.extractFormDefinition(control);
const hideFunction = this.extractHideFunction(formDefinition);
const showFunction = this.extractShowFunction(formDefinition);
const options = this.extractOptions(formDefinition);
let changeTrigger = formDefinition.rxapFormGroup.valueChanges;
if (options.rootChange) {
changeTrigger = formDefinition.rxapFormGroup.root.valueChanges;
}
changeTrigger = changeTrigger.pipe(startWith(null));
if (showFunction) {
this.subscription.add(
changeTrigger.pipe(
tap(() => {
// TODO : resubscribe if the showFunction throws an error
const show = showFunction.call(formDefinition, control);
switch (show) {
case true:
this.show();
break;
case false:
this.hide();
break;
case null:
this.reset();
break;
}
}),
catchError(err => {
console.error(err);
return of(null);
}),
).subscribe(),
);
}
if (hideFunction) {
this.subscription.add(
changeTrigger.pipe(
tap(() => {
// TODO : resubscribe if the hideFunction throws an error
const hide = hideFunction.call(formDefinition, control);
switch (hide) {
case true:
this.hide();
break;
case false:
this.show();
break;
case null:
this.reset();
break;
}
}),
catchError(err => {
console.error(err);
return of(null);
}),
).subscribe(),
);
}
}
public ngOnDestroy() {
this.subscription.unsubscribe();
}
private extractOptions(formDefinition: FormDefinition): ControlHideShowOptions {
const map = getMetadata<Map<string, ControlHideShowOptions>>(
RXAP_FORM_SYSTEM_HIDE_SHOW_OPTIONS,
Object.getPrototypeOf(formDefinition),
) ?? null;
if (!map || !map.has(this.control.controlId)) {
return {};
}
return map.get(this.control.controlId)!;
}
private extractHideFunction(formDefinition: FormDefinition): ControlHideShowFunction | null {
const map = getMetadata<Map<string, ControlHideShowFunction>>(
RXAP_FORM_SYSTEM_HIDE_METADATA,
Object.getPrototypeOf(formDefinition),
) ?? null;
if (!map || !map.has(this.control.controlId)) {
return null;
}
return map.get(this.control.controlId)!;
}
private extractShowFunction(formDefinition: FormDefinition): ControlHideShowFunction | null {
const map = getMetadata<Map<string, ControlHideShowFunction>>(
RXAP_FORM_SYSTEM_SHOW_METADATA,
Object.getPrototypeOf(formDefinition),
) ?? null;
if (!map || !map.has(this.control.controlId)) {
return null;
}
return map.get(this.control.controlId)!;
}
private hide() {
this.renderer.setStyle(this.elementRef.nativeElement, 'display', 'none');
}
private show() {
this.renderer.setStyle(this.elementRef.nativeElement, 'display', 'block');
}
private reset() {
this.renderer.removeStyle(this.elementRef.nativeElement, 'display');
}
}
</code></pre>
</div>
</div>
</div><div class="search-results">
<div class="has-results">
<h1 class="search-results-title"><span class='search-results-count'></span> results matching "<span class='search-query'></span>"</h1>
<ul class="search-results-list"></ul>
</div>
<div class="no-results">
<h1 class="search-results-title">No results matching "<span class='search-query'></span>"</h1>
</div>
</div>
</div>
<!-- END CONTENT -->
</div>
</div>
<label class="dark-mode-switch">
<input type="checkbox">
<span class="slider">
<svg class="slider-icon" viewBox="0 0 24 24" fill="none" height="20" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" width="20" xmlns="http://www.w3.org/2000/svg">
<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"></path>
</svg>
</span>
</label>
<script>
var COMPODOC_CURRENT_PAGE_DEPTH = 1;
var COMPODOC_CURRENT_PAGE_CONTEXT = 'directive';
var COMPODOC_CURRENT_PAGE_URL = 'ControlHideShowDirective.html';
var MAX_SEARCH_RESULTS = 15;
</script>
<script>
$darkModeToggleSwitchers = document.querySelectorAll('.dark-mode-switch input');
checkToggle(darkModeState);
if ($darkModeToggleSwitchers.length > 0) {
for (var i = 0; i < $darkModeToggleSwitchers.length; i++) {
$darkModeToggleSwitchers[i].addEventListener('change', function (event) {
darkModeState = !darkModeState;
toggleDarkMode(darkModeState);
});
}
}
</script>
<script src="../js/libs/custom-elements.min.js"></script>
<script src="../js/libs/lit-html.js"></script>
<script src="../js/menu-wc.js" defer></script>
<script nomodule src="../js/menu-wc_es5.js" defer></script>
<script src="../js/libs/bootstrap-native.js"></script>
<script src="../js/libs/es6-shim.min.js"></script>
<script src="../js/libs/EventDispatcher.js"></script>
<script src="../js/libs/promise.min.js"></script>
<script src="../js/libs/zepto.min.js"></script>
<script src="../js/compodoc.js"></script>
<script src="../js/tabs.js"></script>
<script src="../js/menu.js"></script>
<script src="../js/libs/clipboard.min.js"></script>
<script src="../js/libs/prism.js"></script>
<script src="../js/sourceCode.js"></script>
<script src="../js/search/search.js"></script>
<script src="../js/search/lunr.min.js"></script>
<script src="../js/search/search-lunr.js"></script>
<script src="../js/search/search_index.js"></script>
<script src="../js/lazy-load-graphs.js"></script>
</body>
</html>