Skip to content

Commit

Permalink
Add new post, tweak styling, modify old posts (#43)
Browse files Browse the repository at this point in the history
* Add code titles, update remark-image config

* Update styling

* Tweak heading margin-top

* Convert old posts to use code title

* Lint

* Guard against `window` usage

* Use browser-monads fork

* Update screenshot
  • Loading branch information
poteto committed Mar 17, 2019
1 parent be7d7b4 commit 879b7ad
Show file tree
Hide file tree
Showing 31 changed files with 367 additions and 187 deletions.
Expand Up @@ -26,9 +26,9 @@ Using [transclusion](http://en.wikipedia.org/wiki/Transclusion), we can then eas

### demo (with extra bells and whistles)

[http://gfycat.com/HarmfulQuarterlyBobwhite](http://gfycat.com/HarmfulQuarterlyBobwhite)
Code: [JSBin demo](http://jsbin.com/denep/9/edit?js,output)

[JSBin demo](http://jsbin.com/denep/9/edit?js,output)
<div style='position:relative; padding-bottom:calc(31.10% + 44px)'><iframe src='https://gfycat.com/ifr/HarmfulQuarterlyBobwhite' frameborder='0' scrolling='no' width='100%' height='100%' style='position:absolute;top:0;left:0;' allowfullscreen></iframe></div><p> <a href="https://gfycat.com/harmfulquarterlybobwhite">via Gfycat</a></p>

### draggable-dropzone

Expand Down
Expand Up @@ -20,8 +20,7 @@ Implementing Highcharts in your Ember application so that it live binds to your

#### The component and theme mixin

```js
// component
```js:title=component.js
App.HighChartsComponent = Ember.Component.extend(App.HighchartsThemeMixin, {
classNames: [ 'highcharts-wrapper' ],
content: undefined,
Expand Down Expand Up @@ -85,8 +84,7 @@ App.HighChartsComponent = Ember.Component.extend(App.HighchartsThemeMixin, {
});
```

```js
// mixin
```js:title=mixin.js
App.HighchartsThemeMixin = Ember.Mixin.create({
buildTheme: function() {
Highcharts.theme = {
Expand Down
Expand Up @@ -27,8 +27,7 @@ Source: [Unsplash](https://unsplash.com/photos/IClZBVw5W5A/) | By Todd Quackenbu

### The search macro

```js
// utils/computed/search.js
```js:title=utils/computed/search.js
import Ember from 'ember';
var computed = Ember.computed;

Expand Down
Expand Up @@ -75,8 +75,7 @@ export default Ember.Controller.extend({ ... });

That means we can also create our own controllers and other objects that can then be used as a new base class:

```js
// base.js
```js:title=base.js
export default Ember.Controller.extend({
someComputed: Ember.computed('foo', 'bar', function() {
return get(this, 'foo') + ' ' + get(this, 'bar');
Expand All @@ -86,8 +85,7 @@ export default Ember.Controller.extend({

Then:

```js
// other.js
```js:title=other.js
import BaseController from 'base';

export default BaseController.extend({
Expand All @@ -109,12 +107,4 @@ Over the next few weeks and months, I’ll be working on improving the [Ember Ge

If you have any suggestions, ideas or feedback for the new GSG, please [tweet me](https://twitter.com/sugarpirate_)! Anything, no matter how small or insignificant, will be helpful. If you like my writing, I hope I’ll be able to inject a little bit of _sugarpirate_ goodness into the GSG, and if you don’t, please let me know ☺

See you in the new year! — _Lauren_

#### Follow my Ember Collections on Medium

[`Delightful UI for Ember Apps`
_A curation of insights into building delightful user interfaces for your Ember application_medium.com](https://medium.com/delightful-ui-for-ember-apps "https://medium.com/delightful-ui-for-ember-apps")[](https://medium.com/delightful-ui-for-ember-apps)

[`The Ember Way`
_Doing things the Ember Way_medium.com](https://medium.com/the-ember-way "https://medium.com/the-ember-way")[](https://medium.com/the-ember-way)
See you in the new year! — _Lauren_
Expand Up @@ -49,9 +49,7 @@ The goal was to have an API as declarative as the following:

And the route:

```js
// foo/route.js

```js:title=foo/route.js
export default Ember.Route.extend({
breadCrumb: {
title: 'Animals'
Expand Down
Expand Up @@ -71,8 +71,7 @@ Instead, let’s continue the discussion above on how best to refactor away obse

Let’s say we have an observer in our application that needs to check a user’s birthday ([demo](http://emberjs.jsbin.com/citiwi/edit?js,output)):

```js
// birth-day/component.js
```js:title=birth-day/component.js
import Ember from 'ember';
import moment from 'moment';

Expand Down Expand Up @@ -115,8 +114,7 @@ export default Component.extend({

In the above example, we’re using an observer to set the `isBirthday` flag if it is the user’s birthday.

```handlebars
{{!birth-day/template.hbs}}
```handlebars:title=birth-day/template.hbs
{{input value=birthDate placeholder="Your birthday"}}
<p>
Expand All @@ -133,8 +131,7 @@ When you find yourself setting some value (be in on the component, record or som

In this scenario, you can simply replace the observer with a CP.

```js
// birth-day/component.js
```js:title=birth-day/component.js
import Ember from 'ember';
import moment from 'moment';

Expand Down Expand Up @@ -162,8 +159,7 @@ export default Component.extend({

We could also move the `input` logic out of this component, and use the [new component lifecycle hooks](https://github.com/emberjs/ember.js/pull/11127) to set the `isBirthday` flag on the component.

```js
// birth-day/component.js
```js:title=birth-day/component.js
import Ember from 'ember';
import moment from 'moment';

Expand Down Expand Up @@ -194,8 +190,7 @@ export default Component.extend({
});
```

```handlebars
{{!index/template.hbs}}
```handlebars:title=index/template.hbs
{{one-way-input
value=user.birthDate
update=(action (mut user.birthDate))
Expand All @@ -204,8 +199,7 @@ export default Component.extend({
{{happy-birthday today=today birthDate=user.birthDate}}
```

```handlebars
{{!birth-day/template.hbs}}
```handlebars:title=birth-day/template.hbs
<p>
You are currently {{age}} years old.
{{#if isBirthday}}
Expand All @@ -222,8 +216,7 @@ In this situation, let’s say we want to update the user record’s `isBirthday

By bringing in the [ember-one-way-input](https://github.com/dockyard/ember-one-way-input) addon, we can eliminate the coupling of the component to the user record:

```js
// birth-day/component.js
```js:title=birth-day/component.js
import Ember from 'ember';
import moment from 'moment';

Expand All @@ -249,8 +242,7 @@ export default Component.extend({
});
```

```handlebars
{{!birth-day/template.hbs}}
```handlebars:title=birth-day/template.hbs
{{one-way-input
value=birthDate
update=(action "checkBirthday")
Expand All @@ -267,8 +259,7 @@ export default Component.extend({

And then in the Controller:

```js
// index/controller.js
```js:title=index/controller.js
import Ember from 'ember';

const { Controller, set } = Ember;
Expand All @@ -288,8 +279,7 @@ export default Controller.extend({
});
```

```handlebars
{{!index/template.hbs}}
```handlebars:title=index/template.hbs
{{happy-birthday
setIsBirthday=(action "setIsBirthday")
setBirthdate=(action "setBirthdate")
Expand Down
12 changes: 4 additions & 8 deletions content/blog/2015-12-03-ember-js-goodbye-mvc/index.md
Expand Up @@ -49,8 +49,7 @@ Our little application consists of a couple of checkboxes for selecting animals.

In the route’s template, we can just render the injected service’s state using the `each` helper.

```handlebars
{{! animals/index.hbs }}
```handlebars:title=animals/index.hbs
<div class="row">
<div class="col-md-3">
<h2>Select Animals</h2>
Expand Down Expand Up @@ -89,8 +88,7 @@ In the route’s template, we can just render the injected service’s state usi

In our controller or routable component, we inject the service and define the action for handling the checked animal. The service’s bag of state is then passed into the component, keeping it as pure as possible. Although you could have just injected the service into the component, doing it this way makes things more explicit and allows the component to be decoupled from the service.

```js
// animals/controller.js
```js:title=animals/controller.js
import Ember from 'ember';

const { inject: { service }, Controller } = Ember;
Expand All @@ -114,8 +112,7 @@ export default Controller.extend({

As mentioned, the service itself is simple. We can define more complex behavior later, but the underlying persistence for its state is just a JavaScript array.

```js
// checkbox-group/service.js
```js:title=checkbox-group/service.js
import Ember from 'ember';

const { Service } = Ember;
Expand All @@ -134,8 +131,7 @@ In our component’s template, we make use of small, composable helpers. These h

The `contains` helper doesn’t ship with Ember, but the function itself is [one line of code](https://github.com/poteto/component-best-practices/blob/master/app%2Fhelpers%2Fcontains.js?ts=2). There are a bunch of useful addons that add helpers such as these to your application — for example, [ember-truth-helpers](https://www.npmjs.com/package/ember-truth-helpers) is an addon I find myself using in almost all my apps.

```handlebars
{{! checkbox-group/template.hbs }}
```handlebars:title=checkbox-group/template.hbs
{{#each group as |item|}}
<div class="checkbox">
<label for={{concat "item-" item.id}}>
Expand Down
Expand Up @@ -39,8 +39,7 @@ One of my favorite helpers in the addon is the `pipe` helper (and its closure ac
}}
```

```handlebars
{{! perform-calculation/template.hbs }}
```handlebars:title=perform-calculation/template.hbs
<button {{action (pipe add square) 2 4}}>Should be 36</button>
<button {{action (pipe subtract square) 4 2}}>Should be 4</button>
<button {{action (pipe multiply square) 5 5}}>Should be 625</button>
Expand Down
Expand Up @@ -51,8 +51,7 @@ ember install ember-route-action-helper

And to use it, you can just use `route-action` in place of `action` inside of your route’s template, like so:

```handlebars
{{! foo/template.hbs }}
```handlebars:title=foo/template.hbs
{{foo-bar click=(route-action "updateFoo" "Hello" "world")}}
```

Expand Down
Expand Up @@ -229,8 +229,7 @@ export default {
A validator like `validatePresence` is simply a function that returns a function:
```js
// validators/custom.js
```js:title=validators/custom.js
export default function validateCustom({ foo, bar } = {}) {
return (key, newValue, oldValue, changes) => {
// validation logic
Expand All @@ -241,8 +240,7 @@ export default function validateCustom({ foo, bar } = {}) {
Which is simpler to reason about compared to an OOP implementation that relies on extending base classes and holding on to state. Because validation maps are simply POJOs, composing validators is intuitive:
```js
// validations/user.js
```js:title=validations/user.js
import {
validatePresence,
validateLength
Expand All @@ -256,8 +254,7 @@ export default {
You can easily import other validations and combine them using `Ember.assign` or `Ember.merge`.
```js
// validations/adult.js
```js:title=validations/adult.js
import Ember from 'ember';
import UserValidations from './user';
import { validateNumber } from 'ember-changeset-validations/validators';
Expand Down
Expand Up @@ -65,15 +65,11 @@ The `Player` class no longer needs to know anything about the `Bag`, and also al
I recently realized that the DI pattern can also be used to great effect in Ember components. For example, let’s say you have a container or parent component that uses multiple child components:
```handlebars
<!-- templates/application.hbs -->

```handlebars:title=templates/application.hbs
{{edit-location location=location}}
```
```handlebars
<!-- components/edit-location.hbs -->

```handlebars:title=components/edit-location.hbs
{{google-map
lat=location.lat
lng=location.lng
Expand All @@ -86,9 +82,7 @@ I recently realized that the DI pattern can also be used to great effect in Embe
The parent component `edit-location`’s primary responsibility is to provide UI to edit a location. It could have actions defined on it, like so:
```js
// components/edit-location.js

```js:title=components/edit-location.js
export default Component.extend({
actions: {
setLatLng(latLng) {
Expand All @@ -110,9 +104,7 @@ In this scenario, the `edit-location` component itself shouldn’t need to conce
Using DI, we can decouple the `edit-location` component from its child components, and clean up our tests. This technique is currently only possible with [contextual components](http://emberjs.com/blog/2016/01/15/ember-2-3-released.html#toc_contextual-components) due to the use of the `component` and `hash` helpers, which were made available in [Ember 2.3.0](http://emberjs.com/blog/2016/01/15/ember-2-3-released.html).
```handlebars
<!-- application.hbs -->

```handlebars:title=application.hbs
{{edit-location
location=location
ui=(hash
Expand All @@ -124,9 +116,7 @@ Using DI, we can decouple the `edit-location` component from its child component
We’ve passed in a hash of the child components using the `hash` and `component` helpers. This effectively inverts control to the template that calls the `edit-location` form:
```handlebars
<!-- components/edit-location.hbs -->

```handlebars:title=components/edit-location.hbs
{{ui.location-map
lat=location.lat
lng=location.lng
Expand All @@ -145,9 +135,7 @@ ember install ember-test-component
Now, in our tests, we’ll need to write a little test helper (or use the addon above) to create a dummy component we can use to no-op (do nothing). Credit goes to [@runspired](https://twitter.com/runspired) for nudging me in the right direction:
```js
// tests/helpers/dummy-component.js

```js:title=tests/helpers/dummy-component.js
import Ember from 'ember';

const {
Expand Down Expand Up @@ -176,9 +164,7 @@ export function unregisterDummyComponent(context, name = 'dummy-component') {
This test helper registers a fake component in the container, making it available for us to use in our component integration test:
```js
// tests/integration/edit-location-test.js

```js:title=tests/integration/edit-location-test.js
test('it ...', function(assert) {
registerDummyComponent(this);
this.set('location', {});
Expand All @@ -198,9 +184,7 @@ test('it ...', function(assert) {
Now we can test the `edit-location` component itself without worrying about setting up child components. That said, DI still allows us to test those child components integrating with `edit-location`, in a more controlled environment:
```js
// tests/integration/edit-location-test.js

```js:title=tests/integration/edit-location-test.js
test('it updates location via the form', function(assert) {
registerDummyComponent(this);
this.set('location', {});
Expand Down

0 comments on commit 879b7ad

Please sign in to comment.