Skip to content

Instantly share code, notes, and snippets.

@chriskrycho
Last active June 16, 2017 20:04
Show Gist options
  • Save chriskrycho/7da0c3b66a931783f650c4ca01ada0d5 to your computer and use it in GitHub Desktop.
Save chriskrycho/7da0c3b66a931783f650c4ca01ada0d5 to your computer and use it in GitHub Desktop.
messages-and-errors
import Ember from 'ember';
const { computed, Component, get, run } = Ember;
export default Component.extend({
role: 'alert',
message: null, // passed in
classNameBindings: ['messageTypeClass'],
messageTypeClass: computed('message.type', function (): string {
const block = 'user-messages',
element = 'message',
modifiers = [get(this, 'message.type')];
return block + element;
}),
attributeBindings: [
'id',
'role',
'message.id:data-test-userMessages'
],
id: computed('message.id', function (): string {
return `UserMessage${get(this, 'message.id')}`;
}),
didInsertElement() {
this._super(...arguments);
run.scheduleOnce('afterRender', this, () => {
const messageEl = document.getElementById(get(this, 'id'));
if (messageEl) {
messageEl.focus();
messageEl.setAttribute('tabindex', '0');
}
});
}
});
import Ember from 'ember';
const { computed, Component, get, inject } = Ember;
export default Component.extend({
onClear: undefined,
classNames: ['user-messages'],
userMessages: inject.service(),
messages: computed.readOnly('userMessages.messages'),
actions: {
clear(id) {
get(this, 'userMessages').clear({ id });
const onClear = get(this, 'onClear');
if (onClear && typeof(onClear) === 'function') {
onClear(id);
}
}
}
});
import Ember from 'ember';
export default Ember.Controller.extend({
inputValue: '',
appName: 'Bargle',
actions: {
addMessage(value) {
Ember.get(this, 'userMessages').add('info', value);
},
clearMessages() {
Ember.get(this, 'userMessages').clearAll();
},
},
});
import Ember from 'ember';
import { negate, matchesProperty } from 'lodash';
const { assert, computed, get, getProperties, isNone, Service, set } = Ember;
export const ERROR = 'error';
export const INFO = 'info';
export const WARNING = 'warning';
export const TYPES = [ERROR, INFO, WARNING];
export const UserMessage = Ember.Object.extend({
type: INFO,
detail: '',
id: null,
dismissible: true,
target: null,
global: computed.bool('target')
});
const byTarget = (target: string) => negate(matchesProperty('target', target));
const byId = (id: number) => negate(matchesProperty('id', id));
export default Service.extend({
messages: null, // Initialized in `init`.
_lastId: 0,
_nextId() {
const { isDestroying, isDestroyed } = getProperties(this, 'isDestroying', 'isDestroyed');
if (isDestroying || isDestroyed) { return -1; }
const next = get(this, '_lastId') + 1;
set(this, '_lastId', next);
return next;
},
add(type, detail, target = null, dismissible = true) {
const messages = get(this, 'messages');
assert('`userMessages` service not properly initialized: no `messages`.', !isNone(messages));
if (!TYPES.includes(type)) {
throw new Error(`Tried to call 'userMessages.add()' with bad 'type' argument: ${type}`);
}
const message = UserMessage.create({ type, detail, target, dismissible, id: this._nextId() });
const updatedMessages = messages
.filter(byTarget(target))
.concat(message);
set(this, 'messages', updatedMessages);
},
clearAll() {
if (get(this, 'isDestroying') || get(this, 'isDestroyed')) { return; }
set(this, 'messages', []);
},
clear(where = { id: undefined, target: undefined }) {
if (get(this, 'isDestroying') || get(this, 'isDestroyed')) { return; }
const filtered = get(this, 'messages')
.filter(byTarget(where.target))
.filter(byId(where.id));
set(this, 'messages', filtered);
},
// Set up the messages as new when the service is created.
init() {
this._super(...arguments);
set(this, 'messages', []);
set(this, '_lastId', 0);
}
});
<h1>Welcome to {{appName}}</h1>
<br>
<br>
{{outlet}}
<br>
<br>
{{user-messages}}
<form>
<input oninput={{action (mut inputValue) value="target.value"}}>
<button type='submit' {{action 'addMessage' inputValue}}>Add message</button>
</form>
{{#each messages as |message|}}
{{#if (eq target message.target)}}
{{#user-message message=message}}
<div class="user-messages__message-detail" data-test-userMessageDetail>{{message.detail}}</div>
{{#if message.dismissible}}
<button class="user-messages__close-button" aria-label='Close Notification' onclick={{action 'clear' message.id}}>
<span class='user-messages__close-icon'>x</span>
</button>
{{/if}}
{{/user-message}}
{{/if}}
{{/each}}
{
"version": "0.12.1",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js",
"ember": "2.12.0",
"ember-template-compiler": "2.12.0",
"ember-testing": "2.12.0"
},
"addons": {
"ember-lodash": "4.17.0",
"ember-truth-helpers": "1.2.0",
"ember-data": "2.12.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment