1 import {Component, EventEmitter, OnInit, Input, Output, ViewChildren, QueryList, forwardRef} from '@angular/core';
2 import {ControlValueAccessor, FormGroup, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
3 import {AuthService} from '@eg/core/auth.service';
4 import {IdlObject} from '@eg/core/idl.service';
5 import {OrgService} from '@eg/core/org.service';
6 import {OrgSelectComponent} from '@eg/share/org-select/org-select.component';
7 import {ServerStoreService} from '@eg/core/server-store.service';
9 export interface OrgFamily {
11 includeAncestors?: boolean;
12 includeDescendants?: boolean;
17 selector: 'eg-org-family-select',
18 templateUrl: 'org-family-select.component.html',
21 provide: NG_VALUE_ACCESSOR,
22 useExisting: forwardRef(() => OrgFamilySelectComponent),
27 export class OrgFamilySelectComponent implements ControlValueAccessor, OnInit {
29 // ARIA label for selector. Required if there is no <label> in the markup.
30 @Input() ariaLabel?: string;
32 // Should the Ancestors checkbox be hidden?
33 @Input() hideAncestorSelector = false;
35 // Should the Descendants checkbox be hidden?
36 @Input() hideDescendantSelector = false;
38 // Should the Ancestors checkbox be checked by default?
40 // Ignored if [hideAncestorSelector]="true"
41 @Input() ancestorSelectorChecked = false;
43 // Should the Descendants checkbox be checked by default?
45 // Ignored if [hideDescendantSelector]="true"
46 @Input() descendantSelectorChecked = false;
49 @Input() selectedOrgId: number;
51 // Only show the OUs that the user has certain permissions at
52 @Input() limitPerms: string[];
54 @Input() domId: string;
56 @Input() persistKey: string;
58 @Output() onChange = new EventEmitter<any>();
60 @ViewChildren(OrgSelectComponent) orgSelects: QueryList<OrgSelectComponent>;
62 // this is the most up-to-date value used for ngModel and reactive form
66 orgOnChange: ($event: IdlObject) => void;
67 emitArray: () => void;
69 familySelectors: FormGroup;
71 propagateChange = (_: OrgFamily) => {};
72 propagateTouch = () => {};
75 private auth: AuthService,
76 private org: OrgService,
77 private serverStore: ServerStoreService
82 if (this.selectedOrgId) {
83 this.options = {primaryOrgId: this.selectedOrgId};
84 } else if (this.auth.user()) {
85 this.options = {primaryOrgId: this.auth.user().ws_ou()};
88 this.familySelectors = new FormGroup({
89 'includeAncestors': new FormControl({
90 value: this.ancestorSelectorChecked,
91 disabled: this.disableAncestorSelector()}),
92 'includeDescendants': new FormControl({
93 value: this.descendantSelectorChecked,
94 disabled: this.disableDescendantSelector()}),
98 this.domId = 'org-family-select-' + Math.floor(Math.random() * 100000);
101 this.familySelectors.valueChanges.subscribe(val => {
105 this.orgOnChange = ($event: IdlObject) => {
106 this.options.primaryOrgId = $event.id();
107 this.disableAncestorSelector() ? this.includeAncestors.disable() : this.includeAncestors.enable();
108 this.disableDescendantSelector() ? this.includeDescendants.disable() : this.includeDescendants.enable();
112 this.emitArray = () => {
113 // Prepare and emit an array containing the primary org id and
114 // optionally ancestor and descendant org units, and flags that select those.
116 this.options.orgIds = [this.options.primaryOrgId];
117 this.options.includeAncestors = this.includeAncestors.value;
118 this.options.includeDescendants = this.includeDescendants.value;
120 if (this.includeAncestors.value) {
121 this.options.orgIds = this.org.ancestors(this.options.primaryOrgId, true);
124 if (this.includeDescendants.value) {
125 this.options.orgIds = this.options.orgIds.concat(
126 this.org.descendants(this.options.primaryOrgId, true));
129 // Using ancestors() and descendants() can result in
130 // duplicate org ID's. Be nice and uniqify.
131 const hash: any = {};
132 this.options.orgIds.forEach(id => hash[id] = true);
133 this.options.orgIds = Object.keys(hash).map(id => Number(id));
135 this.propagateChange(this.options);
136 this.onChange.emit(this.options);
138 if (this.persistKey) {
139 const key = `eg.orgfamilyselect.${this.persistKey}`;
140 this.serverStore.setItem(key, this.options);
144 this.loadPersistedValues();
147 private loadPersistedValues() {
148 if (!this.persistKey) return;
150 const key = `eg.orgfamilyselect.${this.persistKey}`;
152 this.serverStore.getItem(key).then(persistedOptions => {
153 if (persistedOptions) {
154 this.writeValue(persistedOptions);
159 writeValue(value: OrgFamily) {
161 this.selectedOrgId = value['primaryOrgId'];
162 if (this.orgSelects) {
163 this.orgSelects.toArray()[0].applyOrgId = this.selectedOrgId;
164 this.options = {primaryOrgId: this.selectedOrgId};
166 this.familySelectors.patchValue({
167 'includeAncestors': value['includeAncestors'] ? value['includeAncestors'] : false,
168 'includeDescendants': value['includeDescendants'] ? value['includeDescendants'] : false,
173 registerOnChange(fn) {
174 this.propagateChange = fn;
177 registerOnTouched(fn) {
178 this.propagateTouch = fn;
181 disableAncestorSelector(): boolean {
182 return this.options.primaryOrgId === this.org.root().id();
185 disableDescendantSelector(): boolean {
186 const contextOrg = this.org.get(this.options.primaryOrgId);
187 return contextOrg.children().length === 0;
190 get includeAncestors() {
191 return this.familySelectors.get('includeAncestors');
193 get includeDescendants() {
194 return this.familySelectors.get('includeDescendants');