Skip to content

Instantly share code, notes, and snippets.

@nightire
Last active January 15, 2018 00:25
Show Gist options
  • Save nightire/f6c08aec83077230ce5bf27bee391fd1 to your computer and use it in GitHub Desktop.
Save nightire/f6c08aec83077230ce5bf27bee391fd1 to your computer and use it in GitHub Desktop.
{{service-action}} helper
import Ember from 'ember';
const { Controller } = Ember;
export default class ApplicationController extends Controller {
constructor() {
super();
this.application_name = '<code>service-action</code> helper';
}
}
import Ember from 'ember';
const { Route } = Ember;
export default class ApplicationRoute extends Route {
activate() {
super.activate(...arguments);
document.body.classList.add('standard');
}
}
<header class="container-fluid">
<h1>Demo: {{{application_name}}}</h1>
</header>
<p class="container-fluid">
<blockquote>
这个 Demo 演示了各种调用来自 <code>service</code> 的方法的姿势,并提供了一个自制的 <code>service-action</code> 作为终极解决方案:
</blockquote>
<blockquote>
<code>\{{service-action}}</code> 是一个 helper,可以帮助你直接调用来自任意 <code>service</code> 的方法,或是 <code>service.actions</code> 里面的方法。它的优点主要是:1. 彻底解决了 <code>this</code> 在方法内部的指向混乱问题,将永远指向 <code>service</code> 对象自身,并且不需要在调用时指明;2. 始终保持参数传递的顺序和一般的 <code>\{{action}}</code> helper 的行为一致,所以不会出现诸如 <code>event</code> 参数的位置变来变去的问题。
</blockquote>
<blockquote>
它的用法很简单:
</blockquote>
<blockquote>
<code>onclick=\{{service-action "service_name" "action_name" ...args}}</code>
</blockquote>
</p>
<hr>
<main class="container-fluid">
{{outlet}}
</main>
import Ember from 'ember';
const { Helper, getOwner } = Ember;
export default class ServiceActionHelper extends Helper {
compute([serviceName, actionName, ...args]) {
const service = getOwner(this).lookup(`service:${serviceName}`);
return event => {
return typeof service[actionName] === 'function'
? service[actionName].apply(service, [...args, event])
: service['actions'][actionName].apply(service, [...args, event]);
}
}
}
import Ember from 'ember';
const { Controller, inject: { service } } = Ember;
export default class IndexController extends Controller {
constructor() {
super();
this.greeting = service();
this.actions = {
hello(event, whom) {
console.log(arguments);
this.get('greeting').hello(...arguments);
},
goodbye(event, whom) {
console.log(arguments);
this.actions.goodbye(...arguments);
}
}
}
}
import Ember from 'ember';
const { Route } = Ember;
export default class IndexRoute extends Route {
}
<h2>Examples:</h2>
<ul>
<li>
<button class="btn btn-ghost" onclick={{action "hello" "nightire"}}>You need to&nbsp;<code>get</code>&nbsp;injected service first</button>
</li>
<li>
<button class="btn btn-ghost" onclick={{action "goodbye"}}>otherwise will throw an error</button>
</li>
<li>
<button class="btn btn-ghost" onclick={{action greeting.hello}}>or, you try to call it by passing a method reference, but&nbsp;<code>this</code>&nbsp;will be messed up</button>
</li>
<li>
<button class="btn btn-ghost" onclick={{action "goodbye" "nightire" target=greeting}}>...unless you specify&nbsp;<code>target</code>&nbsp;explicitly, but you can only use methods in&nbsp;<code>actions</code>&nbsp;hash</button>
</li>
<!-- unfortunatelly you can call method on target directly like below:
<li>
<button onclick={{action "hello" "nightire" target=greeting}}>...</button>
</li>
-->
<li>
<button class="btn btn-ghost" onclick={{service-action "greeting" "hello"}}>use&nbsp;<code>service-action</code>&nbsp;to call method on service directly</button>
</li>
<li>
<button class="btn btn-ghost" onclick={{service-action "greeting" "goodbye" "nightire"}}>or call method in&nbsp;<code>actions</code>&nbsp;hash</button>
</li>
</ul>
import Ember from 'ember';
const { Service } = Ember;
export default class GreetingService extends Ember.Service {
constructor() {
super();
this.everyone = 'my friend';
this.actions = {
goodbye (whom, event) {
console.log(arguments);
alert(`Goodbye, ${whom || this.everyone}!`);
}
};
}
hello(whom, event) {
console.log(arguments);
alert(`Hello, ${whom || this.everyone}!`);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-ms-overflow-style: scrollbar;
-webkit-tap-highlight-color: transparent;
}
@-ms-viewport {
width: device-width;
}
article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}
[tabindex="-1"]:focus {
outline: 0 !important;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: .5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
dfn {
font-style: italic;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 80%;
}
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -.25em;
}
sup {
top: -.5em;
}
a {
color: #007bff;
text-decoration: none;
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:hover {
color: #0056b3;
text-decoration: underline;
}
a:not([href]):not([tabindex]) {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):focus {
outline: 0;
}
pre,
code,
kbd,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
-ms-overflow-style: scrollbar;
}
figure {
margin: 0 0 1rem;
}
img {
vertical-align: middle;
border-style: none;
}
svg:not(:root) {
overflow: hidden;
}
table {
border-collapse: collapse;
}
caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
text-align: left;
caption-side: bottom;
}
th {
text-align: inherit;
}
label {
display: inline-block;
margin-bottom: .5rem;
}
button {
border-radius: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
html [type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
-webkit-appearance: listbox;
}
textarea {
overflow: auto;
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}
progress {
vertical-align: baseline;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
summary {
display: list-item;
cursor: pointer;
}
template {
display: none;
}
[hidden] {
display: none !important;
}
{
"version": "0.12.2",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"hack": "//cdn.staticfile.org/hack/0.7.7/hack.css",
"standard": "//cdn.staticfile.org/hack/0.7.7/standard.css",
"jquery": "//cdn.staticfile.org/jquery/3.2.1/jquery.min.js",
"ember": "2.16.2",
"ember-template-compiler": "2.16.2",
"ember-testing": "2.16.2"
},
"addons": {
"ember-truth-helpers": "latest"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment