Using Vue as an Angular alternative for Ionic: The Services
Instead of centralizing all the code in a Component, it's common practice to use Services.
Services are Classes that contain domain specific code like Ionic Native Device actions, Ionic Native Storage actions, Ionic routing.
When it comes to Services, AngularJS set the bar quite high. Back in the days we had:
- Factories
- Services
- Providers
- Values
- Constants
Angular then simplified it by only offering the basic Services making life simpler for new Ionic users.
In this tutorial, we will analyze how we can use Services with Vue in an Ionic application.
We will :
- Create a simple Vanilla JavaScript Service
- See an example of an unshared Service
- Share a Service instance between two Ionic Vue Components
- Use a Singleton Service
You will have to go back to the first tutorial so we can work with the Ionic team's stack.
Just a Simple Vanilla JS Service
A Service is by nature just a Class that exists in another file.
We can create one in a vanilla-js-service.ts file:
export default class VanillaJsService {
sayHello() {
console.log("hello");
}
}
This is a very simple Service that only has a sayHello method.
We can import it in our main.ts file:
import Vue from "vue";
import VanillaJsService from "./app/vanilla-js-service";
var app = new Vue({
el: "#app",
data: function() {
return {
vanillaJsService: new VanillaJsService()
};
},
methods: {
useVanillaJsService: function() {
this.vanillaJsService.sayHello();
}
}
});
When the Ionic Vue Component is initialized, we will instantiate a new VanillaJsService instance and stock it in a vanillaJsService property.
The useVanillaJsService method will call the sayHello method from this property.
And we will call this method in the index.html file:
<div id="app">
<button @click="useVanillaJsService">
Use Vanilla JavaScript Service
</button>
</div>
Resulting in:
Let's create a more complex Service.
Unshared Service
We will create a traditional counter in a counter-service.ts file:
export default class CounterService {
counter = 0;
add() {
this.counter++;
}
show() {
console.log(this.counter);
}
}
This Service has a counter property which starts at 0.
The add method increments the counter and the show method shows the current counter's value.
We will import this Service in two Ionic Vue Components:
import CounterService from "./counter-service";
export default {
template: `<div>
<button @click="show">
Show
</button>
<button @click="add">
Add
</button>
</div>`,
data: function() {
return {
counterService: new CounterService()
};
},
methods: {
show: function() {
this.counterService.show();
},
add: function() {
this.counterService.add();
}
}
};
Both Ionic Vue Components will have a counterService property created from the CounterService Class and will delegate their actions to this property.
We import them in the main.ts file:
.
.
.
import ComponentOne from './app/component-one';
import ComponentTwo from './app/component-two';
var app = new Vue({
el: '#app',
components: {ComponentOne, ComponentTwo},
.
.
.
}
As we have seen in the previous Ionic Vue Components tutorial, we add those Components to the components field and use them in the index.html:
<div id="app">
<component-one></component-one>
<component-two></component-two>
</div>
The Service is working as it should, when we use the Component 2, the counter gets updated for this Component and the Component's 1 counter stays at 0. However, how can we share the same counter in every Ionic Vue Component?
Shared Service
In the previous example, we created a new CounterService instance in each Component.
Instead of creating a new one, we can create one and use it in both Components:
import CounterService from "./app/counter-service";
const sharedCounter = new CounterService();
const FirstComponent = {
template: `<div>
<button @click="show">
Show
</button>
<button @click="add">
Add
</button>
</div>`,
methods: {
show: function() {
sharedCounter.show();
},
add: function() {
sharedCounter.add();
}
}
};
const SecondComponent = {
template: `<div>
<button @click="show">
Show
</button>
<button @click="add">
Add
</button>
</div>`,
methods: {
show: function() {
sharedCounter.show();
},
add: function() {
sharedCounter.add();
}
}
};
A new CounterService instance is created then stored in a sharedCounter const.
We then create a First and Second Components that will use this sharedCounter Service in their methods.
All we need now is adding those Components to the components field:
var app = new Vue({
el: '#app',
components: {FirstComponent, SecondComponent},
.
.
.
})
And use them in the index.html file:
<div id="app">
<first-component></first-component>
<second-component></second-component>
</div>
We now have our counter shared between both Ionic Vue Components:
It's working, but it's quite annoying to have every Component in the same file right?
We can create a global variable using CounterService then use it wherever we want, however, by experience, global variables generally come to bite us in the a** later on.
Let's see if we can do better.
Singleton Service
This time we will use the good old Singleton pattern.
Since we are using the Ionic's stack, let's inspire ourself from one of the best TypeScript resource and create the counter-singleton.ts file:
export default class CounterSingleton {
counter = 0;
private static instance : CounterSingleton;
static getInstance() {
if (!CounterSingleton.instance) {
CounterSingleton.instance = new CounterSingleton();
}
return CounterSingleton.instance;
}
add() {
this.counter++;
}
show() {
console.log(this.counter);
}
}
Thanks to this Class, we will only have one CounterSingleton instance in our Ionic Vue application!
We only need to replace CounterService by CounterSingleton:
import CounterSingleton from "./counter-singleton";
export default {
template: `<div>
<button @click="show">
Show
</button>
<button @click="add">
Add
</button>
</div>`,
data: function() {
return {
counterService: CounterSingleton.getInstance()
};
},
methods: {
show: function() {
this.counterService.show();
},
add: function() {
this.counterService.add();
}
}
};
And here we have both ComponentOne and ComponentTwo sharing the same Service:
Conclusion
Services are used to keep the Components simple, follow the Don’t Repeat Yourself principle (DRY) or share states in the application.
Vue has been around for quite some time. If they wanted to add a service field in the Components options, they would have done it a long time ago.
AngularJS popularized the usage of Services to share states between Components, however, “recently” Redux/VueX Stores started to rise, we will see more about this in another VueX tutorial.