Skip to content

Instantly share code, notes, and snippets.

@sunishsheth2009
Created June 15, 2020 21:01
Show Gist options
  • Save sunishsheth2009/b942d1794f9fab6cf02a4b56eb0223d5 to your computer and use it in GitHub Desktop.
Save sunishsheth2009/b942d1794f9fab6cf02a4b56eb0223d5 to your computer and use it in GitHub Desktop.
Route-async
import { tracked } from '@glimmer/tracking';
class ProfileActions {
@tracked profile = null;
getUpdateProfile(/* profileId */) {
// API call done here
this.profile = new Promise((resolve) => {
const profile = {
firstname: 'harry',
lastname: 'potter'
};
setTimeout(() => {
resolve(profile);
}, 1000);
});
return this.profile;
}
get profile() {
return this.profile;
}
}
// We do this so we can "mimic" service behavior the way they're instantiated
// in Ember world [e.g. - actions: profileActions(...) instead of directly exposing class]
export default function profileActions() {
return new ProfileActions(...arguments);
}
import Ember from 'ember';
import { getOwner } from '@ember/application';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { assert } from '@ember/debug';
/**
* This is the suspense component which be used to by container components when making API calls in a component.
* This component handles server side rendering issues and loading and error states out of the box for the consumer
* @param {Function|object} [promise] Required promise for the component to render the loading, success and error state
* @param {boolean} [blockRender] Default is false. Used for deciding if the fastboot server should wait for the API call to complete
* ...
* @example
* <Suspense
* @promise={{this.promise}}
* @blockRender={{false}}
* as |task|
* >
* {{#if task.isLoading}}
* Loading...
* {{else if task.isSuccess}}
* {{task.data.userRequest.name}}: {{task.data.userRequest.time}}
* {{else if task.isError}}
* Error occurred: {{task.errorReason}}
* {{/if}}
* </Suspense>
*/
class Task {
@tracked data = null;
@tracked errorReason = null;
@tracked isLoading = false;
get isError() {
return Boolean(this.errorReason);
}
get isSuccess() {
return Boolean(this.data);
}
}
export default class Suspense extends Component {
constructor() {
super(...arguments);
// Recommended way to get the service in the addons and not forcing fastboot for every consuming application
// https://ember-fastboot.com/docs/addon-author-guide#accessing-the-fastboot-service
this.fastboot = getOwner(this).lookup('service:fastboot');
this.blockRender = this.args.blockRender || false;
assert(
'ember-cli-fastboot dependency should be installed if we want to block render',
!(this.blockRender && !this.fastboot)
);
}
get data() {
const blockRender = this.blockRender;
const promiseArg = this.args.promise;
const task = new Task();
const promise =
typeof promiseArg === 'function' ? promiseArg() : promiseArg;
if (this.promise === promise) {
return this.task;
}
this.task = task;
this.promise = promise;
task.isLoading = true;
if (blockRender && this.fastboot.isFastBoot) {
// https://github.com/ember-fastboot/ember-cli-fastboot#delaying-the-server-response
this.fastboot.deferRendering(promise);
}
promise.then(
(payload) => {
task.data = payload;
task.isLoading = false;
},
(e) => {
task.errorReason = e;
task.isLoading = false;
// https://github.com/emberjs/ember.js/issues/15569
if (!Ember.testing) {
throw e;
}
}
);
return task;
}
}
import Controller from '@ember/controller';
export default class ApplicationController extends Controller {
appName = 'Ember Twiddle';
}
import EmberRouter from '@ember/routing/router';
import config from './config/environment';
const Router = EmberRouter.extend({
location: 'none',
rootURL: config.rootURL
});
Router.map(function() {
this.route('child');
});
export default Router;
import Route from '@ember/routing/route';
import Profile from '../classes/profile';
import { inject as service } from '@ember/service';
import RSVP from 'rsvp';
export default class Application extends Route {
@service router
init() {
this.profile = new Profile();
}
model(params, transition) {
console.log(transition);
console.log(this.router);
const profile = this.profile.getUpdateProfile();
// Check for isBrowser check to see if the call is made over bigPipe mode
return { profile };
}
}
import Route from '@ember/routing/route';
import RSVP from 'rsvp';
export default Route.extend({
model(pararms, transition) {
console.log(transition.to.name);
console.log(this.routeName);
const parentApplication = this.modelFor('application');
return RSVP.hash({
...parentApplication
});
}
});
<h1>Welcome to {{this.appName}}</h1>
<Suspense
@promise={{model.profile}}
@blockRender={{false}}
as |task|
>
{{#if task.isLoading}}
Loading...
{{else if task.isSuccess}}
{{task.data.firstname}} {{task.data.lastname}}
{{else if task.isError}}
Error occurred: {{task.errorReason}}
{{/if}}
</Suspense>
<br>
<br>
{{outlet}}
<br>
<br>
{
"version": "0.17.1",
"EmberENV": {
"FEATURES": {},
"_TEMPLATE_ONLY_GLIMMER_COMPONENTS": false,
"_APPLICATION_TEMPLATE_WRAPPER": true,
"_JQUERY_INTEGRATION": true
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js",
"ember": "3.18.1",
"ember-template-compiler": "3.18.1",
"ember-testing": "3.18.1"
},
"addons": {
"@glimmer/component": "1.0.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment