Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kumkanillam/6a913905102f70e13a3dc906f8bd025e to your computer and use it in GitHub Desktop.
Save kumkanillam/6a913905102f70e13a3dc906f8bd025e to your computer and use it in GitHub Desktop.
User Search Example
import Ember from 'ember';
import { task, timeout } from 'ember-concurrency';
const {
get, set, computed, isPresent, tryInvoke,
inject: { service }
} = Ember;
const DEBOUNCE_TIMEOUT = 500;
const HAS_ERROR = Symbol('error');
const USER_STATUS = new Map([
[true, 'That user exists'],
[false, 'That user does not exist']
]);
const FORM_STATUS = new Map([
[HAS_ERROR, 'has-error'],
[true, 'has-success'],
[false, '']
]);
export default Ember.Component.extend({
api: service(),
classNames: ['form-group'],
classNameBindings: ['formStatusClass'],
userStatus: computed('userExists', {
get() {
return USER_STATUS.get(get(this, 'userExists'));
}
}),
formStatusClass: computed('userExists', 'error', {
get() {
return isPresent(get(this, 'error')) ?
FORM_STATUS.get(HAS_ERROR) :
FORM_STATUS.get(get(this, 'userExists'));
}
}),
searchForEmail: task(function * () {
const api = get(this, 'api');
yield timeout(DEBOUNCE_TIMEOUT);
let query = get(this, 'queryTerm');
try {
let { exists } = yield api.queryEmail(query);
set(this, 'userExists', exists);
set(this, 'error', null);
tryInvoke(this, 'onComplete');
} catch (error) {
if (api.isServerError(error)) {
set(this, 'error', error);
tryInvoke(this, 'onError', [error]);
} else {
throw error;
}
}
}).restartable()
});
import Ember from 'ember';
const { get, set, inject: { service } } = Ember;
export default Ember.Controller.extend({
api: service(),
appName: 'Ember Twiddle',
successfull: 0,
failed: 0,
actions: {
handleUnauthorized(error) {
const api = get(this, 'api');
this.incrementProperty('failed');
set(this, 'needsLogin', api.isUnauthorizedError(error));
}
}
});
import Ember from 'ember';
const RESPONSES = [
[200, '{"exists":true}'],
[200, '{"exists":true}'],
[200, '{"exists":true}'],
[200, '{"exists":true}'],
[200, '{"exists":true}'],
[200, '{"exists":false}'],
[200, '{"exists":false}'],
[200, '{"exists":false}'],
[200, '{"exists":false}'],
[200, '{"exists":false}'],
[401, '{"error":"Unauthorized (show fake login)"}'],
[401, '{"error":"Unauthorized (show fake login)"}'],
[401, '{"error":"Unauthorized (show fake login)"}'],
[401, '{"error":"Unauthorized (show fake login)"}'],
[422, '{"error":"Unprocessable email (just pretend)"}'],
[422, '{"error":"Unprocessable email (just pretend)"}'],
[422, '{"error":"Unprocessable email (just pretend)"}'],
[422, '{"error":"Unprocessable email (just pretend)"}'],
[500, '{"error":"The fake server broke!"}'],
[500, '{"error":"The fake server broke!"}']
];
function sample(arr) {
const { floor, random } = Math;
return arr[floor(random() * arr.length)];
}
export function initialize() {
console.count('initialize');
Ember.$.mockjax({
url: '/api/users*',
response() {
let [status, responseText] = sample(RESPONSES);
this.status = status;
this.responseText = responseText;
}
});
}
export default {
name: 'mock-api',
initialize
};
import Ember from 'ember';
const { RSVP } = Ember;
class ServerError extends Error {
constructor(message) {
super();
this.name = 'ServerError';
this.message = message;
this.stack = new Error().stack;
}
}
class UnauthorizedError extends ServerError {
constructor() {
super(...arguments);
this.name = 'UnauthorizedError';
}
}
class ValidationError extends ServerError {
constructor() {
super(...arguments);
this.name = 'ValidationError';
}
}
const RESPONSE_ERRORS = {
401: UnauthorizedError,
422: ValidationError,
_default: ServerError
};
function errorType(klass) {
return function (error) {
return error instanceof klass;
};
}
export default Ember.Service.extend({
queryEmail(email) {
let query = encodeURIComponent(email);
return new RSVP.Promise((resolve, reject) => {
Ember.$.getJSON(`/api/users?email=${query}`)
.done(resolve)
.fail(reject);
})
.catch(jqXHR => {
let { status, responseText } = jqXHR;
let data = JSON.parse(responseText);
let ResponseError = RESPONSE_ERRORS[status] ||
RESPONSE_ERRORS._default;
throw new ResponseError(data.error);
});
},
isServerError: errorType(ServerError),
isUnauthorizedError: errorType(UnauthorizedError),
isValidationError: errorType(ValidationError)
});
<div class="container">
<h1>Welcome to {{appName}}</h1>
<p>Successful searches: <code>{{successfull}}</code></p>
<p>Failed searches: <code>{{failed}}</code></p>
{{#unless needsLogin}}
{{email-search
onComplete=(action incrementProperty "successfull")
onError=(action "handleUnauthorized")}}
{{else}}
<p>Please <strong>pretend to</strong> log in. {{{concat "&#x0001f609;"}}}</p>
<div class="form-group">
<label for={{concat elementId '-login-user'}}>Username:</label>
<input type="text" id={{concat elementId '-login-user'}} class="form-control" placeholder="Jane Doe">
</div>
<div class="form-group">
<label for={{concat elementId '-login-user'}}>Password:</label>
<input type="password" id={{concat elementId '-login-password'}} class="form-control">
<p class="help-block">
This is for illistrative purposes and no data is collected. This form is
a no-op. But still, <strong>please don't use real passwords</strong>.
</p>
</div>
<div class="form-group">
<button
type="button"
class="btn btn-default"
onclick={{action (mut needsLogin) false}}>
Cancel
</button>
<button
type="submit"
class="btn btn-primary"
onclick={{action (mut needsLogin) false}}>
Login
</button>
</div>
{{/unless}}
</div>
<label for={{concat elementId '-query'}}>Email:</label>
<div class="input-group">
{{input
type="email"
class="form-control"
value=queryTerm
id=(concat elementId '-query')
key-up=(perform searchForEmail)}}
<span class="input-group-addon">
{{{if searchForEmail.isRunning '&#x0001f914;' '&#x0001f50d;'}}}
</span>
</div>
{{#if error}}
<p class="help-block">{{error}}</p>
{{else if userStatus}}
<p class="help-block">{{userStatus}}</p>
{{/if}}
{
"version": "0.10.5",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js",
"jquery-mockjax": "https://cdnjs.cloudflare.com/ajax/libs/jquery-mockjax/1.6.2/jquery.mockjax.js",
"ember": "2.6.0",
"ember-data": "2.6.0",
"ember-template-compiler": "2.6.0",
"ember-testing": "2.6.0"
},
"addons": {
"ember-concurrency": "0.7.9",
"ember-bootstrap": "0.10.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment