Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chriskrycho/7b010175de8eb86d8e27c621615c076f to your computer and use it in GitHub Desktop.
Save chriskrycho/7b010175de8eb86d8e27c621615c076f to your computer and use it in GitHub Desktop.
ES6 wrapped in Ember
import Component from '@ember/component';
import { set } from '@ember/object';
import { action, computed } from '@ember-decorators/object';
import { layout, classNames } from '@ember-decorators/component';
import hbs from 'htmlbars-inline-precompile';
const WORDS_PER_SEC = 200 / 60; // Assumption: 200 words per minute
export class CountingTextareaManager {
constructor(str) {
this.isDetailShown = true;
this.updateStr(str);
}
@computed('str')
get wordCount() {
return this.str.split(/\s+/g).length - 1;
}
@computed('str')
get characterCount() {
return this.str.length;
}
@computed('str')
get sentenceCount() {
return this.str === '' ? 0 : this.str.split('. ').length;
}
@computed('str')
get readingTime() {
const seconds = Math.round(this.wordCount / WORDS_PER_SEC);
const partialMinuteInSeconds = seconds % 60;
const fullMins = seconds - (partialMinuteInSeconds);
if (fullMins === 0) return `${partialMinuteInSeconds} seconds`;
return `${fullMins} minutes, ${partialMinuteInSeconds} seconds`;
}
updateStr(newStr) {
set(this, 'str', newStr);
}
}
const TEMPLATE = hbs`
<textarea
value={{manager.str}}
onInput={{action 'onStrUpdated' value='target.value'}} />
<p>
{{#if manager.isDetailShown}}
<button onClick={{action (mut manager.isDetailShown) false}}>Hide Details</button>
<div>
<div><b>Characters: </b> {{manager.charCount}} </div>
<div><b>Words: </b> {{manager.wordCount}} </div>
<div><b>Sentences: </b> {{manager.sentenceCount}} </div>
<div><b>Reading Time: </b> {{manager.readingTime}} </div>
</div>
{{else}}
<button onClick={{action (mut manager.isDetailShown) true}}>Show Details</button>
{{/if}}
</p>
<pre style='background: #eee; padding: 5px; white-space: pre-line'>{{manager.str}}</pre>`;
@layout(TEMPLATE)
@classNames('counting-textarea')
export default class CountingTextareaComponent extends Component {
init() {
super.init(...arguments);
this.manager = new CountingTextareaManager(this.text);
}
@action
onStrUpdated(newStr) {
this.manager.updateStr(newStr);
if (this.onChange) {
this.onChange(newStr);
}
}
}
import Ember from 'ember';
export default Ember.Controller.extend({
appName: 'an example of plain JS wrapped in a thin Ember layer',
message: `This is an example of how we can define most of a component's interesting behavior in an ES6 class and pure JS functions, and wrap it in a thin "Ember.Component" shell.
Why would we want to do this? The interesting part of our component is very easy to manage and unit test, as it's not coupled with Ember at all. Additionally, you can imagine that this pattern may be applied multiple times (i.e., a component has instances of many regular ES6 classes), allowing us to apply the single responsibility pattern (define simple objects and functions that are responsible for one thing only) instead of feeling like we must lump everything into Ember.Component subclasses
`
});
body {
margin: 12px 16px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 12pt;
}
.counting-textarea textarea {
width: 100%;
height: 200px;
}
<h1>Welcome to {{appName}}</h1>
<br>
<code>
From application.hbs's point of view, the string has {{message.length}} characters
</code>
<br>
{{counting-textarea
text=message
onChange=(action (mut message) )}}
{{outlet}}
<br>
<br>
{
"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.1.1",
"ember-decorators": "2.4.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment