This guide will cover converting your Services, Routes, Controllers and class-based Helpers to native class syntax. Each of these types of framework classes can be converted directly to native classes without many complications, so they should be some of the first classes you convert.
Services have no particular gotchas: they can simply be converted directly to native classes. Using the Native Class Codemod plus @classic
Decorator Workflow, you can safely convert any existing service progressively to a fully Octane-ready definition.
<aside> 💡 For a guide to writing new Octane-ready Services, see the Ember Guides!
</aside>
We'll start with a an example of an intentionally simple session service, which is responsible for maintaining information about the user. This has a couple features on it which our process will highlight how to
import Service from "@ember/service";
import { bool, not } from "@ember/object/computed";
const BASKET_URL = "<http://example.com/basket>";
export default Service.extend({
basket: null,
hasItems: bool("basket.items.length"),
isEmpty: not("hasItems"),
add(itemId) {
let basket = this.get("basket");
let body = basket
? JSON.stringify({
id: basket.id,
item: itemId
})
: JSON.stringify({ item: itemId });
fetch(BASKET_URL, { method: "POST", body })
.then(response => response.json())
.then(basket => {
this.set("basket", basket);
})
.catch(() => {
alert("Whoops, something went wrong! Try refreshing your browser!");
});
},
remove(itemId) {
let url = `${BASKET_URL}?removeItem=${itemId}`;
let body = JSON.stringify({ id: this.basket.id });
fetch(url, { method: "PATCH", body })
.then(basket => {
this.set("basket", basket);
})
.catch(() => {
alert("Whoops, something went wrong! Try refreshing your browser!");
});
}
});
First, we'll add the @classic
decorator to our app or add-on, by running ember install ember-classic-decorator
. (You can also do this directly with npm
or yarn
, of course!)
Then we can run the codemod on the service. In one terminal session, start your app:
$ ember serve
In another terminal session, run the codemod on your app, targeting services:
$ npx ember-native-class-codemod <http://localhost:4200> --type=services app/**/*.js
The output should look like this:
import Service from "@ember/service";
import { bool, not } from "@ember/object/computed";
import classic from "ember-classic-decorator";
const BASKET_URL = "<http://example.com/basket>";
@classic
export default class Basket extends Service {
basket = null;
@bool("basket.items.length") hasItems;
@not("hasItems") isEmpty;
init() {
super.init(...arguments);
this.set("basket", {
id: null,
items: []
});
}
add(itemId) {
let basketId = this.get("basket.id");
let body = basketId
? JSON.stringify({
id: basketId,
item: itemId
})
: JSON.stringify({ item: itemId });
fetch(BASKET_URL, { method: "POST", body })
.then(response => response.json())
.then(basket => {
this.set("basket.id", basket.id);
this.set("basket.items", basket.items);
})
.catch(() => {
alert("Whoops, something went wrong! Try refreshing your browser!");
});
}
remove(itemId) {
let url = `${BASKET_URL}?removeItem=${itemId}`;
let body = JSON.stringify({ id: this.basket.id });
fetch(url, { method: "PATCH", body })
.then(basketItems => {
this.set("basket.items", basketItems);
})
.catch(() => {
alert("Whoops, something went wrong! Try refreshing your browser!");
});
}
}
Now you have a native class for your service!