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