1 /* eslint-disable no-unused-expressions */
2 import {Component, EventEmitter, OnInit, Input, Output, ViewChildren, QueryList, forwardRef} from '@angular/core';
3 import {ControlValueAccessor, FormGroup, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
4 import {AuthService} from '@eg/core/auth.service';
5 import {IdlObject} from '@eg/core/idl.service';
6 import {OrgService} from '@eg/core/org.service';
7 import {OrgSelectComponent} from '@eg/share/org-select/org-select.component';
8 import {ServerStoreService} from '@eg/core/server-store.service';
10 export interface OrgFamily {
12 includeAncestors?: boolean;
13 includeDescendants?: boolean;
18 selector: 'eg-org-family-select',
19 templateUrl: 'org-family-select.component.html',
22 provide: NG_VALUE_ACCESSOR,
23 useExisting: forwardRef(() => OrgFamilySelectComponent),
28 export class OrgFamilySelectComponent implements ControlValueAccessor, OnInit {
30 // ARIA label for selector. Required if there is no <label> in the markup.
31 @Input() ariaLabel?: string;
33 // Should the Ancestors checkbox be hidden?
34 @Input() hideAncestorSelector = false;
36 // Should the Descendants checkbox be hidden?
37 @Input() hideDescendantSelector = false;
39 // Should the Ancestors checkbox be checked by default?
41 // Ignored if [hideAncestorSelector]="true"
42 @Input() ancestorSelectorChecked = false;
44 // Should the Descendants checkbox be checked by default?
46 // Ignored if [hideDescendantSelector]="true"
47 @Input() descendantSelectorChecked = false;
50 @Input() selectedOrgId: number;
52 // Only show the OUs that the user has certain permissions at
53 @Input() limitPerms: string[];
55 @Input() domId: string;
57 @Input() persistKey: string;
59 @Output() onChange = new EventEmitter<any>();
61 @ViewChildren(OrgSelectComponent) orgSelects: QueryList<OrgSelectComponent>;
63 // this is the most up-to-date value used for ngModel and reactive form
67 orgOnChange: ($event: IdlObject) => void;
68 emitArray: () => void;
70 familySelectors: FormGroup;
72 propagateChange = (_: OrgFamily) => {};
73 propagateTouch = () => {};
76 private auth: AuthService,
77 private org: OrgService,
78 private serverStore: ServerStoreService
83 if (this.selectedOrgId) {
84 this.options = {primaryOrgId: this.selectedOrgId};
85 } else if (this.auth.user()) {
86 this.options = {primaryOrgId: this.auth.user().ws_ou()};
89 this.familySelectors = new FormGroup({
90 'includeAncestors': new FormControl({
91 value: this.ancestorSelectorChecked,
92 disabled: this.disableAncestorSelector()}),
93 'includeDescendants': new FormControl({
94 value: this.descendantSelectorChecked,
95 disabled: this.disableDescendantSelector()}),
99 // eslint-disable-next-line no-magic-numbers
100 this.domId = 'org-family-select-' + Math.floor(Math.random() * 100000);
103 this.familySelectors.valueChanges.subscribe(val => {
107 this.orgOnChange = ($event: IdlObject) => {
108 this.options.primaryOrgId = $event.id();
109 this.disableAncestorSelector() ? this.includeAncestors.disable() : this.includeAncestors.enable();
110 this.disableDescendantSelector() ? this.includeDescendants.disable() : this.includeDescendants.enable();
114 this.emitArray = () => {
115 // Prepare and emit an array containing the primary org id and
116 // optionally ancestor and descendant org units, and flags that select those.
118 this.options.orgIds = [this.options.primaryOrgId];
119 this.options.includeAncestors = this.includeAncestors.value;
120 this.options.includeDescendants = this.includeDescendants.value;
122 if (this.includeAncestors.value) {
123 this.options.orgIds = this.org.ancestors(this.options.primaryOrgId, true);
126 if (this.includeDescendants.value) {
127 this.options.orgIds = this.options.orgIds.concat(
128 this.org.descendants(this.options.primaryOrgId, true));
131 // Using ancestors() and descendants() can result in
132 // duplicate org ID's. Be nice and uniqify.
133 const hash: any = {};
134 this.options.orgIds.forEach(id => hash[id] = true);
135 this.options.orgIds = Object.keys(hash).map(id => Number(id));
137 this.propagateChange(this.options);
138 this.onChange.emit(this.options);
140 if (this.persistKey) {
141 const key = `eg.orgfamilyselect.${this.persistKey}`;
142 this.serverStore.setItem(key, this.options);
146 this.loadPersistedValues();
149 private loadPersistedValues() {
150 if (!this.persistKey) {return;}
152 const key = `eg.orgfamilyselect.${this.persistKey}`;
154 this.serverStore.getItem(key).then(persistedOptions => {
155 if (persistedOptions) {
156 this.writeValue(persistedOptions);
161 writeValue(value: OrgFamily) {
163 this.selectedOrgId = value['primaryOrgId'];
164 if (this.orgSelects) {
165 this.orgSelects.toArray()[0].applyOrgId = this.selectedOrgId;
166 this.options = {primaryOrgId: this.selectedOrgId};
168 this.familySelectors.patchValue({
169 'includeAncestors': value['includeAncestors'] ? value['includeAncestors'] : false,
170 'includeDescendants': value['includeDescendants'] ? value['includeDescendants'] : false,
175 registerOnChange(fn) {
176 this.propagateChange = fn;
179 registerOnTouched(fn) {
180 this.propagateTouch = fn;
183 disableAncestorSelector(): boolean {
184 return this.options.primaryOrgId === this.org.root().id();
187 disableDescendantSelector(): boolean {
188 const contextOrg = this.org.get(this.options.primaryOrgId);
189 return contextOrg.children().length === 0;
192 get includeAncestors() {
193 return this.familySelectors.get('includeAncestors');
195 get includeDescendants() {
196 return this.familySelectors.get('includeDescendants');