All Projects → algonauti → ember-active-storage

algonauti / ember-active-storage

Licence: MIT license
Direct uploads with Rails' Active Storage

Programming Languages

javascript
184084 projects - #8 most used programming language
HTML
75241 projects
CSS
56736 projects
Handlebars
879 projects

Projects that are alternatives of or similar to ember-active-storage

ember-cli-string-helpers
Set of the String helpers extracted from DockYard's ember-composable-helpers.
Stars: ✭ 73 (+247.62%)
Mutual labels:  ember
ember-validity-modifier
Ember Octane addon to add custom validity (form validation) to form fields
Stars: ✭ 28 (+33.33%)
Mutual labels:  ember
ember-cli-blog
Tom Dale's blog example updated for the Ember CLI
Stars: ✭ 87 (+314.29%)
Mutual labels:  ember
ember-paper-stepper
Step up your app with material steppers.
Stars: ✭ 28 (+33.33%)
Mutual labels:  ember
webmarks
remoteStorage-enabled bookmarking app
Stars: ✭ 65 (+209.52%)
Mutual labels:  ember
ember-resources
An implementation of Resources with some helpful utilities
Stars: ✭ 68 (+223.81%)
Mutual labels:  ember
ember-qunit-assert-helpers
An ember-addon that provides additional QUnit 2.0 assertions specific to Ember.js.
Stars: ✭ 12 (-42.86%)
Mutual labels:  ember
irene
I AM SHER LOCKED. Dashboard for Appknox Users.
Stars: ✭ 15 (-28.57%)
Mutual labels:  ember
7cart
7cart is a php7 project for building online shops, catalogs or service platforms. 7cart built with simple code and database schema. It is easy to support and fast.
Stars: ✭ 27 (+28.57%)
Mutual labels:  ember
ember-mug
Reverse engineered bluetooth protocol for Ember Mugs
Stars: ✭ 87 (+314.29%)
Mutual labels:  ember
ember-app-shell
No description or website provided.
Stars: ✭ 23 (+9.52%)
Mutual labels:  ember
ember-angle-brackets-codemod
Codemod to convert curly braces syntax to angle brackets syntax
Stars: ✭ 58 (+176.19%)
Mutual labels:  ember
emberconf-2018
EmberConf 2018 notes.
Stars: ✭ 56 (+166.67%)
Mutual labels:  ember
ember-cli-geo
Geolocation service for Ember.js web apps
Stars: ✭ 48 (+128.57%)
Mutual labels:  ember
ember-cli-mirage-graphql
A library for mocking GraphQL with Ember CLI Mirage
Stars: ✭ 24 (+14.29%)
Mutual labels:  ember
ember-cli-storybook
📒 Ember storybook adapter
Stars: ✭ 35 (+66.67%)
Mutual labels:  ember
ember-fastboot-addon-tests
Addon testing support for Fastboot compatibility
Stars: ✭ 24 (+14.29%)
Mutual labels:  ember
ember-i18n-cp-validations
ember-i18n support for ember-cp-validations
Stars: ✭ 20 (-4.76%)
Mutual labels:  ember
deprecation-app
Deprecation guides for ember.js, ember-data, and ember-cli
Stars: ✭ 18 (-14.29%)
Mutual labels:  ember
vim-ember-hbs
Ember Handlebars/HTMLBars plugin for Vim with indentation support
Stars: ✭ 45 (+114.29%)
Mutual labels:  ember

ember-active-storage

CI Ember Observer Score

Installation

ember install @algonauti/ember-active-storage

Usage

The addon provides an activeStorage service that allows you to:

  • send files to your Rails backend's direct upload controller;
  • listen to upload progress events.

Assuming your template has a file input like:

<input type="file" {{on "change" (fn this.upload)}} />

and your ember model has an avatar attribute defined as has_one_attached :avatar on its corresponding Active Record model, then in your component (or controller) the upload action would look like:

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';

export default class UploadComponent extends Component {
  @service
  activeStorage;

  @tracked
  uploadProgress = 0;

  @action
  upload(event) {
    const files = event.target.files;
    if (files) {
      const directUploadURL = '/rails/active_storage/direct_uploads';

      for (var i = 0; i < files.length; i++) {
        this.activeStorage
          .upload(files.item(i), directUploadURL, {
            onProgress: (progress, event) => {
              this.uploadProgress = progress;
            },
          })
          .then((blob) => {
            const signedId = blob.signedId;

            this.model.avatar = signedId;
          });
      }
    }
  }
}
  • directUploadURL is the path referencing ActiveStorage::DirectUploadsController on your Rails backend (or a custom one built on top of that).
  • The uploadProgress property will hold a value between 0 and 100 that you might use in your template to show upload progress.
  • After the upload promise is resolved and signedId is set in your model, when a model.save() is triggered, the Rails backend will use such signedId to associate an ActiveStorage::Attachment record to your backend model's record.

loadstart, load, loadend, error, abort, timeout events invokes onLoadstart, onLoad, onLoadend, onError, onAbort, onTimeout accordingly. For example; If you want to use the loadend event in your app, you can use like;

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';

export default class UploadComponent extends Component {
  @service
  activeStorage;

  @tracked
  uploadProgress = 0;

  @action
  upload(event) {
    const files = event.target.files;
    if (files) {
      const directUploadURL = '/rails/active_storage/direct_uploads';

      for (var i = 0; i < files.length; i++) {
        this.activeStorage
          .upload(files.item(i), directUploadURL, {
            onProgress: (progress, event) => {
              this.uploadProgress = progress;
            },
            onLoadend: (event) => {
              debug(`Event captured ${event}`); // https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent
            },
          })
          .then((blob) => {
            const signedId = blob.signedId;

            this.model.avatar = signedId;
          });
      }
    }
  }
}

If you need the actual XHR object in your app, you can use the onXHROpened event. It returns the XHR object reference. For example:

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';

export default class UploadComponent extends Component {
  @service
  activeStorage;

  @tracked
  uploadProgress = 0;
  
  @tracked
  xhrs = [];

  @action
  upload(event) {
    const files = event.target.files;
    if (files) {
      const directUploadURL = '/rails/active_storage/direct_uploads';

      for (var i = 0; i < files.length; i++) {
        this.activeStorage
          .upload(files.item(i), directUploadURL, {
            onProgress: (progress, event) => {
              this.uploadProgress = progress;
            },
            onXHROpened: (xhr) => {
              this.xhrs.push(xhr);  // so you can loop over this.xhrs and invoke abort()
            },
          })
          .then((blob) => {
            const signedId = blob.signedId;

            this.model.avatar = signedId;
          });
      }
    }
  }
}

There is an ember-active-storage ENV config with only one parameter called url. With this config help, you can omit the upload url now. For example:

ENV['ember-active-storage'] = {
  url: 'http://your-domain/rails/active_storage/direct_uploads',
};

Now you can call the upload function without the upload url.

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';

export default class UploadComponent extends Component {
  @service
  activeStorage;

  @tracked
  uploadProgress = 0;

  @action
  upload(event) {
    const files = event.target.files;
    if (files) {
      for (var i = 0; i < files.length; i++) {
        this.activeStorage
          .upload(files.item(i), {
            onProgress: (progress, event) => {
              this.uploadProgress = progress;
            },
          })
          .then((blob) => {
            const signedId = blob.signedId;

            this.model.avatar = signedId;
          });
      }
    }
  }
}

Sending authentication headers

It's pretty common that you want to protect with authentication the direct uploads endpoint on your Rails backend. If that's the case, the activeStorage service will need to send authentication headers together with the direct upload request.

To achieve that, you'll need to extend the activeStorage service provided by the addon and add a headers computed property. For example, if you're using ember-simple-auth, it will be a 2-steps process. First you'll need to define an authenticatedHeaders computed property in your session service, like this:

// app/services/session.js
import Service from '@ember/service';
import { inject as service } from '@ember/service';

export default class MySessionService extends Service {
  @service
  session;

  get authenticatedHeaders() {
    const { access_token } = this.session.authenticated;

    return { Authorization: `Bearer ${access_token}` };
  }
}

Then, you will alias that property in your activeStorage service, like this:

// app/services/active-storage.js
import ActiveStorage from '@algonauti/ember-active-storage/services/active-storage';
import { inject as service } from '@ember/service';

export default class ActiveStorageService extends ActiveStorage {
  @service('my-session')
  session;

  get headers() {
    this.session.authenticatedHeaders;
  }
}

Also note: if the download endpoint is protected as well, and you're using an ajax request to download files, then don't forget to include the same headers in that request as well.

Contributing

Installation

  • git clone <repository-url>
  • cd ember-active-storage
  • yarn install

Linting

  • yarn lint:js
  • yarn lint:js --fix

Running tests

  • ember test – Runs the test suite on the current Ember version
  • ember test --server – Runs the test suite in "watch mode"
  • yarn test – Runs ember try:each to test your addon against multiple Ember versions

Running the dummy application

For more information on using ember-cli, visit https://ember-cli.com/.

License

This project is licensed under the MIT License.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].