]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/share/org-family-select/org-family-select.component.ts
LP#1857150: org-family-select: have ngModel changes update the control
[Evergreen.git] / Open-ILS / src / eg2 / src / app / share / org-family-select / org-family-select.component.ts
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
8 export interface OrgFamily {
9   primaryOrgId: number;
10   includeAncestors?: boolean;
11   includeDescendants?: boolean;
12   orgIds?: number[];
13 }
14
15 @Component({
16     selector: 'eg-org-family-select',
17     templateUrl: 'org-family-select.component.html',
18     providers: [
19     {
20       provide: NG_VALUE_ACCESSOR,
21       useExisting: forwardRef(() => OrgFamilySelectComponent),
22       multi: true
23     }
24   ]
25 })
26 export class OrgFamilySelectComponent implements ControlValueAccessor, OnInit {
27
28     // The label for this input
29     @Input() labelText = 'Library';
30
31     // Should the Ancestors checkbox be hidden?
32     @Input() hideAncestorSelector = false;
33
34     // Should the Descendants checkbox be hidden?
35     @Input() hideDescendantSelector = false;
36
37     // Should the Ancestors checkbox be checked by default?
38     //
39     // Ignored if [hideAncestorSelector]="true"
40     @Input() ancestorSelectorChecked = false;
41
42     // Should the Descendants checkbox be checked by default?
43     //
44     // Ignored if [hideDescendantSelector]="true"
45     @Input() descendantSelectorChecked = false;
46
47     // Default org unit
48     @Input() selectedOrgId: number;
49
50     // Only show the OUs that the user has certain permissions at
51     @Input() limitPerms: string[];
52
53     @Input() domId: string;
54
55     @ViewChildren(OrgSelectComponent)  orgSelects: QueryList<OrgSelectComponent>;
56
57     // this is the most up-to-date value used for ngModel and reactive form
58     // subscriptions
59     options: OrgFamily;
60
61     orgOnChange: ($event: IdlObject) => void;
62     emitArray: () => void;
63
64     familySelectors: FormGroup;
65
66     propagateChange = (_: OrgFamily) => {};
67     propagateTouch = () => {};
68
69     constructor(
70         private auth: AuthService,
71         private org: OrgService
72     ) {
73     }
74
75     ngOnInit() {
76         if (this.selectedOrgId) {
77             this.options = {primaryOrgId: this.selectedOrgId};
78         } else if (this.auth.user()) {
79             this.options = {primaryOrgId: this.auth.user().ws_ou()};
80         }
81
82         this.familySelectors = new FormGroup({
83             'includeAncestors': new FormControl({
84                 value: this.ancestorSelectorChecked,
85                 disabled: this.disableAncestorSelector()}),
86             'includeDescendants': new FormControl({
87                 value: this.descendantSelectorChecked,
88                 disabled: this.disableDescendantSelector()}),
89         });
90
91         if (!this.domId) {
92             this.domId = 'org-family-select-' + Math.floor(Math.random() * 100000);
93         }
94
95         this.familySelectors.valueChanges.subscribe(val => {
96             this.emitArray();
97         });
98
99         this.orgOnChange = ($event: IdlObject) => {
100             this.options.primaryOrgId = $event.id();
101             this.disableAncestorSelector() ? this.includeAncestors.disable() : this.includeAncestors.enable();
102             this.disableDescendantSelector() ? this.includeDescendants.disable() : this.includeDescendants.enable();
103             this.emitArray();
104         };
105
106         this.emitArray = () => {
107             // Prepare and emit an array containing the primary org id and
108             // optionally ancestor and descendant org units.
109
110             this.options.orgIds = [this.options.primaryOrgId];
111
112             if (this.includeAncestors.value) {
113                 this.options.orgIds = this.org.ancestors(this.options.primaryOrgId, true);
114             }
115
116             if (this.includeDescendants.value) {
117                 this.options.orgIds = this.options.orgIds.concat(
118                     this.org.descendants(this.options.primaryOrgId, true));
119             }
120
121             // Using ancestors() and descendants() can result in
122             // duplicate org ID's.  Be nice and uniqify.
123             const hash: any = {};
124             this.options.orgIds.forEach(id => hash[id] = true);
125             this.options.orgIds = Object.keys(hash).map(id => Number(id));
126
127             this.propagateChange(this.options);
128         };
129     }
130
131     writeValue(value: OrgFamily) {
132         if (value) {
133             this.selectedOrgId = value['primaryOrgId'];
134             if (this.orgSelects) {
135                 this.orgSelects.toArray()[0].applyOrgId = this.selectedOrgId;
136                 this.options = {primaryOrgId: this.selectedOrgId};
137             }
138             this.familySelectors.patchValue({
139                 'includeAncestors': value['includeAncestors'] ? value['includeAncestors'] : false,
140                 'includeDescendants': value['includeDescendants'] ? value['includeDescendants'] : false,
141             });
142         }
143     }
144
145     registerOnChange(fn) {
146         this.propagateChange = fn;
147     }
148
149     registerOnTouched(fn) {
150         this.propagateTouch = fn;
151     }
152
153     disableAncestorSelector(): boolean {
154         return this.options.primaryOrgId === this.org.root().id();
155     }
156
157     disableDescendantSelector(): boolean {
158         const contextOrg = this.org.get(this.options.primaryOrgId);
159         return contextOrg.children().length === 0;
160     }
161
162     get includeAncestors() {
163         return this.familySelectors.get('includeAncestors');
164     }
165     get includeDescendants() {
166         return this.familySelectors.get('includeDescendants');
167     }
168
169 }
170