1. 程式人生 > >[AngularFire] Angular File Uploads to Firebase Storage with Angular control value accessor

[AngularFire] Angular File Uploads to Firebase Storage with Angular control value accessor

state spa lec span tor event allow load loading

The upload class will be used in the service layer. Notice it has a constructor for file attribute, which has a type of File. This will allows us to initialize new uploads with a JavaScript File object. You will see why this is important in the next step.

export class Upload {
  $key: string;
  file:File;
  name:string;
  url:string;
  progress:number;
  createdAt: Date 
= new Date(); constructor(file:File) { this.file = file; } }

Then build the upload service, which can inject to component:

import { Injectable } from ‘@angular/core‘;
import {Subject} from ‘rxjs/Subject‘;
import {MatSnackBar} from ‘@angular/material‘;

import * as firebase from ‘firebase‘;
import UploadTaskSnapshot 
= firebase.storage.UploadTaskSnapshot; import {Upload} from ‘./upload‘; @Injectable() export class UploadService { uploading$ = new Subject<number>(); completed$ = new Subject<Upload>(); constructor( private snackBar: MatSnackBar ) { } uploadFile(upload: Upload, folder: string) {
// Create a storage ref const storageRef = firebase.storage().ref(); const uploadTask = storageRef.child(`${folder}/${upload.file.name}`).put(upload.file); // Upload file uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED, (snapshot: UploadTaskSnapshot) => { upload.progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; this.uploading$.next(upload.progress); }, (err) => { this.snackBar.open(err.message, ‘OK‘, { duration: 3000, }); }, () => { upload.url = uploadTask.snapshot.downloadURL; upload.name = upload.file.name; this.completed$.next(upload); this.uploading$.next(null); }); } deleteUpload(name: string, folder: string) { const storageRef = firebase.storage().ref(); storageRef.child(`${folder}/${name}`).delete(); this.completed$.next(); } }

Component:

import {ChangeDetectionStrategy, Component, forwardRef, Input} from ‘@angular/core‘;
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from ‘@angular/forms‘;

import {UploadService} from ‘../../services/upload.service‘;
import {Upload} from ‘../../services/upload‘;
import {Observable} from ‘rxjs/Observable‘;

export const TYPE_CONTROL_ACCESSOR = {
  provide: NG_VALUE_ACCESSOR,
  multi: true,
  useExisting: forwardRef(() => ImageUploaderComponent)
};

@Component({
  selector: ‘image-uploader‘,
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [TYPE_CONTROL_ACCESSOR],
  templateUrl: ‘./image-uploader.component.html‘,
  styleUrls: [‘./image-uploader.component.scss‘]
})
export class ImageUploaderComponent implements ControlValueAccessor {

  @Input() img;

  private onTouch: Function;
  private onModelChange: Function;
  private value: string;

  file: Upload;
  currentUpload: Upload;
  progress$: Observable<number>;

  constructor(private uploadService: UploadService) {
    this.progress$ = this.uploadService.uploading$;
    this.uploadService.completed$.subscribe((upload) => {

      if (upload) {
        this.setSelected(upload.url);
        this.currentUpload = upload;
      } else {
        this.setSelected(‘‘);
        this.currentUpload = null;
      }
    });
  }

  onChange($event) {
    const file = $event.target.files[0];
    this.file = new Upload(file);
    this.uploadService.uploadFile(this.file, ‘icons‘);
  }

  writeValue(value: any): void {
    this.value = value;
  }

  registerOnChange(fn: Function): void {
    this.onModelChange = fn;
  }

  registerOnTouched(fn: Function): void {
    this.onTouch = fn;
  }

  setSelected(value: string): void {
    this.value = value;
    this.onModelChange(value);
    this.onTouch();
  }

  clear() {
    if (this.file) {
      this.uploadService.deleteUpload(this.file.name, ‘icons‘);
      this.setSelected(‘‘);
    }
  }
}

Template:

    <div *ngIf="progress$ | async as p">
      <mat-progress-bar mode="determinate" [value]="p"></mat-progress-bar>
    </div>
    <mat-card-subtitle>
      Select / upload icon
    </mat-card-subtitle>

    <mat-card-content fxLayout="column">
      <div fxLayout="row" fxLayoutAlign="space-around">
        <div
          *ngIf="currentUpload"
          class="image-container"
          fxFlex="30%">
          <img [src]="currentUpload?.url || ‘‘" [alt]="currentUpload?.name || ‘‘">
        </div>
      </div>

[AngularFire] Angular File Uploads to Firebase Storage with Angular control value accessor