LP#1850547: new Angular file-reader component
authorGalen Charlton <gmc@equinoxinitiative.org>
Fri, 8 Nov 2019 23:39:34 +0000 (18:39 -0500)
committerBill Erickson <berickxx@gmail.com>
Thu, 3 Sep 2020 15:50:40 +0000 (11:50 -0400)
This adds a ControlValueAccessor wrapper around a file
reader form input. Any file loaded via that input is interpreted
as a text file whose lines are to be split into an array on
newlines, with leading and trailng whitespace removed.

Usage is:

<eg-file-reader [(ngModel)]="fileContents">
</eg-file-reader>

Sponsored-by: Evergreen Community Development Initiative
Sponsored-by: Georgia Public Library Service
Sponsored-by: Indiana State Library
Sponsored-by: C/W MARS
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Signed-off-by: Tiffany Little <tlittle@georgialibraries.org>
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/share/common-widgets.module.ts
Open-ILS/src/eg2/src/app/share/file-reader/file-reader.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/file-reader/file-reader.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts

index e1f85cd..5064f88 100644 (file)
@@ -15,6 +15,7 @@ import {OrgSelectComponent} from '@eg/share/org-select/org-select.component';
 import {DateRangeSelectComponent} from '@eg/share/daterange-select/daterange-select.component';
 import {DateTimeSelectComponent} from '@eg/share/datetime-select/datetime-select.component';
 import {ContextMenuModule} from '@eg/share/context-menu/context-menu.module';
+import {FileReaderComponent} from '@eg/share/file-reader/file-reader.component';
 
 
 @NgModule({
@@ -24,7 +25,8 @@ import {ContextMenuModule} from '@eg/share/context-menu/context-menu.module';
     DateSelectComponent,
     OrgSelectComponent,
     DateRangeSelectComponent,
-    DateTimeSelectComponent
+    DateTimeSelectComponent,
+    FileReaderComponent,
   ],
   imports: [
     CommonModule,
@@ -45,8 +47,9 @@ import {ContextMenuModule} from '@eg/share/context-menu/context-menu.module';
     OrgSelectComponent,
     DateRangeSelectComponent,
     DateTimeSelectComponent,
-    ContextMenuModule
-  ]
+    ContextMenuModule,
+    FileReaderComponent,
+  ],
 })
 
 export class CommonWidgetsModule { }
diff --git a/Open-ILS/src/eg2/src/app/share/file-reader/file-reader.component.html b/Open-ILS/src/eg2/src/app/share/file-reader/file-reader.component.html
new file mode 100644 (file)
index 0000000..d938bf6
--- /dev/null
@@ -0,0 +1 @@
+<input type="file" (change)="changeListener($event)" />
diff --git a/Open-ILS/src/eg2/src/app/share/file-reader/file-reader.component.ts b/Open-ILS/src/eg2/src/app/share/file-reader/file-reader.component.ts
new file mode 100644 (file)
index 0000000..4cbe5de
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * <eg-file-reader [(ngModel)]="fileContents">
+ * </eg-file-reader>
+ */
+import {Component, OnInit, Input, Output, ViewChild,
+    TemplateRef, EventEmitter, ElementRef, forwardRef} from '@angular/core';
+import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {Observable, of, Subject} from 'rxjs';
+import {map, tap, reduce, mergeMap, mapTo, debounceTime, distinctUntilChanged, merge, filter} from 'rxjs/operators';
+
+@Component({
+  selector: 'eg-file-reader',
+  templateUrl: './file-reader.component.html',
+  providers: [{
+    provide: NG_VALUE_ACCESSOR,
+    useExisting: forwardRef(() => FileReaderComponent),
+    multi: true
+  }]
+})
+export class FileReaderComponent implements ControlValueAccessor, OnInit {
+
+    // Stub functions required by ControlValueAccessor
+    propagateChange = (_: any) => {};
+    propagateTouch = () => {};
+
+    ngOnInit() {
+    }
+
+    changeListener($event): void {
+        const me = this;
+        if ($event.target.files.length < 1) {
+            return;
+        }
+        const reader = new FileReader();
+        reader.onloadend = function(e) {
+            me.propagateChange(me.parseFileContents(reader.result));
+        };
+        reader.readAsText($event.target.files[0]);
+    }
+
+    parseFileContents(contents): Array<string> {
+        const values = contents.split('\n');
+        values.forEach((val, idx) => {
+            val = val.replace(/\s+$/, '');
+            val = val.replace(/^\s+/, '');
+            values[idx] = val;
+        });
+        return values;
+    }
+
+    writeValue(value: any) {
+        // intentionally empty
+    }
+
+    registerOnChange(fn) {
+        this.propagateChange = fn;
+    }
+
+    registerOnTouched(fn) {
+        this.propagateTouch = fn;
+    }
+}
index da1155e..9a93b3e 100644 (file)
 
 
 <div>
+  <h4>File reader component</h4>
+  <eg-file-reader [(ngModel)]="fileContents"></eg-file-reader>
+  <h5>Contents are:</h5>
+  <ol *ngIf="fileContents && fileContents.length > 0">
+    <li *ngFor="let val of fileContents">{{val}}</li>
+  </ol>
+</div>
+
+<div>
   <h4>Cross-tab communications example</h4>
   <p>To test, open this sandbox in a second browser tab. Enter something in the input box below, then switch to the other tab and click anywhere on the page. 
      You should see the message that you sent to the other browser tab.</p>
index caf86fa..7e31ec6 100644 (file)
@@ -103,6 +103,9 @@ export class SandboxComponent implements OnInit {
     // selector field value on metarecord object
     aMetarecord: string;
 
+    // file-reader example
+    fileContents:  Array<string>;
+
     // cross-tab communications example
     private sbChannel: any;
     sbChannelText: string;