Skip to content

Instantly share code, notes, and snippets.

@iezer
Last active October 11, 2019 15:10
Show Gist options
  • Save iezer/3361c1e38666f6ba697953b333b54457 to your computer and use it in GitHub Desktop.
Save iezer/3361c1e38666f6ba697953b333b54457 to your computer and use it in GitHub Desktop.
Ember Concurrency Composable Tasks
import Component from '@ember/component'
import { inject as service } from '@ember/service';
import { alias } from '@ember/object/computed';
export default Component.extend({
flashMessages: service(),
message: alias('flashMessages.message')
});
import Ember from 'ember';
import progressTask from '../tasks/progress';
import { inject as service } from '@ember/service';
export default Ember.Controller.extend({
appName: 'Ember Twiddle',
message: null,
loading: service(),
successful() {
return Ember.RSVP.resolve('success!');
},
failure() {
return Ember.RSVP.reject({ message: 'failed' });
},
doSuccess: progressTask(function*() {
this.set('message', null);
const response = yield this.successful();
this.set('message', response)
}).drop(),
doFail: progressTask(function*() {
this.set('message', null);
const response = yield this.failure();
this.set('message', response)
}).drop()
});
import Ember from 'ember';
import { inject as service } from '@ember/service';
function pause(ms) {
return new Ember.RSVP.Promise((resolve) => setTimeout(() => resolve(), ms));
}
// Implementation without a task
export default Ember.Controller.extend({
message: null,
flashMessages: service(),
loading: service(),
doApiCall(promise) {
const { flashMessages, loading } = this;
this.set('message', null);
loading.start();
pause(500)
.then(() => { return promise; })
.then(result => {
if(this.isDestroyed) { return; }
this.set('message', response);
})
.catch((e) => {
if(this.isDestroyed) { return; }
const errorMessage =
(e.errors && e.errors.mapBy('detail')) || e.message || 'There was an error';
flashMessages.danger(errorMessage);
})
.finally(() => {
if(this.isDestroyed) { return; }
loading.stop();
});
},
successful() {
return Ember.RSVP.resolve('success!');
},
failure() {
return Ember.RSVP.reject({ message: 'failed' });
},
actions: {
doSuccess() {
return this.doApiCall(this.successful());
},
doFail() {
return this.doApiCall(this.failure());
}
}
});
import Ember from 'ember';
import { later } from '@ember/runloop';
// https://github.com/poteto/ember-cli-flash
export default Ember.Service.extend({
message: null,
danger(msg) {
this.set('message', msg);
later(() => this.set('message', null), 1500);
}
});
import Ember from 'ember';
// http://ricostacruz.com/nprogress/
export default Ember.Service.extend({
isLoading: false,
start() {
this.set('isLoading', true);
},
stop() {
this.set('isLoading', false);
}
});
import { task, timeout } from 'ember-concurrency';
import { getOwner } from '@ember/application';
/*
Task wrapper to extract away parsing error messages and showing as flash message.
See https://github.com/machty/ember-concurrency/pull/117
*/
export default function errorTask(_definedTaskFn) {
let taskProperty = task(_definedTaskFn).drop();
let originalTaskFn = taskProperty.taskFn;
taskProperty.taskFn = function*(...args) {
const owner = getOwner(this);
const flashMessages = owner.lookup('service:flashMessages');
flashMessages.set('message', null);
const loading = owner.lookup('service:loading');
loading.start();
try {
yield timeout(1000);
return yield* originalTaskFn.apply(this, args);
} catch (e) {
const errorMessage =
(e.errors && e.errors.mapBy('detail')) || e.message || 'There was an error';
flashMessages.danger(errorMessage);
} finally {
loading.stop();
}
};
return taskProperty;
}
{{#if this.loading.isLoading}}
{{loading-spinner}}
{{else}}
{{flash-messages}}
<br>
<br>
<p>
Message: {{message}}
</p>
<p>
<button onclick={{perform doSuccess}}>
{{if doSuccess.isRunning 'loading...' 'Success'}}
</button>
<button onclick={{perform doFail}}>
{{if doFail.isRunning 'loading...' 'Fail'}}
</button>
</p>
{{/if}}
{{#if message}}
<p style='color: gray; position: absolute; top: -10px; background-color: #FFB6C1; border: 1px solid red; padding: 5px;'>
Flash Message: {{message}}
</p>
{{/if}}
<svg width="20" height="20" viewBox="0 0 57 57" xmlns="http://www.w3.org/2000/svg" stroke="#ff0061">
<g fill="#ff0061" fill-rule="evenodd">
<g transform="translate(1 1)" stroke-width="2">
<circle cx="5" cy="20" r="5">
<animate attributeName="cy"
begin="0s" dur="2.2s"
values="50;5;50;50"
calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="cx"
begin="0s" dur="2.2s"
values="5;27;49;5"
calcMode="linear"
repeatCount="indefinite" />
</circle>
<circle cx="27" cy="5" r="5">
<animate attributeName="cy"
begin="0s" dur="2.2s"
from="5" to="5"
values="5;50;50;5"
calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="cx"
begin="0s" dur="2.2s"
from="27" to="27"
values="27;49;5;27"
calcMode="linear"
repeatCount="indefinite" />
</circle>
<circle cx="49" cy="50" r="5">
<animate attributeName="cy"
begin="0s" dur="2.2s"
values="50;50;5;50"
calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="cx"
from="49" to="49"
begin="0s" dur="2.2s"
values="49;5;27;49"
calcMode="linear"
repeatCount="indefinite" />
</circle>
</g>
</g>
</svg>
{
"version": "0.15.0",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js",
"ember": "3.2.2",
"ember-template-compiler": "3.2.2",
"ember-testing": "3.2.2"
},
"addons": {
"ember-data": "3.2.0",
"ember-concurrency": "0.8.21"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment