## Sub-Project Documentation
The following open source projects have their own dedicated documentation sites with AI-optimized content:
- [Docker PHP - Documentation Index](https://serversideup.net/open-source/docker-php/llms.txt): AI-optimized documentation overview for Docker PHP.
- [Docker PHP - Full Documentation](https://serversideup.net/open-source/docker-php/llms-full.txt): Complete Docker PHP documentation.
- [Spin - Documentation Index](https://serversideup.net/open-source/spin/llms.txt): AI-optimized documentation overview for Spin.
- [Spin - Full Documentation](https://serversideup.net/open-source/spin/llms-full.txt): Complete Spin documentation.
---
# Accessing Route Parameters in Nuxt 3
Accessing route parameters is an essential for your Nuxt 3 app. Route parameters are the dynamic pieces of your URL that determine what resource or content is loaded. If you are following along in this [migration guide](https://serversideup.net/guides/upgrading-nuxt-2-to-nuxt-3/), you probably have seen them accessed in action. In the last section, we loaded [async data](https://serversideup.net/blog/using-async-data-in-nuxt-3/) from the API to display a company or cafe in ROAST. To load the specific resource, we grabbed a route parameter. Let's touch on some of what it took to migrate ROAST from Nuxt 2 to Nuxt 3 and access the route parameters.
## Step 1: Naming Pages
The first step in using route parameters is to set up your naming conventions for your pages correctly. In both Nuxt 2 and Nuxt 3, the way you name your page component reflects the name of the variable. Let's use the individual company URL which would match `https://roastandbrew.coffee/companies/{company}`.
The `{company}` would be the dynamic route parameter we will want to access. However, in order to even gain access, we have to set up our page structure correctly.
### Nuxt 2 Page Naming Conventions
There are two ways you could do this in Nuxt 2. First, would be to add a page in the pages directory named `/pages/companies/_company.vue`. Any page prefixed with an `_` would be accessible as a route parameter with the name following the `_` (I.E. `company`).
The second way you could do this with Nuxt 2 is if you had a directory that started with an `_` and you named a vue page within that directory `index.vue`. The directory would look like: `/pages/companies/_company/index.vue`. This approach is recommended if you have a page to edit a resource. You could throw an `edit.vue` file in the directory and get the following URLs: `https://roastandbrew.coffee/companies/{company}` and `https://roastandbrew.coffee/companies/{company}/edit`.
### Nuxt 3 Page Naming Conventions
To migrate these pages to Nuxt 3, the first step is updating the naming conventions. Instead of an underscore, you need to change the name to be bracketed. For example, if we had `/pages/companies/_company.vue` in Nuxt 2, we'd change the name to be `/pages/companies/[company].vue` in Nuxt 3.
The same process goes for folder naming conventions. You'd have to update `/pages/companies/_company/index.vue` to be `/pages/companies/[company]/index.vue` in Nuxt 3. Simple update, but important nonetheless.
## Step 2: Accessing Dynamic Route Parameters
Now that we have our page layouts named correctly, we can access the dynamic route parameters that we configured. This is useful when you want to load a resource by its identifier on the page. Let's take a look at what we had in Nuxt 2:
```javascript [Accessing the route parameter in Nuxt 2]
this.$route.params.company
```
In Nuxt 2, you could access the route parameters by accessing the global `$route` plugin. You'd then access the `company` variable under the `params` key.
In Nuxt 3, the same functionality looks like:
```javascript [Accessing the route parameter in Nuxt 3]
const route = useRoute();
route.params.company
```
You can access the `route` object once you load it from the `useRoute()` composable function. This must be set up in your `setup()` method or your `
```
I'm going to do a quick over view of some of the form submission, but for more information, check out: [API Driven Form Submissions with Javascript, Vuex and Laravel - Server Side Up](https://serversideup.net/blog/api-form-submissions-javascript-vuex-laravel/).
We need to add a quick template that allow the user to edit their fields. I added the following:
```vue [Manage profile page]
```
This template binds inputs to the appropriate data model for the field we are editing. We also have a quick loading state to show while the profile is loading and a notification when the profile has been updated successfully.
For our data we will add the fields we are editing:
```javascript [Data fields we are managing]
data(){
return {
favorite_coffee: '',
flavor_notes: '',
profile_visibility: 0,
city: '',
state: ''
}
},
```
There will be a few pieces of computed data we will watch from the Vuex state:
```javascript [Computed data we are watching]
watch: {
'userLoadStatus': function(){
if( this.userLoadStatus == 2 ){
this.setFields();
}
},
'userUpdateStatus': function(){
if( this.userUpdateStatus == 2 ){
$("#profile-updated-successfully").show().delay(5000).fadeOut();
}
}
},
```
The `userLoadStatus` we watch so we can set the data to what the user is when they have been loaded. The `userUpdateStatus` is watched so we determine when to display the profile updated notification.
We also check, when created if the `userLoadStatus` is equal to `2` if it is, then we set the fields because we have enough data:
```javascript [Validate the data is loaded]
created(){
if( this.userLoadStatus == 2 ){
this.setFields();
}
},
```
For our computed methods, we grab the `user` the `userLoadStatus` and the `userUpdateStatus`:
```javascript [Computed data for our page]
computed: {
/*
Gets the authenticated user.
*/
user(){
return this.$store.getters.getUser;
},
/*
Gets the user load status.
*/
userLoadStatus(){
return this.$store.getters.getUserLoadStatus();
},
/*
Gets the user update status
*/
userUpdateStatus(){
return this.$store.getters.getUserUpdateStatus;
}
},
```
These will help us display the information we need to on the page.
For methods, we have the `setFields()` method which sets the data to what the user already has in their profile:
```javascript [Set the fields loaded from the API]
setFields(){
this.profile_visibility = this.user.profile_visibility;
this.favorite_coffee = this.user.favorite_coffee;
this.flavor_notes = this.user.flavor_notes;
this.city = this.user.city;
this.state = this.user.state;
},
```
We also have an `updateProfile()` method which validates the information we need in our profile and dispatches the `editUser` action, sending all of our data through the API to be saved for the profile:
```javascript [Method to update profile]
updateProfile(){
if( this.validateProfile() ){
this.$store.dispatch( 'editUser', {
profile_visibility: this.profile_visibility,
favorite_coffee: this.favorite_coffee,
flavor_notes: this.flavor_notes,
city: this.city,
state: this.state
});
}
},
```
The `validateProfile()` method for now returns `true` because there isn't anything we need to specifically validate. It's great to have in place for future validations to be added.
Next, we just have to add a link to the navigation for this page and we have a user profile!
## Step 8: Add Link To Profile Page
Now that we have the profile page made, we need to add a link to the page. All we have to do is open up `/resources/assets/js/components/global/Navigation.vue` and add the following code below our avatar:
```html [Link to the profile page with Vue Router]
Profile
```
This will add a link to the navigation to the page to edit our profile!
## Conclusion
We now have user profiles added to collect some information from our users and expand up to adding friends and other possibilities. Be sure to check out all of the code here: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""}
Of course, reach out if you have any questions and sign up for our mailing list if you are interested in more about API Driven Development: [Server Side Up General List](https://serversideup.net/subscribe/)
# Adding server configurations within your Laravel App
Is it a good idea to put your server configurations within Laravel? In this video, we cover the benefits and challenges of putting server configurations within your Laravel application using Docker.
# Adopt a Ubiquiti USG to a UniFi Cloud Controller & automate device deployments
Learn how to adopt a UniFi Security Gateway to a UniFi Cloud Controller. We'll also set up our UniFi network to automatically deploy other UniFi devices with our network settings.
See the entire "Complete Ubiquiti UniFi + Synology Network Build" course here: {rel=""nofollow""}
# Advanced Data Fetching with Nuxt 3
Working with ROAST and Bugflow, both having a Nuxt 3 frontend, I've come across a lot of scenarios where I've had to do some more advanced data fetching with Nuxt 3 and the provided composables. I've written a basic article about [using asyncData() in Nuxt 3](https://serversideup.net/blog/using-async-data-in-nuxt-3/). This article will be extending on the previous article and covering some more advanced scenarios like automatic watch sources, infinite scrolling, pagination and tips for dealing with multiple async data sources. Let's get started!
## Nuxt 3 Watch Sources with `useAsyncData()`
To be honest, this is my favorite feature of any of the new Nuxt 3 data fetching composables. Watch sources works with `useFetch()` and `useAsyncData()` along with their `lazy` counterparts. Let's set up a use case so we can explain how wild this is.
Say you have a page that has a variety of filters, settings, parameters, etc used to query an API. In ROAST this would be like the [search page](https://roastandbrew.coffee/search){rel=""nofollow""} or in [Bugflow](https://bugflow.io){rel=""nofollow""}, our bug listing page. These pages allow the user to set their parameters, filters, etc. and call the API to get data that matches what they are looking for. Every time the user updates one of these filters, you will have to re-query the API. Normally, this would be done by calling a function, or building a reactive query string.
However, with watch sources, this is much easier! [Watch sources](https://nuxt.com/docs/api/composables/use-async-data#params){rel=""nofollow""} allow you to "watch reactive sources to auto-refresh". What does that mean? That means when the user changes a parameter you used in your query, the data auto refreshes. It's amazing! Let's look at the following code:
```vue [Example of watch sources]
```
Before we start breaking this apart, I'm using `useAsyncData()` but watch sources work with all the new data fetching composables.
In this example we are loading the first set of cafes from the {rel=""nofollow""} endpoint. This is a paginated resource and we can search cafes to get the find the ones we are looking for. So we set up the following filters:
```javascript [Filter set up]
const search = ref('');
const page = ref(1);
```
Next, we set up our data fetching request and pass these two parameters in the `params` section:
```javascript [Adding filters to the watch data]
const { data: cafes, error } = await useAsyncData(
'cafes',
() => $fetch( `/api/v1/cafes`, {
// ... other options
params: {
page: page.value,
search: search.value,
}
} ), {
watch: [
page,
search
]
}
);
```
Looks pretty familiar so far! However, the magic comes in the third parameter to the `useAsyncData()` composable and that's the `watch` key. What this does is allows us to pass an array of reactive sources that will re-query when changed.
Let's start with `page` where we want to add simple next and previous pagination. When the user increments or decrements the `page` value, we want to refresh the data source with the next paginated set of data. Instead of calling `refresh` (a method in the composable) or dynamically computing the query string, we can automatically load the new data instantly in Nuxt 3 when the watch source changes. Add the following methods:
```vue [Pagination example]
```
Notice how these methods don't explicitly call a refresh or another method to reload the data? That's because `page` is one of the watch sources defined. All you have to do is increment or decrement the `page` value. This will automatically refresh the data! Super convenient for dynamic data fetching with Nuxt 3 and implementing searches, pagination, or other filters.
The `location` variable works the same way. Once it changes, a new request to load the data will be made. However, you will probably want to debounce the input if it's a text search or you will send way too many API requests and blow through the throttling!
Here's our final code example:
```vue [Full example of UI and Watch Sources]
```
I really love how the watch sources clean up the code base and make the experience feel so much more optimized and dynamic. Let's touch on another advanced data fetching scenario, infinite scrolling, or "compounding/appending" requests.
## Append Data from `$fetch` in Nuxt 3
Specifically in Bugflow, we ran into a scenario where we wanted a "compounding" or "infinite scroll" type scenario. We had a bug listing screen where the user can see all bugs on a project, newest to oldest. As they scrolled, they had the option to load more.
In this scenario we needed to keep appending the data returned from a data fetch with Nuxt 3 in order to show it all on one screen. For this scenario, I recommend using the `$fetch` method that's globally available to directly call the API. Why not a composable? You can, but the composables provided want to replace the data every request. That's how they are designed. We want to append the data. Take a look at the code:
```javascript [Example with appending data (Infinite Scroll)]
const page = ref(1);
const lastPage = ref(1);
const companies = ref([]);
const pending = ref(false);
onMounted(() => {
loadCompanies();
})
const loadMore = () => {
if( page.value + 1 <= lastPage.value ){
page.value = page.value + 1;
loadCompanies();
}
}
const loadCompanies = () => {
pending.value = true;
$fetch(`/api/v1/companies`, {
method: 'GET',
baseURL: 'https://api.roastandbrew.coffee',
params: {
page: page.value
}
}).then( function( companies ){
appendCompanies( companies.data );
pending.value = false;
lastPage.value = companies.last_page;
});
}
const appendCompanies = ( newCompanies ) => {
newCompanies.forEach( ( company ) => {
companies.value.push( company );
});
}
```
As you can see, the code is a little bit more verbose than the elegant way you'd typically load data with `useFetch()` or `useAsyncData()`. However, the power is there. Let's start at the top:
```javascript [Variable set up for infinite scroll]
const page = ref(1);
const lastPage = ref(1);
const companies = ref([]);
const pending = ref(false);
```
Right away we declare 4 variables, `page`, `lastPage`, `companies`, and `pending`. Since we are loading a paginated resource, we keep track of the `page` (current page we are on) and the `lastPage`(the final page of results for the resource). We also implement our own simple `pending` state while more data loads. If you were using the `useAsyncData()` composable, this would already be available for you.
Let's jump to our `loadCompanies()` method:
```javascript [Load companies method]
const loadCompanies = () => {
pending.value = true;
$fetch(`/api/v1/companies`, {
method: 'GET',
baseURL: 'https://api.roastandbrew.coffee',
params: {
page: page.value
}
}).then( function( companies ){
appendCompanies( companies.data );
pending.value = false;
lastPage.value = companies.last_page;
});
}
```
What this does is first, set the `pending` value to `true`. This allows us to display a loader or handle other events to show the user the data is loading.
Next we call `$fetch` on our endpoint and pass the `page` param. This will grab the current paginated chunk of companies from our API. Most importantly, we listen to the successful return of the promise with `.then()`callback.
```javascript [Callback to append the loaded data]
.then( function( companies ){
appendCompanies( companies.data );
pending.value = false;
lastPage.value = companies.last_page;
});
```
Upon success, we take the response, append the new companies to the local reactive companies array, set `pending` to `false`, and save the last page so we know when to not load any more. The `appendCompanies()` method is the guts of our "compounding" or "infinite scrolling" takes place:
```javascript [Functionality to append companies]
const appendCompanies = ( newCompanies ) => {
newCompanies.forEach( ( company ) => {
companies.value.push( company );
});
}
```
This simple method takes the new companies, iterates over them, and appends them to the local `companies` array which is reactive. We can then display the reactive companies in our template like:
```html [UI to display all companies]
Companies
```
Finally, we have our `loadMore()` method. This method simply increments our page number and calls the `loadCompanies()` method. Unlike in the first section, using a watch source, we aren't using a composable so we have to call the method ourselves. The `loadMore()` method looks like:
```javascript [Load more method]
const loadMore = () => {
if( page.value + 1 <= lastPage.value ){
page.value = page.value + 1;
loadCompanies();
}
}
```
For the sake of thoroughness, I also initially call the `loadCompanies()` method with the `onMounted()` hook. You don't have to if you want to pre-populate your page on the server side.
Our final implementation should look like:
```vue [Final implementation of the infinite scroll]
Companies
Loading...
```
You can implement this in a component or in a page itself. We implemented it in a table listing on Bugflow. The user initially sees the newest bugs, but can view more as they scroll down.
I've also mentioned "infinite" scrolling, but as you can see in the template, I have a button that calls the `loadMore()` method. However, there is a simple VueUse method where you can check if an element, such as an "end of list" element, is visible and then call `loadMore()`. And just like that you have infinite scrolling! Check out [useElementVisibility(](https://vueuse.org/core/useelementvisibility/){rel=""nofollow""}) for more info.
## Helpful Nuxt 3 Data Fetching Hints
Here are a few hints that can help you when you make more advanced data fetching requests with Nuxt 3.
### Renaming the Refresh Method in Nuxt 3
As your app grows, you will no doubt hit a time where you will have to do multiple `asyncData()` requests on the same page. Let's look at loading a few companies and cafes on the same page from the ROAST API:
```vue [Standard refresh method]
```
Note: You can also do simultaneous `asyncData()` requests like [I went through here](https://serversideup.net/blog/using-async-data-in-nuxt-3/). Let's just use the above code as an example for now. It will work great right away. But what if you need to actually call the `refresh()` method provided by the composable for each data source. When destructuring `refresh()` from the composable, you will have two methods with the same name. This will not work!
To rename the destructured `refresh()` method, simply destructure each as follows:
```vue [Renamed refresh method]
```
Now you can call `refreshCafes()` and `refreshCompanies()` when you need to!
### When to use `refresh()` vs a Watch Source?
The simple answer, use `refresh()` when you know data on the server side has changed you need to reload the data on the client side. Use a watch source when the user changes parameters that need to be sent to the server.
For example, let's say you have a list of companies. A user deletes a company. This will not change a query parameter, but the data on the server side will change. Run `refresh()` and you will have accurate data.
If you want to filter results from the API via a search parameter, set that search parameter as a watch source. When a user changes their query, fresh and accurate data will be re-loaded from the API. Quick note, probably want to use a debounce method from VueUse so you don't query the API on every keystroke or you will hit a throttle limit in no time!
## Conclusion
Hope this helps with your advanced data fetching in Nuxt 3! If you have any questions, feel free to hit me up on [Twitter](https://twitter.com/danpastori){rel=""nofollow""} or in our [Discord](https://serversideup.net/discord/)!
If you want to see how these pieces fit into the scope of an entire application, [we have a book available](https://serversideup.net/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). With the purchase of the complete package you can see the entire source code behind [ROAST](https://roastandbrew.coffee/){rel=""nofollow""}.
# Advanced Meilisearch Queries with Laravel Scout
We've touched on a few configurations we can update with Meilisearch to make it more user friendly and powerful. A few that we worked on were setting [sortable](https://serversideup.net/blog/sorting-meilisearch-results-with-laravel-scout-and-eloquent/) and [filterable](https://serversideup.net/blog/filtering-meilisearch-search-results-with-laravel-scout/) attributes on your models. Let's open the gates and show how to use all of the power features of Meilisearch with Laravel Scout!
These advanced queries give you full access to the power that Meilisearch provides. You can fine tune your app and make amazing search experiences for your users. Since these are extremely custom to your application, I'll simply show you how to build these advanced queries and you can run wild!
## Prerequisite
I'm assuming you have Laravel Scout installed and connected to a Meilisearch instance. Other than that, you are ready to go!
## Why use these advanced queries?
When optimizing data for filtering, searching, and sorting, the more optimization the better. Meilisearch provides advanced filtering (when configured, see Filtering with Meilisearch and Laravel Scout), geo queries, etc. Laravel Scout supports some of this fluently, but when you really want to optimize, you will have to interact directly with Meilisearch.
One aspect I absolutely love about Laravel is that you have the capability to add these customizations. Yea, it's complicated to understand what you need to achieve sometimes, but Laravel allows you to do that with ease.
## Designing your Advanced Query with Meilisearch
Let's say you have a massive customer database with millions of records that's searchable, filterable, order-able, etc. You really want to give the power to the end user to search this data and apply all sorts of filters. Some of these filters could be wild, such as "distance from your headquarters to the customer location" or "customers between the ages of 22 and 43 and have been added in the last 10 days". You want to relate these to an Eloquent model and use Scout natively, so how do you do it?
Well, you can pass a call back function to the `search()` method inherited on the model by the `Searchable` trait. Sound like a lot? Let's check it out:
```php [Adding customized filters to Meilisearch Query]
$filters = Request::get('filters');
$customers = Customer::search(
$term,
function( Indexes $meiliSearch, string $query, array $options ) use ( $filters ){
$options['sort'] = [$filters['sort'].':'.$filters['sort_direction']];
$options['filter'] = 'created_at > '.$filter['created_after'].' AND company_id = "'.$filter['company_id'].'"';
return $meiliSearch->search( $query, $options );
}
)->get();
```
Make sure that you add `use MeiliSearch\Endpoints\Indexes;` to the top of your class. This way you can continue to use the advanced features of Meilisearch!
### Breaking Down The Advanced Meilisearch Callback
Let's break this down. First, we grab all of the filters configured by the user through the request:
```php [Grab the filters from the request]
$filters = Request::get('filters');
```
Next, we start to build the customer query. Right away, it should look relatively familiar. You call the `search` method on the `Customer` model and pass it a search term. However, the call back function is where things level up a little bit. Let's break that down by itself.
The first parameter of the call back function is `Indexes $meilisearch`. This is the actual connection interface to the Meilisearch instance. Here you have direct access through the PHP library to Meilisearch itself. Kind of the key ingredient to doing advanced queries.
The second parameter is the `string $query` . The `$query` parameter contains the string the user wants to query (essentially the `$term` variable). You will then pass this along with your overrides to Meilisearch.
Finally, you have the `array $options` parameter. This parameter is what contains the options you can pass to Meilisearch to perform advanced queries. Pretty much anything in the [advanced section](https://docs.meilisearch.com/learn/advanced/filtering_and_faceted_search.html){rel=""nofollow""} of the Meilisearch docs can be used here!
If you look inside the callback function's body, you see that we set the `sort` key and the `filter` key on the `$options` array. This is where we can construct our complex queries and dynamic searches. Just make sure you have your fields configured to be [sortable](https://serversideup.net/blog/sorting-meilisearch-results-with-laravel-scout-and-eloquent/) and [filterable](https://serversideup.net/blog/filtering-meilisearch-search-results-with-laravel-scout/) within Meilisearch! One thing to note, is the sort key expects an array even if you are just sorting by one attribute. It should look like:
```php [Apply sort filter to Meilisearch]
$options['sort'] = [$filters['sort'].':'.$filters['sort_direction']];
```
Since we are overriding query and injecting our own sorting, advanced filters, etc. we need to return the Meilisearch index and call the `->search()` method. In this method, we need to pass in the `$query` and the `$options` variables. This would be the same if you were using the Meilisearch PHP package itself, outside of Laravel Scout. However, with this approach, we can have the best of both worlds. The power of the PHP package and the fluent approach of Laravel Scout binding to our Eloquent models.
Before we move to the next section, we add a `use` statement to the end of the function to ensure our request variables can be accessed from within the callback function. Just a heads up!
### What else can you do?
Within this advanced function, you can do complex filters, geo filters, faceted searches, etc. Here are the links to the documentation in Meilisearch for some inspiration:
- [Filters & Faceted Search](https://docs.meilisearch.com/learn/advanced/filtering_and_faceted_search.html){rel=""nofollow""}
- [Geo Searching](https://docs.meilisearch.com/learn/advanced/geosearch.html){rel=""nofollow""}
- [Sorting](https://docs.meilisearch.com/learn/advanced/sorting.html){rel=""nofollow""}
A lot of features not supported fluently through Eloquent's query builder, but can be used none the less! This power can really help to ensure your users have an excellent search experience.
## Loading Relationships After Searching
One of the other pieces of of functionality I'd like to touch on and give an example of is how to load relationships after a search has been made. This happens after you search your Meilisearch instance, similar to if you are doing an Eloquent query. It's [documented well in the Laravel docs](https://laravel.com/docs/9.x/scout#customizing-the-eloquent-results-query){rel=""nofollow""}, but it took me awhile to grasp it. So maybe another approach will help you as well.
Normally, in Eloquent if you'd like to load a relationship you'd add a `->with('invoices')` or something similar to the query builder. You'd have something like this:
```php [Eloquent example using with()]
$customers = Customer::where('name', 'LIKE', 'Dan%')->with('invoices')->get();
```
However, when building a search request to Meilisearch, this won't work. You have to run a `->query()` method and pass it a callback function. As the documentation states, "this callback is invoked after the relevant models have already been retrieved from your application's search engine, the `query`method should not be used for "filtering" results." What that means is don't put a `where` in this function, only use it for adding relationships.
So let's take our simple Eloquent query above and convert it to query Meilisearch instead:
```php [Adding with() to a Meilisearch query]
$customers = Customer::search('Dan')
->query( function( $query ){
$query->with('invoices');
} )->get();
```
Now we will search Meilisearch, get all of the results that match "Dan" and load any invoices the customer may have associated with them.
## Conclusion
By using some of these advanced features of Meilisearch combined with the fluent nature of Laravel Scout, you can create a super powerful search engine. Your user's experience will benefit dramatically! You can have the best of both worlds with a high speed, full text search powered by Meilisearch. And you can use the beauty of an Eloquent query to relate and return data.
Implementing Meilisearch is beneficial in any application, especially with a high availability API. If you want to learn more about building API Driven Applications, [check out our book](https://serversideup.net/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). We are updating it for Laravel 9 and Vue 3!
If you have any questions, feel free to reach out on [Twitter](https://twitter.com/danpastori){rel=""nofollow""} or in our [community forum](https://community.serversideup.net){rel=""nofollow""}!
# Advanced Vuex 4 Tips
So we covered the basics in "[Beginning Vuex 4 with Vue 3](https://serversideup.net/blog/beginning-vuex-4-with-vue-3/)", but if you are building a larger app, you might have already run into some issues. Or maybe you started thinking, there has to be a better way? When you start building state in larger applications, complex forms, or individual pages that could be an app themselves, you will end up wanting to store more and more in the state. There are a ton of advanced Vuex 4 practices that can help make this process even easier. Now that we have gotten our feet wet with Vuex, let's jump into some more of the advanced Vuex 4 use cases and some other awesome features of the state management system!
## Map Helpers
The map helpers are some of my favorite features of Vuex. This allows you to essentially short hand access the 4 features of your modules (state, getters, mutations, and actions). Each piece has their own `map` helper. For example, `state` has the `mapState` helper method that you can import. These helpers make managing state easy to do in any component.
### mapState
Let's start with the `mapState` helper. To add the `mapState` helper to your component, add the following to your imports:
```javascript [Import mapState helper]
import { mapState } from 'vuex';
```
You now have the `mapState` method available to use within your component. To use this helper method you have to add it to your `computed` properties like this:
```vue [Add computed properties]
```
You now have access directly to your `title` within your component and can access it in the template or in your methods using `this.title`. You will be able to watch for changes locally and you will even be able to use these in your `v-model` assignment. There are a few gotchas with that, so there's a whole different section.
### mapGetters and mapMutations
To be honest, I don't use these helpers often since I tend to use `mapState` or treat the piece of state as a model. These two helpers are good to know exist though since any of these map helper functions can come in handy.
There are two things to note if you are using either of these helpers. First, you have to include them like `mapState` on top of the component:
```javascript [Import mapGetters helper]
import { mapGetters } from 'vuex'
```
and
```javascript [Import mapMutations helper]
import { mapMutations } from 'vuex'
```
The next thing to note is if you are using `mapGetters` you use it inside of your `computed` property:
```vue [Add mapGetters to computed property]
```
You also pass an array with the names of the getters you wish to use locally. Then you can call `this.getUser` to access the user.
With the `mapMutations` , since these are functions, they map inside of the `methods` property:
```vue [Bring in the mutations to the component]
```
You can then call the method to set the user like this: `this.setUser( user )` and it will perform the mutation to update the user in the store.
When we run into using the state as a `v-model` you will see why we don't usually use these helpers much. If you want more information, the Vuex documentation on [mapGetters](https://next.vuex.vuejs.org/guide/getters.html#the-mapgetters-helper){rel=""nofollow""} and [mapMutations](https://next.vuex.vuejs.org/guide/mutations.html#committing-mutations-in-components){rel=""nofollow""} provides a lot more information.
### mapActions
To be honest, I use actions sparingly with my Vuex modules. Reason being is that when you map state locally in a component, I usually perform the methods I need within the component instead of the action. With that being said if I need to perform the same method multiple times from multiple components, I will create an action and then map the action locally. In the [Beginning Vuex 4 with Vue 3](https://serversideup.net/blog/beginning-vuex-4-with-vue-3/) I mentioned this with a `next()` method on a playlist. This could be called anywhere from multiple components so creating an action would be helpful.
Instead of creating a `next()` method locally in each component and calling `this.next()`:
```javascript [Next method with dispatching to Vuex]
export default {
methods: {
next(){
this.$store.dispatch('nextSong');
}
}
}
```
We can use `mapActions` helper to do that for us. First, we can import the `mapActions` helper:
```javascript [Import the mapActions helper]
import { mapActions } from 'vuex'
```
Next, we can add it to our `methods`:
```vue [Bring in the next method from the Vuex actions]
```
Now we can call `this.next()` as much as we want within our components and we don't have to repeat code!
### Using Multiple Map Helpers
We will touch on namespacing in a few sections, but one feature I'd like to touch on before we leave our discussion of map helpers is mapping multiple pieces of state in a component. Say you have a piece of state that is a user and some settings that you want to map in a single component.
```vue [Multiple map helpers]
```
You now can access `this.user` and `this.title` and they both map to their appropriate state. I've definitely run into multiple scenarios where I've had to map multiple pieces of state in a component. Especially when working with a large form where pieces are namespaced. The process is similar for all map helpers, so `actions`, `getters` and `mutations` are the same.
Before we get into namespacing and addressing the issues such as possible naming conflicts, let's touch on using Vuex state as a `v-model`.
## Using Vuex State as a v-model
So now we know we have all of these amazing helpers available for our use, let's go one step further and use the mapped state as a `v-model` on a form input.
Quick tangent. I've built some massive forms that require tons of dynamic computation even before persisting to the database (ie. Entering an address and when every field is validated, grabbing latitude and longitude from Google). When creating these massive forms I ended up with a page or a component that was well over 2000 lines of code. This is unmaintainable.
I then divided the form into small, maintainable components. These Vue components were not meant to be re-usable but scoped simply on the form page. That brought us to the next issue. Passing all of the properties for the form down to each component and watching for changes. This became a headache instantly. Luckily, Vuex state is the perfect tool for the job. When dealing with a large form, I actually make a piece of state that is monitored by all the shared components that make up the form. This way I can easily pass data between the components, have small maintainable form pieces, and handle massively complex computations.
With that being said, using your mapped state as a `v-model` is extremely important.
So with mapped state, you can easily reference the state within components. Maybe this for display or to use in calculation. But there are also times where you want to harness state reactively through a `v-model`.
Let's say you have a piece of state that's a `title` and you want to bind it to an input field:
```html [Set up v-model on input]
```
This title is shared across multiple components through state. To use this as a `v-model` you will have to set up your `computed` property on the component to look like:
```vue [Commit and get data from Vuex for our v-model]
```
What you are doing is explicitly defining a two-way binding that allows you to load the title through a `getter` and also call the `setTitle` mutation when the value updates. You can now use your Vuex state within your `v-model` and most importantly, spread out massive forms across multiple components!
## Namespacing Vuex Modules
Namespacing Vuex modules is extremely important as your app grows. What namespacing allows you to do is neatly nest modules and access properties within the scope of a name. When you get into larger apps, you could have duplicate state names which would conflict and cause a nightmare to maintain.
For example, say you are managing media and you have a piece of state named `title` and a mutation called `setTitle`. Is this title for a movie, book, or song? You can't really decipher that. You also don't want to have a million pieces of top level state where you have getters like `getBookTitle()` and `getVideoTitle()` . Your module will become overwhelmingly large.
Namespacing solves this problem. To namespace a Vuex module you have to add the `namespaced` option set to `true` to the top of your module:
```javascript [Example of a namespaced Vuex module]
export const book = {
namespaced: true,
state: () => ({
title: ''
}),
mutations: {
setTitle( state, text ){
state.title = text;
}
},
getters: {
getTitle( state ){
return state.title;
}
}
}
```
Now, if you have two modules that are namespaced you can register them in your store:
```javascript [Two namespaced modules loaded into a Vuex store]
import { book } from './Modules/book.js';
import { movie } from './Modules/movie.js';
const store = createStore({
modules: {
book,
movie
}
});
```
When it comes time to access the title of the module you want you can call:
```javascript [Example of a getter with the book module]
this.$store.getters['book/getTitle'];
```
or
```javascript [Example of a getter with the movie module]
this.$store.getters['movie/getTitle'];
```
And get the title you want! Same goes with mutations and actions. You reference the specific feature through a namespace (similar to a file structure) reference. The namespace is the exported function `movie` is the name of the namespace.
You can even do this with the map helpers if you pass the namespace as a first parameter:
```vue [Namespaced computed variable]
```
The first argument is the namespace (in this case `book`) and then you can begin your mapping.
You can even map multiple namespaces in the same component:
```vue [Multiple namespaced modules in a component]
```
Now `this.bookTitle` is mapped to the book title state and `this.movieTitle` is mapped to the movie title state. Quick note, you can assign custom names locally and map it to state of a different name (ie. `bookTitle` ≠ `title`). This flexibility comes in handy as your app grows. In the next section we will be getting into some seriously large scale mapping and namespacing.
## Nesting Namespaced Vuex Modules
To cap this tutorial off, let's get into nested namespacing with Vuex modules. Let's say we have an ultra large app or a page that has a ton of settings. Your Vuex module is getting un-maintainable (well over 1000 lines of code... it happens). You will need to split it up. To keep structure, the first thing you want to try is namespacing. But sometimes that might not be enough on its own. You can move to nested namespaced Vuex modules!
What this means, is say you are dealing with a settings form and each setting belongs to a group. We did this with [AirStudio](https://airstudio.video){rel=""nofollow""} editor. You can nest a namespace to make your modules extremely easy to maintain.
What I mean by that is say you have a `settings` module. In your `settings` module you have settings for a `canvas` which has a background color, width, height. You also have settings for a `title` which contains a font, color, size, and weight. These are small settings that are used throughout the massive editor page. If you put them all into a single `editor` namespace, you will end up with an un-maintainable editor module that every time you want to add a setting it becomes a chore to maintain. Also assume there are other modules such as video, user, etc. that live on the same level as the `editor` module.
To make these maintainable, you divide up the module into sub modules. For example, I have an `editor` module, that has a sub module of `canvas` and `title` . Now within each of these modules, I store the localized state. Let's take a look.
First, I register my `editor` module with the store:
```javascript [Import editor module to store]
import { editor } from './Store/editor.js';
const store = createStore({
modules: {
editor
}
})
```
Notice that I didn't include the `canvas` or `title` sub modules? That's alright, those are coming soon.
Next, I create my `editor` module:
```javascript [Import sub module to editor module]
import { title } from './editor/title.js';
import { canvas } from './editor/canvas.js';
export const editor = {
namespaced: true,
modules: {
title,
canvas
}
}
```
This is where the magic begins to happen! I then have sub modules which we will show next, that are imported into my `editor` module. I also like to organize these modules in a directory based on their parent modules. In this case, I have an `editor` directory that contains the `title` and `canvas` modules.
I then namespace the `editor` module, but then I register the sub modules in the `modules` property. Now I have a nested module that keeps our code nice and clean!
So our `title` module can look like this:
```javascript [Title module]
export const title = {
namespaced: true,
state: () => ({
font: '',
color: '',
size: '',
weight: ''
}),
mutations: {
setFont( state, font ){
state.font = font;
}
},
getters: {
getFont( state ){
return state.font;
}
}
}
```
and our `canvas` module:
```javascript [Canvas Module]
export const canvas = {
namespaced: true,
state: () => ({
color: '',
width: '',
height: ''
}),
mutations: {
setColor( state, color ){
state.color = color;
}
},
getters: {
getColor( state ){
return state.color;
}
}
}
```
It's important to note the `namespaced: true` on top of all of these modules. There are so many benefits to this. First, each module is nice and tidy and easily maintained. Second, they are nested within the main `editor` module so you can scope which properties need to be used.
To map a nested module's state, simply extend the namespace to look like:
```javascript [Map nested module state]
import { mapState } from 'vuex';
export default {
computed: {
...mapState('editor/title', {
font: state => state.font
}
}
}
```
The same goes for all of the other mapped helper functions. Now you have a nice clean way to manage the state you need.
If you are not mapping state and need to dispatch or commit an action, you can access the nested and namespaced module like this:
```javascript [Dispatch changes to nested module]
this.$store.commit('editor/title/setColor', color);
```
I hope this helps shed some light on the power of Vuex and what it can do! It's extremely powerful for organizing shared data across components or pages in an SPA. If you have any questions, by all means reach out, I love discussing Vue and Vuex!
# AmplitudeJS Configuration Options
Part of what makes AmplitudeJS so powerful is all of the configuration options available. Most of these options have an associated public method that allows you to set or get the state of the option as well. When building a player, you will be initially setting these options when you first run the `Amplitude.init()` method.
All of the available options are located in the documentation here: [AmplitudeJS Configuration Options](https://serversideup.net/open-source/amplitudejs/docs/){rel=""nofollow""}. In this tutorial we will be diving into more of the abstract, maybe not so straight forward options that AmplitudeJS provides.
## Setting Playback Speed
Sometimes users want to control the speed of playback for a piece of audio. This is especially prevalent with podcasts or audio that can be very long. You might want to speed it up a bit. To do this, you can set the default playback speed for your player to be either `1.0`, `1.5`, or `2.0`. `1.0` is the default speed, `2.0` is twice as fast and `1.5` is right in the middle. To set the default speed, initialize AmplitudeJS with the following key:
```javascript [Initialize AmplitudeJS]
Amplitude.init({
songs: [],
playback_speed: 1.5
});
```
You can get the playback speed through a public method with `Amplitude.getPlaybackSpeed()` or allow the user to switch between the options with an interactive element of ``.
## Volume Increment and Decrement
AmplitudeJS allows for the user to increment and decrement the volume of their player through an interaction (touch or click) on an element. The exception being iOS of course. When you configure AmplitudeJS, you can set how much the volume should increment or decrement each time the element is clicked.
The range the volume can be at is between 1 and 100. By default every time you click a volume up element (``) or volume down element (``) the volume gets adjusted +5 or -5.
To change this value, for the respective function, configure AmplitudeJS like this:
```javascript [Set volume increment and decrement]
Amplitude.init({
songs: [],
volume_increment: 10,
volume_decrement: 15
});
```
The volume will now increment by 10 every time you increase the volume and decrement by 15 every time you decrease the volume.
## Key Bindings
This is one of the newer features of AmplitudeJS and one of the more interesting features. You can actually bind functionality to key presses on the keyboard. Any key can be bound to one of the 6 available events:
1. `play_pause`
2. `next`
3. `prev`
4. `stop`
5. `shuffle`
6. `repeat`
You will just need to know the key code of the key you want to bind to the event. The simplest way to do that is to visit: [JavaScript Event KeyCodes](http://keycode.info/){rel=""nofollow""}. You can then just press a key and find the code!
Say you want to bind `play_pause` button to the `p` key. You would initialize AmplitudeJS like this:
```javascript [Set key bindings]
Amplitude.init({
songs: [],
"bindings": {
80: 'play_pause'
},
});
```
Now whenever you press the `p` button on the page, AmplitudeJS will toggle the play pause!
The functionality is the same and you can combine events to key code in any way you want. It always goes in the same format of:
```javascript [Keybinding layout]
"bindings": {
{KEY_CODE}: {FUNCTION}
}
```
You now have an enhanced UX option for your player!
## Callbacks
When you configure AmplitudeJS, you can add an array of callbacks at certain events. These callbacks are methods that you can hook into the functionality of AmplitudeJS and run at certain events. Below is a list of the possible events to choose from:
- before\_play
- after\_play
- before\_stop
- after\_stop
- time\_update
- album\_change
- song\_change
- time\_updated
- playlist\_changed
- song\_repeated
If you need more information on what each of these callbacks does, check out the docs here: [AmplitudeJS Callbacks](https://serversideup.net/open-source/amplitudejs/docs/){rel=""nofollow""}.
Let's say you want to track how many times your users interact with a certain track. Every time they play, you can add a callback to the `before_play` hook that increments a variable (or calls a remote server to keep track).
You'd initialize your callbacks like this when you init AmplitudeJS:
```javascript [Set up callback methods]
var playCount = 0;
Amplitude.init({
"songs": [],
"callbacks": {
'before_play': function(){
playCount++;
}
}
});
```
Of course, these callback can be as complex as needed. You can also call AmplitudeJS public methods inside to get more information about the state of the player.
The callback is always configured like:
```javascript [Callback configuration]
{callback_key}: function(){
}
```
## Continue Next
This is one of the last weird configurations we will go through. It's the `continue_next` configuration. What this does is when the audio has finished, it determines if AmplitudeJS should continue to the next song or not. Sometimes, you might not want the player to automatically go to the next audio. When the song is finished, you just want the player to stop. This would be used in a scenario where you configure multiple individual players on a page where they aren't all connected. You can set the `continue_next` configuration to be `false` and when the song has ended, the player stops.
To do that, set the configuration like this:
```javascript [Continue next example]
Amplitude.init({
songs: [],
continue_next: false
});
```
So that's an over view of how to configure AmplitudeJS. Any of the configuration variables found in the documentation are handled the same way. You'd set them in the init method. If you have any questions about some of the configuration variables, feel free to head over to our [Discord channel](https://serversideup.net/discord/) and I can lend a hand!
# AmplitudeJS for Live Stream HTML5 Audio
> **Update 5-25-2017** We've made it even easier for you to style HTML5 audio elements. Amplitude 3 is now available! Download it on [GitHub](https://github.com/serversideup/amplitudejs "Amplitude.js Github"){rel=""nofollow""}. Check out the [Amplitude site for latest documentation](https://521dimensions.com/open-source/amplitudejs){rel=""nofollow""} and a to see the latest features.
A unique issue presented itself while reviewing some of the comments in the article: [/blog/style-the-html-5-audio-element/](https://serversideup.net/blog/style-the-html-5-audio-element/). One of the questions was how to use the audio tag for live streams? The issue wasn't necessarily playing the music, but getting the music to stop downloading when the user had the music paused (to save bandwidth) and to play from where the current point of the stream was at rather than starting from where the user paused the stream. With the latest release of AmplitudeJS, you can now manage live stream HTML5 audio sources and give you full control of the UI elements of the audio player.
For more background on how to use AmplitudeJS, [here is the original article](https://serversideup.net/blog/customize-html-audio-css-amplitudejs/ "AmplitudeJS").
The hardest part was disconnecting the live stream so it would stop downloading while the user had the stream paused. Thanks to {rel=""nofollow""} for providing some insight on how to disconnect from the source. I provided code to reconnect when the user wishes to play the stream again, which automatically picks up from where the stream is currently at on the server.
## Step 1: Include Amplitude.JS in the head
```html
```
Now we have all of the features to set up for a live stream
## Step 2: Set up your live stream player
**WARNING:** Live stream will not work with playlists! It also has an undefined endpoint, so having a track status and ending time would make no sense.
We need to add our audio tag:
```html [Add your audio tag]
```
We then need to add our Play/Pause like we would for a single song.
```html [Add a Play/Pause button]
```
NOTE: You can style the all of the elements like you would with a normal AmplitudeJS player
## Step 3: Edit your amplitude\_config variable
This is the most important part. You will set AmplitudeJS up to realize that it is a live stream. To do this, add the following Javascript:
```html [Initialize AmplitudeJS]
```
For more information on the AmplitudeJS config variable, see: [Config](https://serversideup.net/blog/customize-html-audio-css-amplitudejs/)
What happens in the background is when you click play, it loads up the live stream url and binds it to the audio tag. This will do nothing on the first play because the source is already defined in the audio tag. Your stream will start playing. When you click pause, this is where the magic happens. The stream is disconnected and set to null, canceling the download of the live stream and making sure when the user clicks play again, it picks up where the stream is currently, NOT where the user left off.
### Initial Play

### Pause

### Resume

This is what we have for HTML and CSS
```css [Style the player]
/* Player Styles */
#player{
width: 334px;
margin: auto;
box-shadow: 1px 5px 5px #888888;
}
#player-top{
padding: 10px;
height: 55px;
background-color: white;
}
#slash{
color: #3b3b3b;
font-size: 12px;
font-weight: bold;
text-shadow: 1px 1px #ffffff;
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
margin-top: 20px;
}
#track-info-container{
float: left;
font-size: 10px;
width: 170px;
overflow: hidden;
margin-left: 10px;
}
#time-info-container{
float: left;
margin-top: 15px;
font-size: 10px;
line-height: 19px;
}
/* Amplitude Element Styles */
#amplitude-play-pause{
width: 58px;
height: 59px;
cursor: pointer;
float: left;
}
.amplitude-paused{
background-image: url('../images/yellow-play.png');
background-repeat: no-repeat;
}
.amplitude-playing{
background-image: url('../images/yellow-pause.png');
background-repeat: no-repeat;
}
#amplitude-now-playing-artist{
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
font-size: 14px;
}
#amplitude-now-playing-title{
color: #3b3b3b;
font-size: 12px;
font-weight: bold;
text-shadow: 1px 1px #ffffff;
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
margin-top: 20px;
}
#amplitude-current-time{
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
}
#amplitude-audio-duration{
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
}
#amplitude-song-slider{
display: inline-block;
height: 10px;
border-radius: 5px;
width: 220px;
background-color: rgba(237,237,237,.8);
margin-left: 10px;
margin-top: 5px;
}
#amplitude-track-progress{
background-color: #fad52c;
height: 10px;
border-radius: 5px;
width: 0px;
}
```
```html [Full example of a styled player]
Amplitude Live
```
This is very beta functionality, so please ask questions, and leave comments for improvement in our [Discord](https://serversideup.net/discord/).
# Animista CSS Animations with VueJS Transitions
Every once in a while I run across some tools that I can't develop without. VueJS ([Vue.js](https://vuejs.org/){rel=""nofollow""}) is definitely hands down the best front end Javascript framework out there. It's easy to use, extend, and customize to what you need it to do. The front end of this tutorial series is written in VueJS and at [521 Dimensions](https://521dimensions.com){rel=""nofollow""} and we use VueJS daily in our app development process. One of the most awesome feature of Vue is their transitions. It makes animating a process a breeze!
For a good front end framework to really show it's power, it has to not only function well, but also look good. This requires knowledge of CSS and really tricky CSS such as animations. Animations add that extra UX flare to a project that make it stand out above the rest. I myself am not too great with CSS. I write it, it works, but some of the features I just feel like I'm missing something. This is especially true when building animations in CSS. That's where the tool of Animista ([Animista](http://animista.net/){rel=""nofollow""}) comes in. It's built by \[Ana Travas]\([Ana Travas (@ana108) | Twitter](https://twitter.com/ana108){rel=""nofollow""} and is a set of on-demand animations. You essentially can test CSS animations, customize them, then download the keyframes and CSS classes needed for your application. It's simply amazing!
The best features about VueJS and Animista? They work extremely well together. VueJS has awesome transitions functionality built right into the framework: [Enter/Leave & List Transitions — Vue.js](https://vuejs.org/v2/guide/transitions.html){rel=""nofollow""}. You can take this functionality and extend it easily to use animations from Animista. Let's get started!
## What We Will Build
For the sake of this tutorial, we will build a slide out menu similar to what is found on [Roast](https://roastandbrew.coffee/#/cafes){rel=""nofollow""} when the 'Filters' button is clicked. It's a great example and is very easy to implement in your own application.
## Step 1: Build Slide Out Menu Component
The first step is to build our slide out menu component. This will be a Vue Component ([Components Basics — Vue.js](https://vuejs.org/v2/guide/components.html){rel=""nofollow""} that slides out from the left side of the screen.
The initial component will look like:
```vue [Layout for our initial component]
```
First thing to note, I'm using SASS for this tutorial. You don't have to use SASS you can use straight up CSS if that is what works for your project.
In the styles, I have this as a fixed element that maxes out at 550px. I have the width set to 100% so on a mobile device, it goes full screen.
Next, I have the component shown off of a data variable named 'showFilters'. This is a boolean set to true or false. If it is true, then the component will be shown, if it is false, then the component will be hidden. We will want this to animate, and I will explain how shortly.
Lastly, I have the shown variable updated by an event. I created an event bus per this guide for Vue 2.0: [Migration from Vuex 0.6.x to 1.0 — Vue.js](https://vuejs.org/v2/guide/migration-vuex.html#Store%E2%80%99s-Event-Emitter-removed){rel=""nofollow""}. When shown or hidden, the event bus will fire and we will show or hide the filters. You can do this functionality in a variety of ways, you just need a way to toggle whether or not the filter is shown or hidden.
## Step 2: Download Slide-In-Left animation from Animista.net
If you visit [Animista](http://animista.net){rel=""nofollow""} you will see there are TONS of animations. You can animate in so many different ways. In this use case, we just want a simple slide in left animation from our menu. To download that animation, visit: [Animista Slide In Left](http://animista.net/play/entrances/slide-in/slide-in-left){rel=""nofollow""}. Here you can customize duration, delay, timing function, etc. It's so quick to use!
Once you have it customized to your heart's content, you can now download the CSS classes and keyframes. To do that, click the 'Generate Code' brackets icon in the top right of the animation window. You will see two chunks of code you need to copy.
The first one will be the class. This is highly important as it's what Vue uses to identify the animation to use. In our use case, it should look like:
```css [Slide in Left Animcation]
.slide-in-left {
-webkit-animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
}
```
The next chunk of code is equally important and is key frames for the `slide-in-left` class:
```css [Keyframes for animation]
/* ----------------------------------------------
* Generated by Animista on 2018-5-16 13:50:1
* w: http://animista.net, t: @cssanimista
* ---------------------------------------------- */
/**
* ----------------------------------------
* animation slide-in-left
* ----------------------------------------
*/
@-webkit-keyframes slide-in-left {
0% {
-webkit-transform: translateX(-1000px);
transform: translateX(-1000px);
opacity: 0;
}
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
opacity: 1;
}
}
@keyframes slide-in-left {
0% {
-webkit-transform: translateX(-1000px);
transform: translateX(-1000px);
opacity: 0;
}
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
opacity: 1;
}
}
```
There are a couple things you can do with this code.
1. You could place it all in your Vue Component. This is useful if you have an animation that is ONLY being used within that component. However, most of the time animations are re-usable.
2. If you are using standard CSS, copy both of these chunks of code to your file. You will be able to reference them as needed.
3. If you are using SASS, like in this example, I created an `animations` folder and placed the css and keyframes in a file named: `_slide-in-left.scss`. This gets compiled in my main SASS file and can be reused throughout components.
Now that you have your animations, let's make that slide out navigation work!
## Step 3: Wrap Vue Component in `` Tag
So to make sure Vue adds the proper animations and reverses animations when needed, you need to wrap your entire Vue component in a `` tag. This allows you to utilize your CSS animation on a component. Now Vue will automatically add the classes necessary to get your component to animate. This is actually a super slick feature of Vue transitions. You can animate entrances and exits and Vue will take care of it for you! For more information on transition statuses check out: [Enter/Leave & List Transitions — Vue.js](https://vuejs.org/v2/guide/transitions.html#Transition-Classes){rel=""nofollow""}.
Our component should now look like:
```vue [Animations applied to Vue Component]
```
The big thing to note, besides the entire component in a `` tag is the `name` attribute on the `` tag. This is huge. This references the class that will be used to do the animation. In this case it is set to `slide-in-left`. If you see in Step 2, the CSS class was `slide-in-left`. This is how Vue knows which transition to use. There's really only one more step and we are ready done!
## Step 4: Adjust CSS Classes for Animation to work with VueJS
This is really the meat of getting everything working together. I mentioned earlier that Vue adds custom transition statuses to the element to make sure it is firing correctly. To make use of these open the file that contains your animation and find the class. Update your class to be:
```css [Vue class updates to animations]
.slide-in-left-enter-active {
-webkit-animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
}
```
VueJS will add the `enter-active` suffix to your class when you are starting to run the transition. This gets fired when your variable that determines whether or not to show the navigation.
Next, after that class add the following class:
```css [Vue leave updates to animations]
.slide-in-left-leave-active{
-webkit-animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both reverse;
animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both reverse;
}
```
What this does is the same animation when the navigation is transitioning to hidden. Looks exactly the same EXCEPT for the `reverse` style on the property. This is so the animation runs in reverse when hidden! Slick isn't it? Now that you have these properties defined, and your component wrapped in a transition tag, you are ready to use your animation!
Our final CSS should look like:
```css [Final CSS for animation]
.slide-in-left-enter-active {
-webkit-animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
}
.slide-in-left-leave-active{
-webkit-animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both reverse;
animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both reverse;
}
/* ----------------------------------------------
* Generated by Animista on 2018-4-12 13:38:23
* w: http://animista.net, t: @cssanimista
* ---------------------------------------------- */
/**
* ----------------------------------------
* animation slide-in-left
* ----------------------------------------
*/
@-webkit-keyframes slide-in-left {
0% {
-webkit-transform: translateX(-1000px);
transform: translateX(-1000px);
opacity: 0;
}
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
opacity: 1;
}
}
@keyframes slide-in-left {
0% {
-webkit-transform: translateX(-1000px);
transform: translateX(-1000px);
opacity: 0;
}
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
opacity: 1;
}
}
```
And our Vue Component should look like:
```vue [Final Vue component with animations]
```
## Conclusion
The combination of VueJS and Animista helps make slick front end development a breeze! You can add multiple animations to a project to be used in a variety of scenarios and just map the animated/transitioning object in a transition tag that references the class name of your transition. Bam! UX transition ready to rock! Just make sure you add the corresponding exit class and suffix of `leave-active` IF that's what you want to use.
To check out how I've used a few more transitions, and this transition in Roast, visit: [Roast and Brew Github](https://github.com/serversideup/roastandbrew){rel=""nofollow""}. Of course, ask any questions in the comments below! If you want to learn more and take a deeper dive into API Driven Development, sign up for the mailing list: [API Driven Development Mailing List](https://serversideup.net/subscribe/). We will be launching exclusive intro offer soon!
# API Driven Form Submissions with Javascript, Vuex and Laravel
We are now at the point where we can begin adding some functionality to our application. In most cases, this requires some sort of data to compute or display. To do that, we need to add a way to add data to our application. Since we are building an application to help coffee enthusiasts find their next cup of coffee, we should begin by allowing *authenticated* users to submit coffee shops to our application. Whenever you are working with a form in the sense of a Single Page Application, everything has to work together from the Laravel API to the Vuex module calling the Javascript route and updating the Vuex data store. There's lots of moving parts, but if you break it down, it's not that bad.
## Step 1: Revisit What We Already Have
We already have a route set up in our API to add a cafe that requires the following parameters:
- Name
- Address
- City
- State
- Zip
These are the most basic of parameters for a cafe, we will be adding a lot more later!
We also already have a Vue Router route set up for adding a cafe which is `/cafes/new` and a template for adding a cafe in the `/resources/assets/js/pages/NewCafe.vue` file.
Since we have been prepared for this, we also have a Javascript API call set up in the `/resources/assets/js/api/cafe.js` file that accepts the 5 parameters to submit to a cafe.
We still have a few things we need to do and those are:
1. Add a form to the NewCafe.vue file that can be submitted with the new cafe.
2. Send an action request to our Cafes module that we will be submitting a new cafe.
3. Submit the new cafe through our Javascript API
4. Return either success or failure back to our front end, specifically the Vuex module.
5. Reload the cafes and update our Vuex module.
This is the basic flow for adding *ANY* content to our database with an API Driven perspective and a single page application with VueJS/Vuex.
Let's get started!
## Step 1: Add a Form to the NewCafe.vue Page
First we should open our `/resources/assets/js/pages/NewCafe.vue` file. We have nothing in this page right now, but in the template of the file, we will be adding our form.
Our form right away should be very simple and just have some empty text fields that match the data we need to collect for our new cafe:
```html [HTML Layout for our form]
```
You will notice a few things: 1. The grid layout is Zurb Foundation 6.4 XY-Grid and some of the styles are already pre-defined. To read more about the grid layout, check out the docs here: [Forms | Foundation for Sites 6 Docs](https://foundation.zurb.com/sites/docs/forms.html){rel=""nofollow""} 2. I added a page container `
` with a class of `page`. I defined this in the the `/resources/assets/sass/layouts/_page.scss` file and just gave the page a margin top of 25px so we have some breathing room. Following the 7-1 pattern, layouts would be the best place to put this component. We can also re-use this across all of our pages.
Now we need to set up our models in our page. First we need to add a `data()` function to our page component and have it return an object like this:
```javascript [Set up data() in Vue Component]
data(){
return {
}
}
```
This will return the variables we will use for our models in our form. We now need to add these variables in our data object:
```javascript [Define our form variables]
data(){
return {
name: '',
address: '',
city: '',
state: '',
zip: ''
}
}
```
Now we need to add the `v-model` parameter to each of our inputs and bind the data to one of the variables in the object being returned by our component.
What we are doing is binding the value of each of our inputs to a piece of data in our page model. Vue takes care of all of the updates when we type into an input. We can also access these variables inside of our component and do actions like pass them to actions to save to the database.
For more information visit: [Components — Vue.js](https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function){rel=""nofollow""} for more on data and for the `v-model` visit: [Components — Vue.js](https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events){rel=""nofollow""}
Our page should now look like:
```vue [Current state of our Vue Page]
```
We have a very basic form where each input is bound to a piece of data.
For fun you can use the Vue inspector in your browser and see each model update as you type. You just have to find the page component and you will see the data you added: 
Finally, we will have to add a simple submit button and create a method in our page component to handle a click. This is how we will kick off our form submission.
Right after our `zip` field row, I added:
```html [Added a submit button]
```
You will notice a component `v-on:click="submitNewCafe()"`. What this is doing is binding an event handler on our button to a method in our Vue component. We need to add the `submitNewCafe()` method to our methods object like this:
```javascript [Define our method to submit the cafe]
methods: {
submitNewCafe(){
}
}
```
Now whenever a user clicks on the `Add Cafe` button, we will call the method to submit a new cafe.
## Step 2: Send Action To Submit New Cafe
We are now ready to submit an action to our Vuex module saying "hey, we want to add this new cafe!" To do that, we will stay in our `NewCafe.vue` file and work with the `submitNewCafe()` method.
Since we are calling an action on a Vuex module, we need to dispatch an `addCafe` method which we will implement on our module in the next step. The first parameter of the store dispatch method is the name of the action being dispatched. The second parameter is a JSON object containing the data we want to send in the action. In this case, it will be the value of our inputs.
The `submitNewCafe()` method should look like:
```javascript [Method to submit our new cafe]
submitNewCafe(){
this.$store.dispatch( 'addCafe', {
name: this.name,
address: this.address,
city: this.city,
state: this.state,
zip: this.zip
});
}
```
If you are new to VueJS, the `this.` references the local component's data value. So what we are doing is dispatching an `addCafe`method that will be received by our cafes Vuex module containing all of the data from our form.
## Step 3: Handle addCafe action in cafes Vuex Module
So now we have the action being dispatched, but nothing to catch the action so that's what we are doing now.
We will need to open our `/resources/assets/js/modules/cafes.js` file to edit the module.
First, we need to add a variable to our state which will keep track of our cafe add status. We should simply add: `cafeAddStatus: 0` at the end of our state which will initialize our variable.
Our state should look like:
```javascript [State in our Vuex Module to handle submission]
/*
Defines the state being monitored for the module.
*/
state: {
cafes: [],
cafesLoadStatus: 0,
cafe: {},
cafeLoadStatus: 0,
cafeAddStatus: 0
},
```
Now we should add our method to handle the addition of the cafe. Right after we defined our `loadCafe` method, we should add the following method in the actions object:
```javascript [Vuex action to add a cafe]
/*
Adds a cafe
*/
addCafe( { commit, state, dispatch }, data ){
}
```
One thing to notice about this method's signature is that we have an extra piece in the decoupled data in the first parameter, it's `dispatch`. What this does is allow us to dispatch actions from within our Vuex module which we will be doing to re-load our Cafes after the cafe has been added. The second parameter, `data`, will be our form data object.
If you remember in the tutorial /build-api-requests-javascript/ we already added the API request in our Cafes.js API. We will use this very similar to the way we've made other API requests to load the cafes.
In our `addCafe()` method, in our Vuex module, we should add the following code:
```javascript [Full Vuex action to submit a cafe]
/*
Adds a cafe
*/
addCafe( { commit, state, dispatch }, data ){
commit( 'setCafeAddedStatus', 1 );
CafeAPI.postAddNewCafe( data.name, data.address, data.city, data.state, data.zip )
.then( function( response ){
commit( 'setCafeAddedStatus', 2 );
dispatch( 'loadCafes' );
})
.catch( function(){
commit( 'setCafeAddedStatus', 3 );
});
}
```
There are a few things to note: 1. We haven't added the `setCafeAddedStatus` mutation yet, we will do that next! 2. When successful, we will be setting the `cafeAddedStatus` to 2 which we can use to display the successful addition of the cafe. 3. When successful, we also dispatch the `loadCafes` action which will reload the cafes containing the one we just added. 4. On failure, we flag the `cafeAddStatus` to 3 which we can use to display an error.
A few more things to add our Vuex Module. We need to add the mutation `setCafeAddedStatus` at the end of our mutations object:
```javascript [Mutation that sets the status of adding a cafe]
/*
Set the cafe add status
*/
setCafeAddedStatus( state, status ){
state.cafeAddStatus = status;
}
```
This will set the `cafeAddStatus` accordingly to what we have as a state in our app.
The last thing to add is a getter to get the add state of the cafe:
```javascript [Getter to access the adding of a cafe's status]
/*
Gets the cafe add status
*/
getCafeAddStatus( state ){
return state.cafeAddStatus;
}
```
We can now use this to show data updates in our app. Our Vuex module is ready to rock.
## Step 4: Review The Laravel Process and Build App
In this tutorial: [Add Laravel API End Points - Server Side Up](https://serversideup.net/blog/add-api-end-points-laravel/) we already built the endpoint to add a cafe. which is `POST /api/v1/cafes`.We just completed the front end side that calls the appropriate route.
Upon completion, we will have a cafe in the database and this route will return the status.
Let's give it a try!
Make sure you run your `npm build dev` or `production` if you want!
So I added Ruby Coffee Roasters in Nelsonville WI. Side note on Ruby Coffee, if you haven't tried them yet, try them! Small town in central Wisconsin that does an amazing subscription service and sells locally and also in Long Beach, CA . Check em out here: [Ruby Coffee Roasters | Colorful Coffees roasted in Central Wisconsin, USA.](https://rubycoffeeroasters.com/){rel=""nofollow""}

You can see on the response back I got all of the cafes this means that our cafe was added successfully and Vuex called the action to load the cafes again.

I confirmed this by checking out our console and then navigating back to the home page to see it in the list:

This is exciting! We now have a way to add cafes to our application! And even better, everything automatically stays in sync with Vue and Laravel as it's all reactive with Vuex with minimal API requests!
## Conclusion
This is the first of many functionality tutorials. It's awesome to see the application start to come together and personally I can't wait to start using it! Any styles or little designs will be mentioned in tutorials and available to be viewed here: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""}
The next tutorial we will add a few finishing touches to our API Request and then focus on some awesome enhancements for displaying the cafes.. I'm thinking maps could be helpful!
# Append Gravatar Attribute to the Laravel Eloquent User Model
There are so many useful tricks when it comes with working with Laravel Eloquent. This is one of my favorites. It allows you to simply make a computed attribute on the User model that returns the user's Gravatar URL. Best part? It's only a few lines of code. Let's jump in!
## Step 1: Open Your User Model
First, you need to open your user model. In Laravel 8 this should be in the `app/Models` directory.
## Step 2: Append Gravatar Attribute
All you need to do now is append the attribute for the computed Gravatar URL:
```php [Eloquent model Gravatar attribute]
public function getGravatarAttribute()
{
return ''.md5( strtolower( trim( $this->email ) ) );
}
```
What this does is uses the Eloquent syntax to create an attribute. When returned, the field `gravatar` will be populated with the URL of the user's gravatar if they have one. Gravatar returns a default icon if the URL doesn't map to a user. This is great so you always have an empty state in place when it comes to implementing this in your app.
You can now reference this value from within Laravel and in Blade templates as:
```php [Access the Gravatar on the User Model]
$user->gravatar;
```
However, if you are not using Blade templates or loading your User through an API, see below.
## Step 3: (OPTIONAL) Append Gravatar Value to Array
The reason this is optional is it only matters if you are loading your User resource through an API or some serialized format. If you are using Laravel blade, you won't be interacting much with your User Model in a serialized form (JSON or Array), so you can use the attribute through:
```php [Access the Gravatar on the User Model]
$user->gravatar;
```
However, since it's a computed attribute, you will have to add this to your `$appends` array on your model. When your resource is loaded through an API, the `gravatar` field will appear in the JSON for that user. All you have to do is add the following to your User Model:
```php [Append the gravatar to all JSON models]
protected $appends = [
'gravatar'
];
```
If the `$appends` array already exists, just add `gravatar`. Now you can access `user.gravatar` when returned in JSON or if you convert your model to an array within your Laravel application.
## Conclusion
I love these computed attributes and how simple they are to add useful functionality to your app. The reason I choose to do a computed Gravatar URL instead of saving it to the database is there really isn't any need to save the URL since it's easily computed and keeps our database tidy. It also stays up-to-date if the user changes their email. If you have any thoughts or questions, feel free to reach out in the comment section below!
# Automatic Controller Assignment for UniFi DHCP Option 43 on Mikrotik Routers
Managing many network devices can be a pain. Especially if you need to simply plug a device in and repeat the steps to configure your the device. Thankfully, Ubiquiti's controllers can be automatically discovered and joined to UniFi using DHCP Option 43.
In this article, we'll go through configuring a Mikrotik Router that is running Routerboard 3.x (my version that I am using at this time of the article).
## Step 1: Configure your UniFi controller
A few things to remember before getting started:
1. Make sure your UniFi controller is always running (using a cloud key or a computer that is dedicated to serving the network with the UniFi Controller services)
2. Make sure your UniFi controller has a static IP address (this is very important). It can be a local static IP address, or a WAN static IP address. Whatever IP address it is, your devices must be able to access this IP and it cannot change
## Step 2: Configure your DHCP Server in Routerboard
After you have your controller up and running, you will want to sign into your Mikrotik router. For simplicity sake, I am using Winbox (available on [Windows \[see download at bottom of this page\]](https://mikrotik.com/download){rel=""nofollow""} or [Mac OS X](http://joshaven.com/resources/tools/winbox-for-mac/){rel=""nofollow""}).
- Once logged in, on the left sidebar go to "IP > DHCP Server".
- At the top, you will find a tab called "Options". Click the "+" sign to create a new option.
This is where things get fun...
## Step 3: Convert your IP address to hexadecimal
Don't screw this step up because it is kind of confusing. Take your static IP address (for my example, I am going to use `192.168.1.200`) and convert it to hexadecimal format using the [IP to Hex Converter](https://www.miniwebtool.com/ip-address-to-hex-converter/){rel=""nofollow""} tool.
You'll see your result below, but we need to modify it yet:

We need to take our result (in my case `0xC0A801C8`) and add `0104` after the `x` in the hexadecimal result that was created for us on the IP to Hex Converter tool.
**This will give me a final result of:** `0x0104C0A801C8`
Why `0104`? This is [how the documentation explains it](https://help.ubnt.com/hc/en-us/articles/204909754-UniFi-Device-Adoption-Methods-for-Remote-UniFi-Controllers#DHCP){rel=""nofollow""}:

Going back to the "New DHCP Option screen, configure your options with the following options:
```bash [DHCP Option Configuration]
Name: unifi
Code: 43
Value: {{ your "final result" after modifying your hexadecimal IP }}
```
It should look like this once you are complete:

## Step 4: Configure your DHCP network to use the "unifi" option
Under "IP > DHCP Server" then under the "Networks" tab, you will see a list of all your available DHCP networks. Double click on your network and assign it the `unifi` option that we just created.

## Step 5: Plug in your *factory default* device
Now all you need to do is plug in your **factory default device** and connect it to your network. If you have a device that belonged to a different network or controller, [reset the device first](https://help.ubnt.com/hc/en-us/articles/205143490-UniFi-How-to-Reset-the-UniFi-Access-Point-to-Factory-Defaults){rel=""nofollow""}. When your device is booted and connected to your network, open up your controller. You should see a new device that is awaiting adoption.

Click "Adopt" and all your network settings will be applied to that device. This method has saved me countless hours of going on site and makes device upgrades painless. It's so easy, even my grandmother can do this (literally). All you need to do from this point forward is have the device on and plugged into the network and you can manage it anywhere in the world.
If you found this article helpful or if you have any questions, drop a comment below!
# Basic GET Requests with Fetch API and VueJS
The best way to learn something or solve a problem is to break it down into the smallest pieces. While transitioning from Axios to the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch){rel=""nofollow""} and integrating it into my VueJS application, I had to start small. I actually wrote both requests side by side so I could migrate in pieces and ensure the integrity of the app. In this tutorial, we are going to be making some basic `GET` requests using Fetch and comparing those requests to Axios.
I tend to learn best when I need to solve a problem and apply new tools to do it. For this course, I'm building a budgeting app (I actually am too as a side project, if it ever gets polished up, we'll release it). This will provide a great example of the different use-cases we need to account for during our migration.
Since Fetch is a web browser API, there's no need to configure it [globally](https://serversideup.net/blog/configuring-axios-globally-with-vuejs/)! All methods are made available through the web browser. For this tutorial we are just creating a simple VueJS component. This VueJS component will call the `/api/v1/transactions` endpoint on our API through both the Fetch API and Axios so you can see how they both work.
## Step 1: Create your API Request
For loading our transactions, I made 2 methods (one with the Fetch API and the other with Axios) within the same component. I'll show both of the methods and then break down the differences.
### Fetch API GET Request
```javascript [Fetch API Example]
methods: {
loadFetch(){
fetch( 'https://api.roastandbrew.coffee/api/v1/companies' )
.then( function( response ){
if( response.status != 200 ){
throw response.status;
}else{
return response.json();
}
}.bind(this))
.then( function( data ){
this.fetchResponse = data;
}.bind(this))
.catch( function( error ){
this.fetchError = error;
}.bind(this));
}
}
```
### Axios API GET Request
```javascript [Axios API Example]
methods: {
loadAxios(){
axios.get(' https://api.roastandbrew.coffee/api/v1/companies' )
.then( function( response ){
this.axiosResponse = response.data;
}.bind(this))
.catch( function( error ){
this.axiosError = error;
}.bind(this));
}
}
```
I also had to import the library in to our component:
```javascript [Import the Axios module]
import axios from 'axios';
```
So those are our two requests! From first glance, they are extremely similar! In the next step, we will break down the differences.
## Step 2: Breaking Down the Method Signature
So these are very simple GET requests, we will get to the more advanced stuff later on, but starting here we can begin to see the differences between the two requests.
So to make the request, the method signature is very similar. Fetch API uses `fetch( END_POINT_URL )` and Axios uses `axios.get( END_POINT_URL )`. Right away you can see that Axios provides a method where you can explicitly call the HTTP Verb of `GET`. You can also do that with:
```javascript [Define the GET method on an Axios Request]
axios({
method: 'get',
url: END_POINT_URL
})
```
or using their default (which looks just like a Fetch API request):
```javascript [Use the default GET call]
axios( END_POINT_URL )
```
I prefer the `.get()` when using Axios, just because it's easy to read and very explicit.
Fetch API doesn't provide that explicit `.get()` method. Nor does it apply the `.post()` method when we get to that later. To specify more options to your request, the method allows for a second parameter which is an object of configuration. We won't touch on that now, but it will look very similar to the Axios method without the explicit `.get()`.
## Step 3: Breaking Down the Method Response
This is where the two methods differ a little bit. Both return promises which is nice and both work with `async/await`. However, the `fetch()` API returns a [Response object](https://developer.mozilla.org/en-US/docs/Web/API/Response){rel=""nofollow""} when the request completes successfully. This object has a ton of methods and settings you can work with!
Axios returns the response in an object as well and it's accessible through `response.data`. To get the data from the request with the Fetch API you can call a method on the `Response` object. In our example we called `.json()` which returns another promise.
However, let's take a step up one block of code and look at:
```javascript [Ensure we have a valid response code]
if( response.status != 200 ){
}
```
We first check to see if the response completed successfully. Within the Fetch API, the promise will be resolved even if there was a server side error. Axios allows you to catch the server side error with `.catch()`. For me, I can see this being a hard habit to break, I like the `.catch()` syntax.
There's another handy method which is `response.ok` which checks to make sure the status code is a `2XX` status code (perfect for 204 and other API type responses).
If we don't get an error in our Fetch API request, we then call `response.json()` and chain another promise to load our data:
```javascript [Access the JSON response returned from the server]
response.json().then( function( data ){
this.response = data;
}.bind(this));
```
This is called on the implemented [Body](https://developer.mozilla.org/en-US/docs/Web/API/Body){rel=""nofollow""} interface. There are a variety of other methods you can parse your response body with as well depending on your circumstance.
One thing to note about both of these functions is the `.bind(this)` at the end of them. That statement gives the methods scope to our VueJS component so we can set local variables within it. You have to bind twice since there's a child promise returned in the Fetch API when you convert the response to `json()`.
## Conclusion
So this is beginning our journey of learning the Fetch API alongside Axios. So far, in my opinion, what I like about the Fetch API is:
- No external library
- Powerful enough to give you access to the data and settings you need
What I don't like about the Fetch API is:
- Parsing the data with another promise (`response.json`) feels weird.
- Not catching server side errors with `.catch()` will be a hard habit to break.
I'm looking forward to doing some data creation with both libraries and eventually authentication (both handling oAuth Tokens and Laravel Sanctum). Also, I'm interested to see how we can make some API wrappers with Fetch API [like we did with Axios](https://serversideup.net/blog/build-an-api-wrapper-with-vuejs-axios/).
For the rest of the series, I'll be focusing on the side-by-side comparison with Axios and using them both within VueJS. Matt Netkow over at Ionic wrote a super helpful article about [switching to Fetch as well](https://ionicframework.com/blog/replacing-native-plugins-with-web-apis/){rel=""nofollow""}.
If you want to see the actual components that make these Fetch API requests, head [over to our Github repo.](https://github.com/serversideup/fetch-api-vuejs){rel=""nofollow""} The Fetch API could also be used instead of Axios if you are creating an [API driven web and mobile application](https://serversideup.net/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). Feel free to reach out if you have any more questions.
On to more advanced requests!
# Beginning Vuex 4 with Vue 3
Vuex is one of the most useful plugins in the entire VueJS ecosystem. I honestly include Vuex in every app whether it's an SPA or Monolith. Using Vuex modules allows me to divide up large, dynamic and complex pages or components into maintainable, reusable, scoped components without having to pass a million `props`.
I've written a few times about Vuex modules on Server Side Up, with [Using Vuex Modules Inside Components](https://serversideup.net/blog/using-vuex-modules-inside-components/) and [Build a Vuex Module](https://serversideup.net/blog/build-vuex-module/). Both I believe are good examples of what you can do with Vuex, I think it only scratches the surface of what Vuex is capable of. The scope of this tutorial is to introduce Vuex in a generic fashion and explain how you can fit it into your project.
Let's dive in!
## What Is Vuex?
According to the official docs on {rel=""nofollow""}, "Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion."
I like to define and look at Vuex as a single source of truth that allows you to easily share data between components and pages without having to pass every piece of data through a component's props. If you've ever run into a situation where you have a component that has a million props, tons of defaults, and you can't seem to keep it in sync, Vuex is the right option.
Before we go too far, with the introduction of the composition API, there's been some discussion on whether or not Vuex is still relevant or not. This is because the composition API makes code reusability even easier and allows you to share pieces of code through multiple components. I believe the composition API will be extremely helpful in replacing mixins and other functions, but I still love the **predictable fashion** and pattern that Vuex provides.
## When to Use a Vuex Store?
There are a wide variety of times when it makes sense to use a Vuex data store! I've touched on a few of them already, but this is how I choose.
### When `props` becomes unmaintainable
There isn't a quantitative number of props a component must have before switching to Vuex, but there's definitely a time where the component becomes difficult to maintain. If you've worked with components in the past you've probably experienced a point where there should "be a better way" to handle the data. That's when you need to use Vuex.
### Building Massively Complex Forms
This is one area where I found Vuex to be extremely helpful. I used to think it was solely for sharing state across an SPA (which it is also very good at). If you are building a form that changes a state based off of user inputs, my guess is props will start getting hairy between your components. Time to check out Vuex!
Another turning point for me was I wanted to divide my form into multiple, smaller components. These weren't necessarily re-usable, they were form specific, but my form was well over 2000 lines of code which is un-maintainable. By abstracting my form's state into separate Vuex modules, I was easily able to divide up the form into multiple components and make it much easier to maintain! We will run through an example on how to do this.
### Sharing Data Across an Entire SPA
This is the standard example of when to use Vuex and we run through this a lot in our [book](https://serversideup.net/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). Sharing data across an entire single page application is kind of the go-to for Vuex. Typically global app data would be like a user, notifications, etc. Any piece of data that needs to be present on an entire app, a state management system like Vuex makes this a breeze!
### Creating a Page With Multiple Moving Parts
Sometimes you have a page that is super complex. When designing the transaction import page for [Financial Freedom](https://github.com/serversideup/financial-freedom){rel=""nofollow""}, I used a Vuex store. This allowed me to easily share data with other VueJS components. Complicated pages are essentially tiny single page applications. I've done this a couple other times in other apps as well where I make a store for a page.
## Installing Vuex 4
To install Vuex 4, run the following command:
```bash [Install Vuex]
npm install vuex@next --save
```
At the time of this writing, Vuex 4 was on the `next` branch to give time to update to Vuex 3. Keep an eye out for when the branches are merged.
### Setting up Vuex 4 in an Vue 3
I assume you have a working Vue 3 instance and some sort of root file that gets compiled like `app.js`. Whatever that file is, open it up and add the following line first:
```javascript [Import the createStore method to start with Vuex]
import { createStore } from 'vuex'
```
This will allow us to build our store for our app. Next, we need to initialize our data store by adding the following code:
```javascript [Start defining our Vuex Store]
const store = createStore({
modules: {
}
});
```
We will talk about creating modules next!
Finally, let your Vue 3 app know to use the store by appending `.use(store)` to the end of your app definition. This can look like:
```javascript [Ensure your Vue app is set up to work with Vuex]
createApp({ }).use(store).mount(el)
```
or like in the Vuex docs:
```javascript [Another method of ensuring Vuex is set up]
const app = createApp({});
app.use(store);
```
Perfect! We now have Vuex ready to go in our Vue app!
Before we dive into Vuex modules, I want to quickly note that you don't **HAVE** to use Vuex modules and can just make a few pieces of state to map in your components. However, I've honestly never used Vuex this way. I've always had enough state to make modules. Even if it's a few pieces of state, modules really clean up the code base and allow it to grow seamlessly.
## Vuex Modules
As I mentioned, everything I do with Vuex is divided into modules. These modules consist of `actions`, `mutations`, `state` and `getters`. I touched on these with examples a little bit in "[Building a Vuex Module](https://serversideup.net/blog/build-vuex-module/)", but wanted to give a more generic approach.
Let's break down each one of these pieces from my perspective. The [Vuex docs](https://next.vuex.vuejs.org/){rel=""nofollow""} also explains these well.
### State
This is the heart of what Vuex helps with. These are the variables/pieces of data that you'd most likely be passing down as props if you didn't have Vuex. It's also the data that is shared between pages. Pieces of state are modified via `mutations` and retrieved via `getters`.
### Actions
In a Vuex module, an action is a standard function usually used to prep data before committing to state through a mutation. I've placed API requests in actions that load data and commit based on success/failure. I've also calculated data in an action before committing to state such as incrementing or keeping track of a value.
### Mutations
Mutations are simple methods that simply follow a pattern to modify a piece of state. Say you want to set an active song in a playlist. You'd "commit" a mutation that sets the active song in the playlist. This way each component or method that interacts with your state does so in the proper fashion. Actions also tend to "commit" mutations when they are finished.
### Getters
A getter is how you receive a piece of data from state. They are used very heavily in components to make a reference to data.
### Creating a Vuex Module
Let's keep track of a few songs in a playlist. To do this, I first start by creating a `store` directory wherever your `src` javascript files are. In that directory, I add a module named `playlist.js`:
```javascript [Example of a Vuex state for a playlist]
export const playlist = {
state: () => ({
songs: [],
activeIndex: 1,
activeSong: {},
status: 'paused'
}),
actions: {
nextSong( { commit, state } ){
let nextIndex = state.activeIndex + 1;
commit( 'setActiveIndex', nextIndex );
commit( 'setActiveSong', state.songs[ nextIndex ] );
}
},
mutations: {
setActiveIndex( index ){
state.activeIndex = index;
},
setActiveSong( song ){
state.activeSong = song;
}
},
getters: {
getActiveIndex( state ){
return state.activeIndex;
},
getActiveSong( state ){
return state.activeSong;
}
}
}
```
So there's a lot to break down but let's go through it. First of all, I didn't create all of the getters and mutations for the state simply because we are using this as an example, and it's all the same process.
Let's start by looking at our `state`. In our `state` we initialize a few variables. These will be tracked in our components and will be reactive.
Next up, we have our `actions`. If you take a look at the action, we are calling a standard process of `nextSong()`. Vuex injects the `context` variable which contains all of the available functions and state into the method. I decoupled what we need which is the `commit` method and the `state` in the method signature. This standardized action prepares what we need before committing to state. In this action, we find the next index number, then commit the mutation to `activeIndex` and we find the song at the new index and commit the mutation to set the `activeSong`. It's pretty slick to have a standardized way of performing state modifications.
After `actions` we have `mutations`. The mutations are simple. They simply accept what is going to be set to a piece of state and set it. However, they are very important since all state needs a process to be modified correctly.
Finally, we have `getters` . Getters allow us to retrieve values from our state store. These are used heavily in our components to gain access to the state that we have available.
Now that we have a quick overview, let's get to the fun implementation!
## How to Use Vuex
So we have our Vuex store registered, our first piece of state, let's get this implemented! The first step we need to take is register our module with our store.
To do this, find where we created the Vuex store:
```javascript [Register our Vuex Module]
const store = createStore({
modules: {
}
})
```
Modify the code to look like:
```javascript [Add our playlist module]
import { playlist } from './store/playlist.js';
const store = createStore({
modules: {
playlist
}
})
```
What we are doing is importing our playlist module and registering it with our code! Now we can perform our actions, mutations, and retrieve the values we need!
### Sharing State Between Components
Let's say we want to display the `activeSong` from our state in 2 different components in our app. To do that, let's look at an example component that provides access to our Vuex store:
```javascript [Using our Vuex module in a Vue component]
export default {
computed: {
activeSong(){
return this.$store.getters.getActiveSong();
}
}
}
```
The first thing to note is that any time you need to reference state in a component, it's in the `computed` property. You can name this property whatever you want (though I think it'd be hard to keep track if the names get crazy), it's inside of the property is what matters.
Inside of the property, we return a reference to `this.$store.getters.getActiveSong()`. Right away you can see the `this.$store` reference. We registered the `$store` globally within our VueJS app so now we have it available everywhere! Next, we call the `getters.getActiveSong()` which returns the value of the `getActiveSong()` getter that we created.
That's all we need to do! We can reference these values through their getters in any component we choose in our app! You may start to see that this could get a little hairy if you have a ton of state. I'll discuss how to handle that with `namespacing` in another tutorial.
## Dispatching an Action
The final thing we should touch on is how to dispatch an action to set our state. Let's say we have a button that fires the `next()` method in our component. Implement the method like this:
```javascript [Dispatch an action from a component]
export default {
methods: {
next(){
this.$store.dispatch('nextSong');
}
}
}
```
What this method does is simply `dispatch` an action with the string name of the action. In our case it's the `nextSong` action. Now our Vuex state will go to the next song in our `songs` array!
You can also pass data to your action for pre-processing as well. The second parameter in the action is the data being passed. Say you want to either provide an index to skip to, or go incrementally. You could have an action that looks like:
```javascript [Define a Vuex action]
actions: {
nextSong( { commit, state }, index ){
let nextIndex = null;
if( index ){
nextIndex = index;
}else{
nextIndex = state.activeIndex + 1;
}
commit( 'setActiveIndex', nextIndex );
commit( 'setActiveSong', state.songs[ nextIndex ] );
}
},
```
Now you can incorporate some passed data into the process before you commit.
### Nested Vuex State
One final thing I'd like to touch on in this tutorial is that you can nest Vuex state. I first saw this in an Axios example with [InertiaJS](https://inertiajs.com/){rel=""nofollow""} where they'd build a `form` object and submit just the `form` to the API. I thought it was brilliant! It really cleans up the axios requests and scopes the data nicely. With that being said, let's say we had a form to request a song. The data of the form spans multiple components and we store the form in the Vuex state:
```javascript [An example of nested Vuex state]
export const playlist = {
state: () => ({
form: {
name: '',
artist: '',
album: ''
}
}),
actions: {
setName( { commit }, data ){
commit( 'setName', data );
}
},
mutations: {
setName( state, name ){
state.form.name = name;
}
},
getters: {
getForm( state ){
return state.form
}
}
}
```
You can actually provide nested objects in the state! So when it comes time to submit the form to the API all you have to do is submit: `this.form` with `form` being the name of the computed property that references the state:
```javascript [Setting up your Vue component to use a Vuex store]
export default {
computed: {
form(){
return this.$store.getters.getForm();
}
},
methods: {
requestSong(){
axios.post( '/api/v1/requests', this.form );
}
}
}
```
I find this syntax really nice for scoping a form so you can easily add fields if needed without having to adjust the `axios` request.
Hopefully that helps a little bit and gives a better understanding of the power of Vuex. In the next tutorial, I'll go into some advanced Vuex methods such as mapping state, actions, using Vuex state as a v-model, namespacing and nested namespacing. Until then, let me know if you have any questions!
# Best practices for planning for UniFi Video
See the entire "Complete Ubiquiti UniFi + Synology Network Build" course here: {rel=""nofollow""}
# Browser Extension Messaging
One of the most confusing parts of developing your first browser extension is how to make each part communicate efficiently. Reading the docs doesn’t help. There’s ports and one time message calls, confusion on how to handle responses whether they are synchronous or asynchronous, and it’s super difficult to target certain parts of your extension.
That’s where the [webext-bridge](https://serversideup.net/open-source/webext-bridge/) package comes in. The `webext-bridge` package allows you to easily communicate between different parts of your extension. Every piece is handled cleanly and efficiently. Most importantly, you can target where you are sending the message so you don’t have check if the part of the extension receiving the message should be receiving the message.
Let’s run though how to use the `webext-bridge` package.
## Installing the `webext-bridge` package
First, we will need to add the `webext-bridge` package to our browser extension. To do that, run the following command:
```bash [Install the webext-bridge package]
yarn add webext-bridge
```
That’s it! Now we can begin to use the package within our extension.
## Including the right module
Let’s say we have 3 pieces to our extension. We have a background script, content script, and popup. Each piece of the extension needs to communicate with one another efficiently.
For the sake of simplicity in this example, we’ll say our popup script lives in the `popup.js` file, our background script in `background.js`, and our content script in `content.js`.
The most important part of using the `webext-bridge` package is to import the right module in the right place when setting up your communication. Let’s say we want to handle communication in the content script:
```javascript [Handle communication from the content script]
import { sendMessage, onMessage } from "webext-bridge/content-script"
```
Notice how we import the `webext-bridge/content-script` module? That module is scoped to the content script. So when we target a message to the content script, we ensure it’s going to the right place and getting picked up by the script itself.
The same goes for the popup and the background script. To handle communication in either of those areas, you’d add the following code respectively:
```javascript [Handle communication from the popup]
import { sendMessage, onMessage } from "webext-bridge/popup"
```
or
```javascript [Handle communication from the background page]
import { sendMessage, onMessage } from "webext-bridge/background"
```
Once you have the right module imported in your code, you are ready to pass messages!
## Sending Messages
Now that we have our modules included, let’s send some messages! This can happen on a user interaction, a timed event, or anywhere within your extension you need to communicate.
If you notice, when you import a module, you are importing two methods, `sendMessage()` and `onMessage()`. Let’s look at the `sendMessage()` method first.
The `sendMessage()` method is, you guessed it, how you send a message. It accepts 3 parameters no matter where you include it:
- `messageId` - ID of the message that we are sending. I usually set this to an uppercase enum or string value so it's easy to listen for and makes sense.
- `data` - JSON data to send along with the message. Used for processing.
- `target` - The target we are sending the message to. In our example, there are 3 targets we will be using: `background`, `content-script`, and `popup`. We will also show how to target the content script at a specific tab.
Let’s say we want to send a message from our popup to the background. Our full code would look like this:
```javascript [Send message from popup to background]
import { sendMessage, onMessage } from "webext-bridge/popup";
const response = await sendMessage("MESSAGE_ID", {
// your data
}, "background");
```
The two big things to note are the `MESSAGE_ID` which will identify the message where we intend to receive it and handle the functionality, and the `background` target. The `background` target identifies the background script as the receiving end of the message.
Right now, we are halfway through the process. Let’s look at the `onMessage()` handler and finish up this message passing.
## Receiving Messages
So we have a message being passed to our background script, but how do we handle it? That’s where the `onMessage()` handler comes in. The \`
# Build an API Wrapper with VueJS & Axios
Creating an API wrapper using VueJS & Axios makes your API interfacing code extremely fluid, modular, and maintainable. Before we get started, those using NuxtJS should skip to the next tutorial. This will tutorial will ONLY work with VueJS and not within NuxtJS.
With that being said, so far we've installed [Axios and got it to work with VueJS](https://serversideup.net/blog/using-axios-to-make-api-requests-with-vuejs/) and configured [Axios to work globally](https://serversideup.net/blog/configuring-axios-globally-with-vuejs/). We've also went through a few more [complex requests like POST, PUT, & PATCH](https://serversideup.net/blog/post-put-patch-requests-with-vuejs-and-axios/). In this tutorial we will abstract all of our API requests into wrapper modules. Before we get started, let's begin with why.
## Why Should We Do This?
Simply put, for code maintainability and re-usability. What we will be doing is taking all of our API requests for a specific resource and wrapping them in a module. If you need to make a request to an API endpoint using this module with a Vuex action, you can. If you need to make an API request in a component, you can do that as well. This process will work with your own API or a 3rd Party API.
However, the best part is, if you need to change a request in any way (like adding a header, upgrading API versions, etc), you update it once. This will update the request through your entire app! It's really convenient and easy to use. In the next tutorial I'll show you how to do this with NuxtJS if that's what you are using. Let's get started!
## Step 1: Build a Front End API Directory
The way I approach development is to set up "buckets" (which are just folders) to house code and pseudo-structure before I start developing. Before I add any API wrapping modules, I always create an `/api` directory at the root of my app. In this directory I will place all of my modules.
If you have a very complex API, feel free to add sub-directories to this as well. The more code organization, the better! Let's add our first module.
## Step 2: Add Your First Module
Let's say we have an API focused around music. This API has endpoints that allow us to manage the songs resource. We can view all songs (`GET`), create a song (`POST`), update a song (`PUT`), view a song (`GET`) and delete a song (`DELETE`). There are a variety of places in our application where we interact with this resource, so we want to build a module.
The first step is to add the file: `/api/songs.js`. In this file, add the following code:
```javascript [Template for our API request module]
export default {
index( params ){
},
show( id ){
},
update( id, data ){
},
create( data ){
},
delete( id ){
}
}
```
If you've worked with Laravel in the past, this naming convention may look familiar. If you haven't, you might be wondering what's up with the names of these methods. Laravel uses a naming scheme for resource endpoints that I really enjoy implementing on both back and front end. If you want to read about it, check out their [documentation on Resource Controllers](https://laravel.com/docs/7.x/controllers#resource-controllers){rel=""nofollow""}.
You don't have to use Laravel as your backend and you really don't even have to use this naming format. These are just the methods that we will be implementing for each resource regardless of first party or third party. Each method will return an Axios request which is a promise. This way we can efficiently handle the request in context.
### The `index` Endpoint
When following a resource naming scheme, the `index` endpoint will load all of a specific resource. It's a `GET` request that usually accepts parameters so you can filter/order your response. You don't really want to load all of the resource when you call this route. Especially in larger systems where this could literally be over a million items, so these filters usually come in handy or maybe even be required.
Let's take a look at our music app example. Say we want to load all of the songs, we will have to implement the `index()` method. To do that, add the following code:
```javascript [Example of calling an index endpoint]
index( params ){
return axios.get( 'https://music.com/api/v1/songs', {
params: params
})
},
```
*Remember, we set up [Axios to be global!](https://serversideup.net/blog/configuring-axios-globally-with-vuejs/)*
This method accepts a single parameter which should be a JSON object containing any filters, or ordering data that you wish to send. This variable allows you to pass filters to your resource module in the form of a JSON object that Axios will turn into a query string for your API. Remember, sending a [GET request with Axios](https://serversideup.net/blog/using-axios-to-make-api-requests-with-vuejs/), the second parameter of the Axios GET request is the configuration. This will build a nice query string to append to your request. Because of these filters, this tends to be the most complicated endpoint to create.
Let's say you wanted to search for an artist and order by newest releases. You'd pass a JSON object like this:
```json [Example search request]
{
"artist": "Red Hot Chili Peppers",
"order_by": "release_date",
"order_direction": "DESC"
}
```
Your query string will then be formatted like: `?artist=Red%20Hot%20Chili%20Peppers&order_by=release_date&order_direction=DESC`
Let's build out the rest of our endpoints for our module and then we can show the power of this module within VueJS.
### The `show` Endpoint
The `show()` method is very similar to the `index()` method since it is a `GET` request. However, the `show()` method should only return a **single** resource. In our example, this is a song. To implement this method simply add the following code:
```javascript [Example of calling a show() endpoint]
show( id ){
return axios.get( 'https://music.com/api/v1/songs/'+id );
},
```
Since we are limiting what this endpoint is returning to a single resource, we don't pass a `params` variable like we did to the `index()` method. The variable `id` allows us to load an individual resource based on it's unique identifier.
### The `create` Endpoint
This endpoint will handle the creation of a resource on the API, thus using the `POST` method. As [discussed in the last section](https://serversideup.net/blog/post-put-patch-requests-with-vuejs-and-axios/), there are two ways to send data to a server with Axios, through JSON and through Form Data. Our API wrapper will account for this by accepting whatever version you throw at it. Let's add the endpoint like this:
```javascript [Example of calling a create() endpoint]
create( data ){
return axios.post( 'https://music.com/api/v1/songs', data );
},
```
The parameter `data` will contain either a JSON object or a FormData object depending on if you need to send files. Either one works! When we implement this module in Step 3 & 4 you can see how fluid this will be to submit data to a server.
### The `update` Endpoint
Similar to the `create` endpoint, the `update` endpoint sends data to the API. However, since we are updating a resource, we need an additional parameter of `id` to identify the specific resource we are updating. Depending on the [use case,](https://serversideup.net/blog/post-put-patch-requests-with-vuejs-and-axios/) you should use `PUT` or `PATCH` for the method. Let's say we are using `PUT`. Our wrapper method should look like this:
```javascript [Example of calling an update() endpoint]
update( id, data ){
return axios.put( 'https://music.com/api/v1/songs/'+id, data );
},
```
We build the URL to update the specific song defined by our `id` parameter. Similar to the `create` endpoint, the `data` parameter can be either JSON or FormData. HOWEVER, if it is FormData, you will have to modify your method in certain circumstances (like with Laravel), to look like this:
```javascript [Using PUT with FormData]
update( id, data ){
data._method = 'PUT';
return axios.post( 'https://music.com/api/v1/songs/'+id, data );
},
```
I know Laravel specifically requires you to `POST` any FormData to the server, but if you add `_method` and set it to `PUT` you can keep your resource controllers standardized and can respond to the request.
### The `delete` Endpoint
This is the last endpoint we will be implementing. It simply deletes a resource from the system. The only parameter it accepts is the `id` of the resource we wish to delete:
```javascript [Example of calling a DELETE endpoint]
delete( id ){
return axios.delete( 'https://music.com/api/v1/songs/' + id )
}
```
There we go! This is what our final API Wrapper should look like:
```javascript [Final API Wrapper]
export default {
index( params ){
return axios.get( 'https://music.com/api/v1/songs', {
params: params
})
},
show( id ){
return axios.get( 'https://music.com/api/v1/songs/'+id );
},
update( id, data ){
return axios.put( 'https://music.com/api/v1/songs/'+id, data );
},
create( data ){
return axios.post( 'https://music.com/api/v1/songs', data );
},
delete( id ){
return axios.delete( 'https://music.com/api/v1/songs/' + id )
}
}
```
Now, let's get to the part where our hard work pays off. Using the module within a VueJS component and Vuex module! You will be able to use these methods anywhere you feel is necessary making the code standardized and extremely re-usable!
## Step 3: Using Your API Wrapper in a VueJS Component
Now it's time to reap the rewards of our hard work! We can re-use our module in any component or page within our VueJS App! The API Wrapper comes in handy whenever you need to interface with an API. Sometimes, this could be in a parent page (like a layout) as well where you need to load data globally.
All you need to do inside of your component is include the API module you created. Let's say we have a page that loads all of the songs from our music app by the `Red Hot Chili Peppers`. We would have a component that looks like this:
```vue [Using our API Wrapper in a Vue component]
```
All we needed to do is `import` our API module within our component and then call the method in our \`
# Build Out API Requests in Javascript
In the last tutorial we added a few API Endpoints in Laravel [Add Laravel API Endpoints](https://serversideup.net/blog/add-api-end-points-laravel/). Now it's time to build out API requests in javascript to access these routes. Since we have everything ready to rock and roll with our VueJS configuration and our routes, it should be pretty smooth to add these requests and we can store the data. We get to start using Vuex modules as well!
## Step 1: Configure the config.js file
The config.js file I find extremely important for development in multiple environments. I put all of my JS environment specific information in this file. Right now we will only have one variable that varies per environment and that is the api\_url. On my development machine, I have a development domain called roast.dev and in production in the source code that is roastandbrew\.coffee. When I build my app I want node to call each API route correctly based on my environment. So in my `/resources/assets/config.js` I make a simple switch statement that builds out the `api_url` depending on which environment I'm building my app for, then I export the config.
My config.js file looks like:
```javascript [Example config file]
/*
Defines the API route we are using.
*/
var api_url = '';
switch( process.env.NODE_ENV ){
case 'development':
api_url = 'https://roast.dev/api/v1';
break;
case 'production':
api_url = 'https://roastandbrew.coffee/api/v1';
break;
}
export const ROAST_CONFIG = {
API_URL: api_url,
}
```
I can then export out any of the variables I feel fit as an object and use them in the rest of the front end of my app, like the next section where we define the API routes.
## Step 2: Add cafe.js file
I like to mimic our API structure with our resources in Javascript. This allows us to group our API routes by resource. First what we will need to do is make the cafe.js file in the `/resources/assets/js/api` directory.
Next we need to import the `ROAST_CONFIG` from the last step so we can access the API\_URL to make our requests. To do that, add the following code to the top of your `cafe.js` file:
```javascript [Import the config file into our API wrapper]
/*
Imports the Roast API URL from the config.
*/
import { ROAST_CONFIG } from '../config.js';
```
Now we have our base API URL to make our requests to. Next we should export a default module so we can use our API requests elsewhere in our application. Our `cafe.js` file should look like:
```javascript [Set up our basic API wrapper]
/*
Imports the Roast API URL from the config.
*/
import { ROAST_CONFIG } from '../config.js';
export default {
}
```
## Step 3: Add Request For Getting Cafes
We now need to add a method for getting all of the cafes. This method will access the `/api/v1/cafes` route on the Laravel side of our application. For all of our front end requests, we will be using the axios library that we installed when we configured our application: [GitHub - axios/axios: Promise based HTTP client for the browser and node.js](https://github.com/axios/axios){rel=""nofollow""}. We have this set up in this tutorial: /configuring-js-sass-single-page-app/ and will automatically add the proper header to access the application's API routes.
To add the request to access the cafes, add the following code inside your module:
```javascript [Create our getCafes() method]
export default {
/*
GET /api/v1/cafes
*/
getCafes: function(){
return axios.get( ROAST_CONFIG.API_URL + '/cafes' );
},
}
```
What this does is returns an axios GET request to the proper API route. In Vuex we will call this API request as an action and save the data to a module that we will build.
## Step 4: Add Request For Getting Single Cafeh
Similar to the last request, we will return an axios GET request to the `/api/v1/cafes/{cafeID}` route that we will call through Vuex. The difference is in this request we have a parameter for the cafeID which will be used to reference the cafe we are loading. To add this request add the following line of code below the last request:
```javascript [Load an individual cafe]
/*
GET /api/v1/cafes/{cafeID}
*/
getCafe: function( cafeID ){
return axios.get( ROAST_CONFIG.API_URL + '/cafes/' + cafeID );
},
```
This will return the specific cafe we are loading up.
## Step 5: Add Request For Adding A Cafe
The mindset is the same we are building an axios request that we are returning that we can call anywhere in the app. This time it's a POST request though and requires a few more parameters. However once again we are going to be calling this through a Vuex action which we will make in the next tutorial.
To add the POST request, add the following below the request for a single cafe:
```javascript [Add the request to create a new cafe]
/*
POST /api/v1/cafes
*/
postAddNewCafe: function( name, address, city, state, zip ){
return axios.post( ROAST_CONFIG.API_URL + '/cafes',
{
name: name,
address: address,
city: city,
state: state,
zip: zip
}
);
}
```
This method's parameters match what will get passed to our `/api/v1/cafes` route to add a new cafe. When we send our Vuex action's payload we will extract the data and call this method adding an action.
## Wrapping Up
So we have all of our actions added in javascript to the `/resources/assets/js/api/cafe.js` file and it should look like:
```javascript [Our entire API request module]
/*
Imports the Roast API URL from the config.
*/
import { ROAST_CONFIG } from '../config.js';
export default {
/*
GET /api/v1/cafes
*/
getCafes: function(){
return axios.get( ROAST_CONFIG.API_URL + '/cafes' );
},
/*
GET /api/v1/cafes/{cafeID}
*/
getCafe: function( cafeID ){
return axios.get( ROAST_CONFIG.API_URL + '/cafes/' + cafeID );
},
/*
POST /api/v1/cafes
*/
postAddNewCafe: function( name, address, city, state, zip ){
return axios.post( ROAST_CONFIG.API_URL + '/cafes',
{
name: name,
address: address,
city: city,
state: state,
zip: zip
}
);
}
}
```
We are returning axios requests so we can listen and act upon the promises within the Vuex action. Sounds really complex, but in the next tutorial you will begin to see how this all comes together very elegantly. This will keep track of all of the data we need in the front end of our app and make loading all of this data on demand a breeze. Since we are returning the axios requests and calling them in a Vuex action, we can handle errors and handle successes.
I know this is a lot of set up, but trust me, once you have it configured correctly everything will start to fall into place. Think of it this way. You can build a house with a foundation, but the second you add stuff to it you will quickly see it needs to be redone. Or you can build a solid cement foundation and adding features to your house is easily supported. Very similar in coding. We have all of this configuration, but once we get the base of it done, we can really fly together and make a beautiful SPA.
You will really see the benefits of doing it this way if you ever wanted a hybrid mobile application. The goal of this tutorial series is to get there with cordova. You literally can copy the code you have, create a new entry point file and within about 1-2 hours have a fully functioning mobile application.
# Build a Vuex Module
In the last tutorial [Build Out API Requests in Javascript](https://serversideup.net/blog/build-api-requests-javascript/) , we went through building methods to call our Laravel API routes with Javascript. We built the `/resources/assets/js/api/cafe.js` file which contains the front end requests to our backend API that we built here: [Add Laravel API End Points - Server Side Up](https://serversideup.net/blog/add-api-end-points-laravel/). Now we are at the point where we need to store the data we get from the API so we can use it in our Single Page Application. That's where Vuex Modules come in!
According to the Vuex documentation [What is Vuex? · Vuex](https://vuex.vuejs.org/en/intro.html){rel=""nofollow""} "Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion." What this translates to is it's a single point of data that can be reused across multiple components and multiple pages. Why would you want this? As you build larger, more complex single page applications, you end up using data in multiple places. For example, you have a user that's logged into your app. Instead of passing that user in as a parameter to each component that uses that user's first name and last name, you can store it in a Vuex module and access that data whenever is needed. We will be using that example and many more vuex modules in Roast.
Vuex is also extremely helpful in tracking the state of the data in your application. If you are using the dev tools, you can see the data each module holds and how to access it.
Vuex can be a little difficult to understand at first as it's a different way to store data in your application. The documentation written about Vuex is extremely thorough and helpful: [What is Vuex? · Vuex](https://vuex.vuejs.org/en/intro.html){rel=""nofollow""} . Not all applications will require a Vuex data store, but if you were like me, and you are writing a larger single page application, you begin to think "there has to be a better way to handle all of this data". You are right, that's Vuex.
## Step 1: Understanding mutations, getters, actions, modules, and store.
The first thing we have to do is lay out our data plan for what we are trying to build. Right now we have a backend API for cafes in Roast and a front end set of API methods to access these routes. Our fist module we will build will be a cafes module. This module will handle all of the data relating to what we get back from the API for cafes. A module is almost like a a basic object with certain methods used to access and set the data.
Next up, we have mutations. Mutations are ways to manipulate the data. Every piece of data in a module should have mutations there to manipulate it. You will *NOT* be manipulating the data directly. The goal of Vuex is to have a single funnel for all components to access the state of the data and keep it in sync. You will call an action to mutate the data. If this doesn't make sense, the code examples in this tutorial will help and this part of the Vuex documentation [What is Vuex? · Vuex](https://vuex.vuejs.org/en/intro.html){rel=""nofollow""}. The section "What is a "State Management Pattern"? has some super helpful images that explain the flow of Vuex.
Getters are how you access the current state of the data. You will be using these in the components you create to access the single point of data for your application.
All of the modules live in what's called a data store. This is like the entry point for your data structure. Each module like cafe will be part of a larger data store.
Lets get started and I'll explain more as we go!
## Step 2: Configure your `/resources/assets/js/store.js`
So we already created an initial `store.js` file when we set up our application to work with VueJS. We will need to open this file and a little code so we have an initial store ready to rock and roll.
At the top of your file, import Vue and Vuex so we can start building out our store:
```javascript [Initialize our store]
/*
Imports Vue and Vuex
*/
import Vue from 'vue'
import Vuex from 'vuex'
```
Next, we will instruct Vue to use Vuex as a data store. This will extend our Vue instance with the methods needed to utilize the Vuex data store. Add this line of code right after you imported Vue and Vuex:
```javascript [Add Vuex to Vue]
/*
Initializes Vuex on Vue.
*/
Vue.use( Vuex )
```
Lastly, we will export a new Vuex data store from our store.js file. This is so we can apply it to our Vue instance and have all of the modules accessible within components and routes. Add this to the end of the file:
```javascript [Export our data store]
/*
Exports our data store.
*/
export default new Vuex.Store({
modules: {
}
});
```
We now have a very basic data store configured. This will make it easy for us to expand with our modules.
## Step 3: Add es6-promise polyfill to store for IE
Of course IE 11 doesn't support promises and requires extra configuration. We will need to add the polyfill to our store so Vuex will work with IE.
So open up a terminal, navigate to your development directory and run the following command:
```bash [Add polyfill for IE]
npm install es6-promise —save-dev
```
This will add the es6-promise polypill to your package.json file for your application.
On the top of the `/resources/assets/js/store.js` file add the following line of code:
```javascript [Require the polyfill in our store]
/*
Adds the promise polyfill for IE 11
*/
require('es6-promise').polyfill();
```
Our store.js file should look like:
```javascript [store.js]
/*
|-------------------------------------------------------------------------------
| VUEX store.js
|-------------------------------------------------------------------------------
| Builds the data store from all of the modules for the Roast app.
*/
/*
Adds the promise polyfill for IE 11
*/
require('es6-promise').polyfill();
/*
Imports Vue and Vuex
*/
import Vue from 'vue'
import Vuex from 'vuex'
/*
Initializes Vuex on Vue.
*/
Vue.use( Vuex )
/*
Exports our data store.
*/
export default new Vuex.Store({
modules: {
}
});
```
We will be adding modules shortly, but it's nice to get this all set up so we have a place to put our modules.
## Step 4: Add the Data Store to Vue
Now that we have our data store built, we will need to add it to Vue. Our Vue instance that we are using resides in `/resources/assets/js/app.js` so open that file.
Below our import for our router add the following line of code: `import store from './store.js'`
This includes our store that we created. Next we have to extend our Vue instance with that store. So right under the router, make sure include the store we just imported. The updated code should look like:
```javascript [Import our store into our app.js]
new Vue({
router,
store
}).$mount('#app')
```
Our `app.js` file should now look like:
```javascript [app.js]
window._ = require('lodash');
try {
window.$ = window.jQuery = require('jquery');
require('foundation-sites');
} catch (e) {}
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Next we will register the CSRF Token as a common header with Axios so that
* all outgoing HTTP requests automatically have it attached. This is just
* a simple convenience so we don't have to attach every token manually.
*/
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
import Vue from 'vue';
import router from './routes.js'
import store from './store.js'
new Vue({
router,
store
}).$mount('#app')
```
We now are using the entire VueJS ecosystem in our application! Let's add some modules and put this work horse to use!
## Step 5: Add cafes.js Vuex module
The first part is to create a new file in your `/resources/assets/js/modules/` directory named `cafes.js`. This will be where we manage all of the data for our cafes. We can then use all of this data through out our app. This is where you will start to see some of the benefits of load time with a Single Page App. You will load the cafes once, store it our Vuex module and only re-load when needed. It will be available for use on any page or any component in our app!
Right now, the `cafes.js` file should be empty, the next step is configuring the file.
## Step 6: Configure the State
We are diving heavily into Vuex now! With our `/resources/assets/js/modules/cafes.js` file open, first import the CafeAPI from our API directory that we built in: /build-api-requests-javascript/. We will be using these methods in our actions to load the data.
Our file should look like this:
```javascript [Example of our cafes module]
/*
|-------------------------------------------------------------------------------
| VUEX modules/cafes.js
|-------------------------------------------------------------------------------
| The Vuex data store for the cafes
*/
import CafeAPI from '../api/cafe.js';
```
Now we will export a constant which will be our cafes module. Underneath the imported CafeAPI add the following code:
```javascript [Export our cafes store]
export const cafes = {
}
```
This is our module that we will be adding to our data store. We will import this into the store later on, for now we are just stubbing out our module.
Next we will add the 4 aspects of a Vuex module (state, actions, mutations, getters). First we will add an empty object for the state like so:
```javascript [Add state to our cafes]
export const cafes = {
state: {
}
}
```
The state is all of the data we want to track. In the cafes module I can think of 2 different pieces of data that we would want: An array of cafes, and an object that stores a single cafe. This matches our API that returns all of the cafes and an individual cafe.
We will initialize these two pieces of data like this:
```javascript [Initialize 2 pieces of state for our store]
export const cafes = {
state: {
cafes: [],
cafe: {}
}
```
As a rule of thumb, one issue I always ran into was displaying a loading state. In a Single Page App, loading states are essential. The HTML/CSS and other page features will load usually before the data giving a bad UX to users who are waiting for the data to load. For each piece of data we track in the state, I add a corresponding variable to the state which keeps track of the load status. This way I can read this variable to determine whether to display the loading state or not. With VueJS being reactive, when the data is loaded, the variable is updated, the components using the variable will be updated as well and show the screen accordingly. I'd add the following to the state:
```javascript [Add our loading statuses]
export const cafes = {
state: {
cafes: [],
cafesLoadStatus: 0,
cafe: {},
cafeLoadStatus: 0
}
}
```
I usually follow a method like this:
- status = 0 -> No loading has begun
- status = 1 -> Loading has started
- status = 2 -> Loading completed successfully
- status = 3 -> Loading completed unsuccessfully
This way we can display the appropriate information when needed depending on what loaded.
## Step 7: Configure the Actions
I briefly went over what actions were, but now it's time to implement them. Actions are what is called on the module to mutate the state. In this case, we will call an action that makes a request to the API and commits a mutation. The mutations we will implement in the next step.
First we will add an empty object that will contain our action methods:
```javascript [Define actions within our module]
export const cafes = {
state: {
cafes: [],
cafesLoadStatus: 0,
cafe: {},
cafeLoadStatus: 0
},
actions: {
}
}
```
In our actions object we will add the methods to load our cafes and individual cafe. Our module should look like:
```javascript [Template out our individual actions]
export const cafes = {
state: {
cafes: [],
cafesLoadStatus: 0,
cafe: {},
cafeLoadStatus: 0
},
actions: {
loadCafes( { commit } ){
},
loadCafe( { commit }, data ){
}
}
}
```
Two things to note. 1. Each method contains a destructured argument called `commit`. This is passed in by Vuex and allows us to commit mutations for our store. There are other destructured arguments you can pass in as well. To read more about Argument Destructuring read: [GitHub - lukehoban/es6features: Overview of ECMAScript 6 features](https://github.com/lukehoban/es6features#destructuring){rel=""nofollow""} 2. The `loadCafe` action contains a second argument named `data`. This is an object we will pass to the method that contains the ID of the cafe we are loading. We are limited to an extra argument so you can pass in an object for more variables.
Now to implement these methods, we will do the following:
```javascript [Implement our methods within our store]
export const cafes = {
state: {
cafes: [],
cafesLoadStatus: 0,
cafe: {},
cafeLoadStatus: 0
},
actions: {
loadCafes( { commit } ){
commit( 'setCafesLoadStatus', 1 );
CafeAPI.getCafes()
.then( function( response ){
commit( 'setCafes', response.data );
commit( 'setCafesLoadStatus', 2 );
})
.catch( function(){
commit( 'setCafes', [] );
commit( 'setCafesLoadStatus', 3 );
});
},
loadCafe( { commit }, data ){
commit( 'setCafeLoadStatus', 1 );
CafeAPI.getCafe( data.id )
.then( function( response ){
commit( 'setCafe', response.data );
commit( 'setCafeLoadStatus', 2 );
})
.catch( function(){
commit( 'setCafe', {} );
commit( 'setCafeLoadStatus', 3 );
});
}
},
}
}
```
First thing to note is the commit function. This is committing a mutation. We will be setting up these mutations in the next step. However, just a reminder, each piece of data in the state should have a mutation. In both methods, we commit the load status for the piece of the state we are using. Next we make an API call to load the specific piece of information that we want to load. These API calls are defined in our `/resources/assets/js/api/cafe.js` file. Since we are returning axios ([GitHub - axios/axios: Promise based HTTP client for the browser and node.js](https://github.com/axios/axios){rel=""nofollow""}) calls, we can then bind into the `then` and `catch` chained promises. `then` method gets called when the method is returned successfully, so we will commit a mutation with the data returned. The `catch` method gets called when the data has been loaded unsuccessfully. We then will mutate the data accordingly like set the load statuses to failure and clear out the cafes or the state since we don't want failed data. The `response` variable passed into each of the method allows us to access the response data and headers from the request.
## Step 8: Configure the Mutations
The mutations are how your data gets updated. Each module has state, which requires a mutation to update. The flow is like this:
1. The user calls an action
2. The action loads/computes data
3. The action commits a mutation
4. The state gets updated
5. The getter (next step) reactively returns the state back to the component.
6. The component gets updated.
There are lots of steps but trust me, compared to implementing something like this in jQuery or vanilla JS, Vuex makes this a breeze.
We have our state built, our actions made, now it's time to implement the mutations. We saw mutations being called when we configured our actions, time to make them functional.
If you look at the uniqueness of the commit methods in our actions, there are 4 unique mutations needed:
1. setCafesLoadStatus
2. setCafes
3. setCafeLoadStatus
4. setCafe
To start, let's add these methods to our mutations object:
```javascript [Add mutations to our module]
mutations: {
setCafesLoadStatus( state, status ){
},
setCafes( state, cafes ){
},
setCafeLoadStatus( state, status ){
},
setCafe( state, cafe ){
}
},
```
So all mutations do is set the state. This way they can be tracked and it's consistent whenever the state needs to be updated. The first argument for the mutation is the state. This is the local module state NOT the global state. So the state we configured in step 6 is accessible. The second parameter is the data we need to update the state to. Our mutations should look like:
```javascript [Wire up our mutations]
mutations: {
setCafesLoadStatus( state, status ){
state.cafesLoadStatus = status;
},
setCafes( state, cafes ){
state.cafes = cafes;
},
setCafeLoadStatus( state, status ){
state.cafeLoadStatus = status;
},
setCafe( state, cafe ){
state.cafe = cafe;
}
},
```
In each mutation, we set the local module's state data to what is being passed in. Really this is all each mutation does, mutates the state data. Next, we will configure the getters and our first Vuex module will be ready to go!
## Step 9: Configure the Getters
So we have state data we want to track, actions to retrieve the data from the API and mutations to set the data. Now it's time to retrieve the data from the modules. We do that with getters. We need to define an object that will contain all of our getter functions. If you are used to some OOP design principals, mutations are like setters, getters are... getters, they retrieve the data.
Our getters object should be added to our cafes component like:
```javascript [Define the getters key on our module]
getters: {
}
```
Next we will need a method for each of our state variables that will return the state data we are looking for:
```javascript [Build out our getters in our store]
getters: {
getCafesLoadStatus( state ){
return state.cafesLoadStatus;
},
getCafes( state ){
return state.cafes;
},
getCafeLoadStatus( state ){
return state.cafeLoadStatus;
},
getCafe( state ){
return state.cafe;
}
}
```
Each method takes the local module state as the parameter and each method returns the corresponding data in the state we wish to retrieve. That's all we need to do for our getters! We can now utilize all of this data within our components.
Our module is now complete for this tutorial and should look like this:
```javascript [Complete Vuex module]
/*
|-------------------------------------------------------------------------------
| VUEX modules/cafes.js
|-------------------------------------------------------------------------------
| The Vuex data store for the cafes
*/
import CafeAPI from '../api/cafe.js';
export const cafes = {
/*
Defines the state being monitored for the module.
*/
state: {
cafes: [],
cafesLoadStatus: 0,
cafe: {},
cafeLoadStatus: 0
},
/*
Defines the actions used to retrieve the data.
*/
actions: {
/*
Loads the cafes from the API
*/
loadCafes( { commit } ){
commit( 'setCafesLoadStatus', 1 );
CafeAPI.getCafes()
.then( function( response ){
commit( 'setCafes', response.data );
commit( 'setCafesLoadStatus', 2 );
})
.catch( function(){
commit( 'setCafes', [] );
commit( 'setCafesLoadStatus', 3 );
});
},
/*
Loads an individual cafe from the API
*/
loadCafe( { commit }, data ){
commit( 'setCafeLoadStatus', 1 );
CafeAPI.getCafe( data.id )
.then( function( response ){
commit( 'setCafe', response.data );
commit( 'setCafeLoadStatus', 2 );
})
.catch( function(){
commit( 'setCafe', {} );
commit( 'setCafeLoadStatus', 3 );
});
}
},
/*
Defines the mutations used
*/
mutations: {
/*
Sets the cafes load status
*/
setCafesLoadStatus( state, status ){
state.cafesLoadStatus = status;
},
/*
Sets the cafes
*/
setCafes( state, cafes ){
state.cafes = cafes;
},
/*
Set the cafe load status
*/
setCafeLoadStatus( state, status ){
state.cafeLoadStatus = status;
},
/*
Set the cafe
*/
setCafe( state, cafe ){
state.cafe = cafe;
}
},
/*
Defines the getters used by the module
*/
getters: {
/*
Returns the cafes load status.
*/
getCafesLoadStatus( state ){
return state.cafesLoadStatus;
},
/*
Returns the cafes.
*/
getCafes( state ){
return state.cafes;
},
/*
Returns the cafes load status
*/
getCafeLoadStatus( state ){
return state.cafeLoadStatus;
},
/*
Returns the cafe
*/
getCafe( state ){
return state.cafe;
}
}
}
```
## Step 10: Add the Module to the Store
Last but not least, we need to tell our Vuex data store to use the cafe module. To do this we need to open our `/resources/assets/js/store.js` file first.
Right below telling Vue to use Vuex, add the following:
```javascript [Add our cafes module to the Vuex store]
/*
Imports all of the modules used in the application to build the data store.
*/
import { cafes } from './modules/cafes.js'
```
This imports our cafes module, we are now ready to register it with the store. To do that, simply add the cafes to the Vuex.Store({}) modules object. Our `store.js` file should look like:
```javascript [store.js]
/*
|-------------------------------------------------------------------------------
| VUEX store.js
|-------------------------------------------------------------------------------
| Builds the data store from all of the modules for the Roast app.
*/
/*
Adds the promise polyfill for IE 11
*/
require('es6-promise').polyfill();
/*
Imports Vue and Vuex
*/
import Vue from 'vue'
import Vuex from 'vuex'
/*
Initializes Vuex on Vue.
*/
Vue.use( Vuex )
/*
Imports all of the modules used in the application to build the data store.
*/
import { cafes } from './modules/cafes.js'
/*
Exports our data store.
*/
export default new Vuex.Store({
modules: {
cafes
}
});
```
## Wrapping Up
In this tutorial we created a Vuex store and configured a module for our cafes. To see this in the application, visit your development environment and open the developer tools. Switch to the Vue tab (assuming you are using a supported browser) and it should look something like this:

You can see the state, the module and the getters within. The left side will show all of the mutations run and even allow you to time travel back and forth through your state. The entire Vue ecosystem is beautiful and the developer tools is the icing on the cake! The next tutorial will include a little bit of styling and some functionality on the front end of the app! Make sure to check out the code base here: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""}
# Building a Queue with Vue 3 and Vuex 4
This is a post I've been wanting to write for some time. We had to implement a client side queue in two of our apps recently using Vue 3 and Vuex 4.
Now why would you want a client side queue? Well with so much more power given to the web browsers, there are actually times where you might want to perform a long running task in the browser. For example, with [FFMPEG WASM](https://ffmpegwasm.netlify.app/){rel=""nofollow""} you can actually encode videos directly the browser itself, WITHOUT touching a server! For these scenarios, building a queue is the perfect system to make this work.
Since these are still kind of "fringe" scenarios, I wasn't going to go to the trouble right away by making an officially supported package. However, using Vuex with the reactivity of Vue 3, you can make a simple queue system fairly simply. Let's get started!
## Prerequisites
I'm going to assume you have Vue 3 and Vuex 4 installed as well. We will step through the configuration of these, but I won't go through everything on how you need to install them.
## Base Concepts
The idea of a queue is deferred processing. Essentially meaning, you can place jobs on a queue, they process in order, and you can continue working. When using a queue in Laravel for example, you might want to have the user send a request to an endpoint and return immediately, queuing up the job for later. This allows the user to use your site, get efficient responses, and still have resource intensive processes take place in the background. This will be similar with our client side queue where we want long running processes to run in the background while the user continues to use the app.
When you are using Vuex 4, you are already coming from the mindset that you need to have some "system level state". What that means is that you need access to data throughout all pages and components. This usually happens within a Single Page Application (NuxtJS or Vue 3) or an InertiaJS app. Any app that doesn't make a hard refresh between navigation. Or, if you have like one massive page that does a lot of features. An example would be an in browser video editor where it lives on one page and you want the user to export and continue working.
What we will be implementing in this tutorial is a simple queue in Vuex 4. With that queue you can push jobs, see the status, be alerted when the job finishes and view job history. This is great for those long running client side processes. If you want to jump right to the code, [check out the repo](https://github.com/serversideup/vue-3-vuex-4-queue){rel=""nofollow""}. Otherwise, let's get started!
## Step 1: Register your store in your Vue 3 app
This is the first step to using Vuex 4 to begin with, registering your store. There are many places you can do this, and will need to find where you configure your app's set up. If you installed your app using the `vue-cli`, this will be in your `src/main.js`.
Ours looks like:
```javascript [main.js]
import { createApp } from 'vue'
import { createStore } from 'vuex'
import AppLayout from './App.vue'
/**
* Vuex Queue Set Up
*/
import { queue } from './modules/queue.js';
const store = createStore({
modules: {
queue
}
});
const app = createApp(AppLayout);
app.use(store);
app.mount('#app');
```
There's a few things I'd like to point out.
First, we need to import the `createStore` function from `vuex`. This allows us to build our store and register it with the app.
Second, I already divided up our queue into it's own module (`import { queue } from './modules/queue.js';`). This is stored in the `/modules` directory. I think this is the most beautiful feature of Vuex. The fact you can break up state into small maintainable modules. The queue will have the full gamut of Vuex module functionality, so breaking it up right away allows us to easily manage the queue.
Next, if you installed through the `vue-cli` or have a similar structure, save what is returned from the `createApp()` function to a `const` variable. The `vue-cli` doesn't assign this function's return to anything. We need to do that, because in the next step we need to tell our app to use the store we created:
```javascript [Make sure our store is registered]
app.use(store)
```
Finally, make sure you mount the element using `app.mount('#app')` or whatever the ID of your primary element is. If you installed using `vue-cli` this happens right after the `createApp()` method is called. We need to ensure the app uses the store before we mount the element.
You are now ready to start and build out your queue module in Vuex!
## Step 2: Build your Queue Vuex 4 Module
As I mentioned in the last step, we created a Queue module. This module can live anywhere you want, but I recommend putting it inside a `/modules` directory. This way everything is easy to maintain and easy to find.
The first step is to add `queue.js` to `/modules` and give it some scaffolding for all of our Vuex functionality. If you are unfamiliar with Vuex, I'd take the time to [read the docs](https://vuex.vuejs.org/guide/state.html){rel=""nofollow""} before going further. We will be touching on all of the core components.
Our `queue.js` module should look like:
```javascript [Define our queue module]
export const queue = {
namespaced: true,
state: () => ({
}),
actions: {
},
mutations: {
},
getters: {
}
}
```
A quick note, I'd recommend namespacing so none of your modules interfere. Everything will be accessible from the `queue/` namespace within our components. Let's start building out our module.
**State**
Let's start with the state. In your `queue.js` module, add the following to your `state`:
```javascript [Define the state for our queue]
state: () => ({
pending: [],
completed: [],
active: {}
}),
```
This is the guts of our queue. The pending state will be all of the jobs in the queue. As the queue runs, it will move the first job into active so we can see which job is currently being processed. When the job is finished, the active job will move to the completed array and the next job will begin if it's available.
**Actions**
Now we are going to start getting into the weeds. The actions on your queue control how the queue works and moves jobs from pending, to active, to completed. There will be two actions on this queue, but they are fairly dense. Remember, actions can contain logic, but mutations only adjust state within Vuex. Let's take a look:
```javascript [The core functionality of our queue]
actions: {
addJob({ commit, state, dispatch }, job ){
commit( 'addPendingJob', job );
if( Object.keys( state.active ).length == 0 ){
dispatch('startNextJob');
}
},
startNextJob({ commit, state }){
if( Object.keys( state.active ).length > 0 ){
commit( 'addCompletedJob', state.active );
}
if( state.pending.length > 0 ){
commit( 'setActiveJob', state.pending[0] );
commit( 'popCurrentJob' );
}else{
commit( 'setActiveJob', {} );
}
}
},
```
The first action is `addJob()` which uses the decoupled `commit` , `state` , and `dispatch` parameters as the first local parameter. The second parameter is `job`. We need the `commit` function so we can perform mutations on our queue which we will talk about next. Then we need the `state` parameter to check if we need to start the next job which we will talk about next. The `dispatch` parameter dispatches the method to start the next job if we have nothing active. Finally, the `job` is the job object we will add to the queue. I'll show you the format of that when we dispatch our first queue job.
Right away, we commit the job to pending jobs. We want to make sure that we add it to the queue to keep the order of operations correct. Next, we check to see if we have any active job. If we don't have an active job, we immediately start the job by dispatching the action `startNextJob` .
The second action, `startNextJob` is doesn't take any special parameters, just the decoupled `commit` method, and `state`. First, we check to see if there is an active job. If there is, and we are starting the next job, we add the job to the completed jobs array. Next, we check to see if there is another pending job. If there is another pending job, we set the active job to the next job and then "pop" the current job, meaning we remove the job we just set from `pending` to `active`. If there's no next job, we just set the active job to an empty object.
Those are the only two core actions you need to use when building your queue! Next up, we will go through the mutations and getters, then on to dispatching our first jobs!
**Mutations**
There are 4 simple mutations in this queue. The power of using Vue 3 with Vuex 4 is the reactivity. A lot of the hard stuff is solved right out of the box! Let's add the following 4 mutations to our queue:
```javascript [Define the mutations used by our module]
mutations: {
addPendingJob( state, job ){
state.pending.push( job );
},
setActiveJob( state, job ){
state.active = job;
},
popCurrentJob( state ){
state.pending.shift();
},
addCompletedJob( state, job ){
state.completed.push( job );
}
},
```
Alright, so when we set up state, we set up the `pending` and `completed` variables to be arrays. With that, we have a few built in methods that we can use to manipulate these in away to make them act like queue.
First, we have the `addPendingJob` mutation. This uses the `.push()` method to push a job on to our `pending` jobs array.
Second, we have the `setActiveJob`. This mutation just accepts a job that comes from the `startNextJob()` action and sets it to the active job in our queue.
Next, we have the `popCurrentJob()` mutation. Once again, we can use a built in array function to remove the first job in the array, working how a queue data structure would work. We use the `.shift()` which "pops" the first job from the queue.
Finally, we have the `addCompletedJob()` mutation. This uses the `.push()` method again, except we push a job, the previous active job, onto the `completed` array that's monitored in our queue.
With those 4 mutations, we have a pretty powerful queue handler right there! Finally, let's add the getters and we can move to the next steps!
**Getters**
There are 3 getters to match the 3 pieces of data in our Vuex store. They are:
```javascript [Define the getters that access queue data]
getters: {
PENDING( state ){
return state.pending;
},
ACTIVE( state ){
return state.active;
},
COMPLETED( state ){
return state.completed;
}
}
```
Each getter works in the Vuex fashion of returning a piece of state. This way we can monitor the queue status from anywhere within our application.
Our entire queue module should look like this:
```javascript [Our entire queue Vuex module]
export const queue = {
namespaced: true,
state: () => ({
pending: [],
completed: [],
active: {}
}),
actions: {
addJob({ commit, state, dispatch }, job ){
commit( 'addPendingJob', job );
if( Object.keys( state.active ).length == 0 ){
dispatch('startNextJob');
}
},
startNextJob({ commit, state }){
if( Object.keys( state.active ).length > 0 ){
commit( 'addCompletedJob', state.active );
}
if( state.pending.length > 0 ){
commit( 'setActiveJob', state.pending[0] );
commit( 'popCurrentJob' );
}else{
commit( 'setActiveJob', {} );
}
}
},
mutations: {
addPendingJob( state, job ){
state.pending.push( job );
},
setActiveJob( state, job ){
state.active = job;
},
popCurrentJob( state ){
state.pending.shift();
},
addCompletedJob( state, job ){
state.completed.push( job );
}
},
getters: {
PENDING( state ){
return state.pending;
},
ACTIVE( state ){
return state.active;
},
COMPLETED( state ){
return state.completed;
}
}
}
```
Now that we have our module completed, let's put it to work and dispatch some jobs.
### Step 3: Dispatching Queue Jobs
Dispatching a job interacts directly with the `addJob()` action on our queue. That action will handle the movement of our job throughout our queue. Before we dispatch the `addJob` action, let's talk about the structure of our job.
The job will be a simple JSON object with two required parameters, an `id` and a `handler()` function. The ID will be used so you can visualize whatever is on the queue throughout your app. The `handler()` bundles the functionality you need to process your job. Before we dive into the `handler()`, I want to point out, you can add whatever you want to the job object that you need to display or use within your app. This object will be present in the `pending`, `active`, and `completed` sections of your queue.
Let's take a look at a sample job we can dispatch from anywhere within our app:
```javascript [Dispatch a queue job from a component]
this.$store.dispatch('queue/addJob', {
id: 'your-job-id',
handler: function( ){
// Add functionality and data here
// Complete the job. Can also be called from
// a promise like when making an API request
this.$store.dispatch('queue/startNextJob');
}.bind(this)
});
```
What this does is dispatch a job onto the queue using the `addJob` action. Remember, we namespaced our queue so we have to call `queue/addJob` to actually add the job. The **major** point to note is that our handler method has to have `.bind(this)` at the end. This way we can call the `this.$store.dispatch('queue/startNextJob')` method. This signals to our queue that the active job is completed and we can work on the next job.
Within the body of the `handler()` method, you can add whatever you need to process your job. You can include data, API requests, etc. Whenever you are done, just dispatch the `startNextJob` action and the queue will go to the next job!
With that being said, we need to add 1 more component within our app, and that's the component to control the queue.
### Step 4: Controlling the Queue
Now this component can be very customized. It could even be a mixin that you include on the app level, or through a composition function. For this example, we are just going to throw it in a headless component and include it at the root level of our app. This component simply reads in from the queue and facilitates the processing of the jobs. This is what the component should look like:
```javascript [Control the queue within a component]
import { mapState } from 'vuex';
export default {
computed: {
...mapState('queue', {
pending: 'pending',
active: 'active',
completed: 'completed'
})
},
watch: {
active(){
this.processJob();
}
},
methods: {
processJob(){
if( this.active.handler ){
this.active.handler();
}
}
}
}
```
Since we are operating with `vuex`, we import the `mapState` method from `vuex`. This allows us to view the state right within our component. In the `computed` section, we mapped the `vuex` state locally so we can watch for reactive changes. Once again, we namespaced our `queue`, so we add the namespace as our first parameter.
Next, we have the glue that holds this all together, the `watch` method that watches our `active` job. Due to the reactive nature of Vue, whenever there is a change, we can monitor that change. So whenever there is a new active job, we can process it! That's exactly what happens here:
```javascript [Watch the active job]
watch: {
active(){
this.processJob();
}
},
```
When the active job changes, we call the method to process the job.
Next up, we define our only method of `processJob()`. It's super straight forward. If there's a handler on the active job, we call the handler! That decouples the functionality into the job itself so you can have flexible jobs! It also checks to see if the handler is present. This is because if we finish our queue, we reset the active job to an empty object. The reset of the job will trigger the `processJob()` since the active job changed. We don't want an error since the handler will not be defined.
That completes the full loop of the queue with Vue (nice rhyme, took me this long to realize that)! Next we will touch on how to display this status anywhere in your app and some optional steps.
### Step 5: Viewing Queue Status
So the three pieces of state can be mapped anywhere within your app. All you have to do is make sure the `mapState` method is imported `import { mapState } from 'vuex';` and you map the state locally. From there, you can loop over the pending and completed jobs and/or display the active job and reference keys on that object.
For example, you could show pending jobs like this:
```html [Display pending jobs]
{{ job }}
```
You can also show the active job, and some data like this:
```html [Show active job]
Processing Job {{ active.id }}
```
Those are very simple examples, but that should show a small example of how you can expand in the future!
## Optional Steps
There are a few optional features you can add to enhance the functionality of your queue.
### Clear Pending Jobs
If you have a lot of tasks and realize you made a mistake, you might need to clear the pending jobs. Adding an action you can call to clear the pending jobs is extremely useful. All you have to do is add the following action:
```javascript [Clear pending jobs]
clearPendingJobs({ commit }){
commit( 'setPendingJobs', [] );
}
```
With the mutation:
```javascript [Set pending jobs]
setPendingJobs( state, jobs ){
state.pending = jobs;
}
```
Now when you call:
```javascript [Dispatch even to clear pending jobs]
this.$store.dispatch('queue/clearPendingJobs');
```
Your pending jobs will be cleared!
### Clear Completed Jobs
Similar to pending jobs, but more likely to occur, is the clearing of the completed jobs. After awhile, these may become irrelevant. Let's add an action that clears these jobs:
```javascript [Clear completed jobs]
clearCompletedJobs({ commit }){
commit( 'setCompletedJobs', [] );
}
```
Then add the following mutation:
```javascript [Set completed jobs mutation]
setCompletedJobs( state, jobs ){
state.completed = jobs;
}
```
Now when you call:
```javascript [Action used to clear completed jobs]
this.$store.dispatch('queue/clearCompletedJobs');
```
All of your completed jobs will be cleared.
### Remove Individual Job
This is also possible with the queue system. And super useful if you mess up a job and don't want to re-add everything to make it work. To do this you will need to add the following action:
```javascript [Cancel job action]
cancelJob({ commit }, index ){
commit( 'removeJob', index );
}
```
This action takes the index of the job we want to cancel. Next, we need to add the following mutation:
```javascript [Remove an individual job from the state]
removeJob( state, index ){
state.pending.splice( index, 1 );
}
```
Once again, we can use another array function, `splice()` provided by Javascript to make this efficient. The first parameter in `splice()` is the index we wish to remove, while the second parameter is how many elements we wish to remove. We only want to remove 1 element at an index, so that's all we need.
Now when we want to remove an individual job, we can run the following:
```javascript [Action used to cancel the actibe job]
this.$store.dispatch('queue/cancelJob', index );
```
And our job has been removed! Those are just a few of the other helpful queue functions we can add.
## Conclusion
Hopefully this helped show you some of the possibilities with Vuex 4 and Vue 3 to create dynamic systems! If you have any requests or other ideas, let me know. I might mess around with Pinia in the future to see how that works as well. If you want to see the final product, check out the repo. It's very simple, but should demonstrate some of the potential!
If you want to learn more about Single Page Application structuring, [check out our book](https://serversideup.net/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). We are in the process of updating it to Vue 3 & Nuxt 3! We cover a lot more information regarding larger Single Page Applications as well as building an API.
Any questions, feel free to reach out on [Twitter](https://twitter.com/danpastori){rel=""nofollow""} or on our [community forum](https://community.serversideup.net){rel=""nofollow""}!
# Building a Single Song Player
So, let's start with a very simple player, a single song player. For this tutorial, I will be building the example player here: [amplitudejs/examples/single-song at master · 521dimensions/amplitudejs · GitHub](https://github.com/521dimensions/amplitudejs/tree/master/examples/single-song){rel=""nofollow""}. All the code is available to download but I'll be walking through the basics as an introduction on how to use AmplitudeJS and play a single song in your browser.
## Pre-Requisites
Before we start building our player, we need to have a few assets in order:
1. The newest version of AmplitudeJS here: [GitHub - 521dimensions/amplitudejs: Amplitude.js is the HTML5 Audio Player for the modern era. No dependencies required.](https://github.com/521dimensions/amplitudejs){rel=""nofollow""}
2. A song you want to play (preferably in an MP3 Format)
3. An organized directory you can open in a web browser. You shouldn't need a web server since there's no server side code.
If you have a design for your player already, you can follow along with the basics, otherwise feel free to grab the files from the example player as that's what we will be using to step through.
## What We Will Touch On
- Getting AmplitudeJS initialized
- Play/Pause Button
- Song Meta Data
- Song Progress Bar
- Time Meta Data
## Getting Started
With any web development, I like to lay out my folder structure right away so as I build I have places for all of my assets.
For this player, I'd open your working directory and make the following files and folders:
- `/index.html`
- `/css`
- `/js`
- `/img`
If you are doing SASS compiling or any other JS compiling, you might want to add a `/resources` directory and add all of your components in there. In the example player we DO use SASS, but that's outside the scope of this tutorial. We will be walking through the mindset of using AmplitudeJS for building a single song player.
As for JS, if you are following along with the example player, I have foundation and jQuery included. AmplitudeJS is not dependent on any 3rd party library so these are not required for any player. I use foundation for just page layout and jQuery for any animation functions.
## Structuring Your Player
To start off, set up your `index.html` file like this:
```html [Basic HTML structure for the player]
AmplitudeJS Single Song Example
```
A few things to notice here, we included foundation: [The most advanced responsive front-end framework in the world. | Foundation](https://foundation.zurb.com/){rel=""nofollow""} like I mentioned, this is only for layout and helps design the player.
I also included a Google Font which makes the player look good. You can include any other styles or fonts if needed.
Next, I add a player container which is simply a `
` element that we will be structuring to make our player.
```html [Player container element]
...
...
```
This is not an AmplitudeJS element it's simply a container for the player that we are building. Inside of this element will contain a few AmplitudeJS specific elements.
## Adding Album Art
This is our first encounter with an AmplitudeJS specific element. We will be adding our album art image to the player. To do this, right inside of the `id="single-song-player"` element, add the following:
```html [Album art element with AmplitudeJS attributes]
```
What this does is it will show the album art for whatever song is being played. There are two attributes. The first one `amplitude-song-info="cover_art_url"` defines the meta data we should use for the element. In this case, whatever is defined in our song object (discussed later) for `cover_art_url` will be set to the `src` attribute of the image.
The next attribute, `amplitude-main-song-info="true"` states that this will show the `cover_art_url` for whatever song is playing.
Our player element should now look like this:
```html [Player container with album art]
...
...
```
## Adding Song Progress
A unique element in AmplitudeJS is the song played progress bar. It visualizes how far along the song being played. In a later tutorial I will dive in on some of the specifics and how to style the element. For now, add the following HTML to your player right below the image:
```html [Song progress bar element]
```
You will notice on the progress bar the class of `amplitude-song-played-progress`. This alerts AmplitudeJS that this progress element will display the current progress played in the song. We also have the attribute `amplitude-main-song-played-progress="true"`. This means that whatever song being played this will show the progress for that song. It is the main area to find that information.
## Adding Time Information
Below the progress bar, we will add a few time meta data fields. This will give a number read out of how long the song is and how far into the song we are. As discussed in [Working With AmplitudeJS Song Object Metadata](https://serversideup.net/blog/working-with-amplitudejs-song-object-metadata/), there are lots of different time meta data fields. For this player, we are only going to use:
- Current Minutes
- Current Seconds
- Duration Minutes
- Duration Seconds
You can use whatever you want, I like scoping each into their own span just for full control, but you can use a time stamp or whatever you want. Add the following HTML below the progress bar:
```html [Time metadata elements]
:
:
```
The time container that wraps everything is not an AmplitudeJS specific element. It's solely a structural element. As you notice, AmplitudeJS fits right in with structural elements and only binds the information needed to the elements specified.
In the code, there are the following classes: \* `amplitude-current-minutes` \* `amplitude-current-seconds` \* `amplitude-duration-minutes` \* `amplitude-duration-seconds`
Each of these elements has an attribute `amplitude-main-[WHATEVER-ELEMENT]` (ex. `amplitude-main-current-seconds`). This means that they will display the appropriate data for whatever song is playing since they are main elements. When we dive into playlists and multiple songs per page, we will notice that these are linked to a playlist or a song and will display data for the specific song or the specific playlist. For this example there is only one song, so it needs to be the main data.
As with everything we've added, you can apply any styles you want through CSS. The next section, we will add some control elements, these have a few styling guidelines.
## Adding Play Pause Element
The Play/Pause button is probably the most important element in AmplitudeJS. It does exactly what it's name states, it either plays or pauses the current song depending on the state of the song. It's a toggle button so the individual element will act differently depending on the state of the player.
Add the following HTML below your time elements:
```html [Play/Pause button element]
```
The control container is just a structural element, and is not an Amplitude specific element. We want to focus on the element `amplitude-play-pause`. You can apply a class to ANY element you want and when the user touches or clicks the element, it will toggle play or pause. Before we get into the styling details, I'd like to point out, this is an element with the attribute `amplitude-main-play-pause="true"`. This means it will play or pause on a global level. Whatever song is active whether it's a playlist, individual song, or the only song will get toggled depending on the state of the player.
Now, when Amplitude initializes (see Initializing AmplitudeJS), there will be an additional class that gets added to this element called `amplitude-paused`. This is the current state of the player on initialization. When the button is clicked, the class will be removed and the class `amplitude-playing` will be added. These classes allow the developer to style the element based on the state of the player. AmplitudeJS will NEVER add HTML to the page. It will only adjust classes to represent the state of the player.
Now a common use case is to use an image to represent the play or pause state of the player. Do not put an image inside of the element!! Instead set it in the CSS as the `background-image` attribute like this:
```css [CSS styling for play/pause button]
div.amplitude-play-pause {
width: 74px;
height: 74px;
cursor: pointer;
float: left;
margin-left: 10px;
}
div.amplitude-play-pause.amplitude-paused {
background: url('../img/play.svg');
background-size: cover;
}
div.amplitude-play-pause.amplitude-playing {
background: url('../img/pause.svg');
background-size: cover;
}
```
The reason we won't put the image inside of the element is AmplitudeJS binds a click/touch handler to the element. Javascript might detect the click on the inner element and it won't handle correctly. You can do whatever you want in the CSS and add the style accordingly.
## Adding Now Playing Meta Data
Before we initialize AmplitudeJS, let's add the last HTML we need to display the now playing meta data. Below the play/pause button add the following HTML;
```html [Now playing metadata elements]
```
Once again, meta container is not an AmplitudeJS element, but the rest of them are. We are showing the song name and artist. As discussed in [Working With AmplitudeJS Song Object Metadata](https://serversideup.net/blog/working-with-amplitudejs-song-object-metadata/), these elements get populated with the text from the song meta data. Whatever is in the `amplitude-song-info` attribute relating to the song object is inserted in the element. These are also main elements as noted by `amplitude-main-song-info="true"`. This means whatever song is playing, the data gets added to the element. These can be scoped per song, per playlist, or global.
So now we have our HTML laid out for AmplitudeJS to bind to! All CSS can be found here: [amplitudejs/examples/single-song at master · 521dimensions/amplitudejs · GitHub](https://github.com/521dimensions/amplitudejs/tree/master/examples/single-song){rel=""nofollow""} along with the code that we stepped through. Next up is initializing AmplitudeJS!
## Initializing AmplitudeJS
Now that we have our HTML set up, it's time to activate it! This is done with the `Amplitude.init()` method: [Initializing AmplitudeJS](https://serversideup.net/open-source/amplitudejs/docs/){rel=""nofollow""}. The `Amplitude.init()` method initializes AmplitudeJS with a variety of different configuration options, callbacks, songs, playlists, etc. The most important being the songs. Now you don't HAVE to even add a song to AmplitudeJS on configuration, you can dynamically load them if needed. However, in this case, we we will be initializing with one song.
The `Amplitude.init()` method takes a JSON object as it's only parameter in there, you can define everything you need. Let's add the following code to the bottom of the `index.html` file:
```javascript [AmplitudeJS initialization code]
```
A few things to note. First, this method runs when it's called. It does not wait for a ready function or anything else. That means it should be called at the end of your document or wrapped inside a document.ready() method! If you call this method before you have your HTML, it won't bind to any elements and it won't do anything! Now, you can call `Amplitude.bindNewElements()` if your display changes and you need to add more AmplitudeJS elements. We will discuss that in a later tutorial.
Next, the songs are in an array. This is important to note because a lot of AmplitudeJS functions reference a song index. The only song we have in the array right now has an index of 0. This is programming so the first object starts at 0 and goes up. When creating playlists, these indexes will be used to create the playlist.
You can see in the song object, there's a lot of meta data. The only required piece is the `url`. This points to the song that will be played by AmplitudeJS.
That's it! By calling `init()` AmplitudeJS has bound all of the click/touch handlers to the elements and has loaded a song. The code from GitHub includes a few other pieces of JS that we will dive more in depth on in later tutorials. You can do cool things like bind key events to control AmplitudeJS on the keyboard and other click handlers.
Let me know if you have any questions or want me to explain something differently in the comment section below!
# Building a Page Layout for Vue Router
One thing that comes in real handy with any app is the ability to add layouts to your application. Laravel does that with the Blade templating system: [Blade Templates - Laravel - The PHP Framework For Web Artisans](https://laravel.com/docs/5.6/blade){rel=""nofollow""}. However, since we are doing a Single Page Application, doing that with Vue Router is a little bit of a trick. The way I like to do it is create a root level page that contains the Vue components present on every page, like Header and Footer. The other advantage of using a layout, is you can load all of the Vuex data you need once and it will be present on all of the child pages, which in this case, is our entire app.
## Step 1: Restructure To Nested Routes
In the tutorial where we set up our `/assets/js/routes.js` file: /configuring-vue-router-single-page-app/ we didn't include any nested routes. The nested feature of Vue Router is the trick that I use to make a layout. For more information on nested routes, check out: [Nested Routes · vue-router](https://router.vuejs.org/en/essentials/nested-routes.html){rel=""nofollow""}
First we will build our top level route which will be our Layout. To do this, add the following route object to your routes file:
```javascript [Top level route configuration]
{
path: '/',
name: 'layout',
component: Vue.component( 'Home', require( './pages/Layout.vue' ) ),
}
```
We will be adding the Layout.vue in the next step. Now we will add a `children` key that will contain the array of routes that are nested routes. We should have a routes.js file that looks like:
```javascript [routes.js]
/*
|-------------------------------------------------------------------------------
| routes.js
|-------------------------------------------------------------------------------
| Contains all of the routes for the application
*/
/*
Imports Vue and VueRouter to extend with the routes.
*/
import Vue from 'vue'
import VueRouter from 'vue-router'
/*
Extends Vue to use Vue Router
*/
Vue.use( VueRouter )
/*
Makes a new VueRouter that we will use to run all of the routes
for the app.
*/
export default new VueRouter({
routes: [
{
path: '/',
name: 'layout',
component: Vue.component( 'Layout', require( './pages/Layout.vue' ) ),
children: []
}
]
});
```
Now we will take the existing routes and add them to the children array to get a file that looks like:
```javascript [routes.js]
/*
|-------------------------------------------------------------------------------
| routes.js
|-------------------------------------------------------------------------------
| Contains all of the routes for the application
*/
/*
Imports Vue and VueRouter to extend with the routes.
*/
import Vue from 'vue'
import VueRouter from 'vue-router'
/*
Extends Vue to use Vue Router
*/
Vue.use( VueRouter )
/*
Makes a new VueRouter that we will use to run all of the routes
for the app.
*/
export default new VueRouter({
routes: [
{
path: '/',
name: 'layout',
component: Vue.component( 'Layout', require( './pages/Layout.vue' ) ),
children: [
{
path: 'home',
name: 'home',
component: Vue.component( 'Home', require( './pages/Home.vue' ) )
},
{
path: 'cafes',
name: 'cafes',
component: Vue.component( 'Cafes', require( './pages/Cafes.vue' ) ),
},
{
path: 'cafes/new',
name: 'newcafe',
component: Vue.component( 'NewCafe', require( './pages/NewCafe.vue' ) )
},
{
path: 'cafes/:id',
name: 'cafe',
component: Vue.component( 'Cafe', require( './pages/Cafe.vue' ) )
}
]
}
]
});
```
Now we will add our Layout.vue page.
## Step 2: Add Layout.vue
In the `/resources/assets/js/pages` directory add a file called `Layout.vue`. This will be our Layout file where we can add our Navigation component discussed in our last tutorial. Stub out the component like this:
```vue [Layout.vue]
```
If you check out `` that component is built into Vue Router which renders the child component inside of the parent component. It's like a place holder for the nested component.
We now have our layout, so let's import our `Navigation.vue` component so you can see the power of the layout. We just need to add the following line before we export the default component:
```javascript [Navigation component import]
import Navigation from '../components/global/Navigation.vue';
```
That will include our Navigation component, we then need to tell our page to use it by adding it to the `components` object on the page:
```html [Adding Navigation to components]
```
Finally, we add the component to our template before the `` component so our layout looks like this:
```vue [Layout.vue]
```
What this means is now the Navigation component will appear on all pages since every page is a sub page of the layout. Kinda slick eh?
Let's load the data we need on every page.
To do that, since we have Vuex ready to rock and roll: [Build a Vuex Module](https://serversideup.net/blog/build-vuex-module/), we simply bind to the created() hook and load some data!
This case, we know we want a user in the navigation component so we can display a profile picture, as well as loading the cafes. When we load it on the parent component, and since we are using Vuex, the user will be available to ALL routes. I didn't write a tutorial about writing everything we need to load the user, but if you followed along with this series, it's follows along the same steps as adding a cafe module, routes, models, etc. Of course all of the code can be found here: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""} .
Our Layout.vue component should look like:
```vue [Layout.vue]
```
When created, the layout loads the cafes and user that's currently logged into the app.
## Step 3: Adjust the Authentication Controller Redirect
This is the last step of adding a Layout to the app. Since now the top level link is our layout, we have to direct users to our `/home` route upon login. To do this, open up the `/app/Http/Controllers/Web/AuthenticationController.php` and find the `getSocialCallback()` method. At the end of the method adjust the redirect from
```php [Original redirect code]
return redirect('/');
```
to
```php [Updated redirect code]
return redirect('/#/home');
```
Now your users will be redirected to the home page on login.
## Conclusion
Now that you have your layout created, you can add any sort of elements you want to appear on each page. I've added an AppFooter.vue as another example as a component that should be on every page. You can find it in the repository: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""}. There are other ways you can pull this off if needed. One would be if you don't want to access Vuex data, then you could add header and footer into your `app.blade.php` file and you can load the Vue data on a `
` element between the header and footer. I just prefer to do it this way since we can load global data and access Vuex information.
# Caching API Endpoints with Laravel
As your API begins to grow, you will want to start looking into some performance optimizations. Laravel provides a [variety of different options](https://laravel.com/docs/9.x/cache){rel=""nofollow""} when it comes to caching endpoints, such as Redis, Memcached, DynamoDB, and even simpler file caches. By default, Laravel is set to cache in a file.
Since Laravel is facade driven, basic caches don't matter from a programming perspective. The method to actually cache data is the same and will use what you have configured for your environment. However, you will get better performance using a high-availability service like Redis or Memcached as your app grows.
## Why Cache API Endpoints?
Speed and performance. That's why you'd want to cache an API endpoint. Say you have a database with thousands or millions of records. Every time you hit an endpoint to load data, you are querying the database to load the records you need. Throw a bunch of users on your system and you will get massive performance issues. If you cache pre-calculated data and update the data in your cache when it changes, you will have a much better performance!
You can get infinitely complex with how much you cache, but try to take the low hanging fruit first. By that, I mean say you have an endpoint that rarely gets updated, but has a lot of requests and a lot of data computation. Those are the perfect places to begin where you will see the biggest performance increase.
For this tutorial, I'll be going through a real life example of an app I'm working on called Amplitude Hosting that works with [AmplitudeJS](https://github.com/serversideup/amplitudejs){rel=""nofollow""}. Amplitude Hosting allows you to create a config file to load into your Amplitude player through a URL. The endpoint can get complicated to compute, but doesn't change very often. We want to load it through a URL structured like `/api/v1/configs/{key}`. Our end game being to return a JSON object with our config pre-computed and up-to-date resource from the cache. All without touching our database. Let's get started!
## Caching a Resource Endpoint
The first step is to save your resource to a cache, otherwise you will have nothing to return! Let's pretend we just persisted our Amplitude Config to the database. After you persist your resource to the database, then it's time to cache it.
As I mentioned previously, we will be using the `Illuminate\Support\Facades\Cache` facade provided by Laravel to work with our cache. This facade will work with whatever caching system we have in place.
So in our controller or service, include the following in your use statements:
```php [Import Cache Facade]
use Illuminate\Support\Facades\Cache;
```
I like to keep my controllers small, so I created a service that saves our config. Right now, it looks like:
```php [Initial Config Service Class]
data = $data;
}
public function persist()
{
$config = $this->saveToDatabase();
return $config;
}
private function saveToDatabase()
{
// Save config to database
}
}
```
Let's add our cache functionality now:
```php [Config Service with Cache Implementation]
data = $data;
}
public function persist()
{
$config = $this->saveToDatabase();
$this->persistInCache( $config );
return $config;
}
private function saveToDatabase()
{
// Save config to database
}
private function persistInCache( $config )
{
Cache::forever('config_'.$config->unique_id, $config);
}
}
```
Two things to note. First, we persist to the cache after the resource has been saved to the database. This way we can return the resource exactly how it would appear from our database, except without the query.
Second, in our `persistInCache()` method, we call the `Cache::forever()` method on the `Cache` facade. The `Cache::forever()` method accepts 2 parameters. The first parameter is the unique id of the key within the cache. In this case it's `config_{unique_config_id}`. This way we know how to reference the key when returning from the cache.
The second parameter is the `$config` itself. This is the data we will return from the cache. Since we used the `Cache::forever()` method, the cache will remain until it's explicitly deleted. This works perfect for our use case.
There are actually a few methods you can call depending on your use case. `Cache::put()` allows you to define a `key` and a `value` but also accepts a third parameter of `expiration_time` . The `expiration_time` can be either the amount of seconds the value should live in the cache or the `DateTime` instance of when the cache should expire.
The other option is `Cache::add()` which is the same as `Cache::put()`except only adds if the value does not exist. You can read more about the [methods here](https://laravel.com/docs/9.x/cache#storing-items-in-the-cache){rel=""nofollow""}. Now that we have our Config stored in the cache, let's return the value!
## Returning Data from a Cached Endpoint with Laravel
The simplest way to return a value from the cache is to use the `Cache::get()` method. The `Cache::get()` method accepts the key of your cached item and returns the value. So in our case, on our `/api/v1/configs/{key}` endpoint, we would return the value at `Cache::get('config_{key}')`. Our service to load the config from the cache could look like this:
```php [Basic Cache Loading Service]
key = $key;
}
public function load()
{
return Cache::get('config_'.$this->key);
}
}
```
The service accepts the key of the config and loads it dynamically from the cache without touching our database (we did not update the typehint for the `$config` to load from the `$key` variable or it would have hit our database).
This is great, but there are instances where your cache is out of sync like after a reboot, or you need to rebuild and the value may not exist. So I like to return my cached values like this:
```php [Advanced Cache Loading with Fallback]
key = $key;
}
public function load()
{
if( Cache::has('config_'.$this->key) ){
return Cache::get('config_'.$this->key);
}else{
return Cache::rememberForever('config_'.$this->key, function () {
$config = Config::where('unique_id', '=', $this->key)
->first();
return $config;
});
}
}
}
```
It looks slightly more complex, but it's actually fairly simple and elegant, let's dive in!
First, we check to see if the `Cache::has()` a config with the key specified. If it does, we simply return the config at that key. Second, if there is no config at the key, we run the `Cache::rememberForever()` method, passing the key and the config function. This is an EXTREMELY helpful method. The first parameter should look familiar since it's the key that we want to cache the value at. The second parameter is a callback function that loads the value. In this case, it's our Config resource. After we load the value, set the value at the key and return what was loaded from the database. Yes, it's a database call, but it only happens once so any sub-sequent requests will be cached appropriately! This way you cache is never out of sync! Unless, you update the value of the config, in that case, see the next section.
## Updating a Cached Endpoint with Laravel
In our scenario, configs aren't updated often, but they are updated. When they are updated, we need to "re-prime" the cache with the latest and greatest data so it's ready to go. It's pretty simple. After you persist the updates to the database you can prime the cache with the same method as saving the cache. It will just overwrite the value:
```php [Cache Update Example]
Cache::forever('config_'.$this->config->unique_id, $this->config );
```
Now when we call our endpoint, `/api/v1/configs/{key}`, we will get the latest version of the config without calling from the database. This will speed up responses dramatically.
## Deleting a Cached Endpoint in Laravel
Say you changed your structure and no longer want to save a cached config. Or the user deletes the config and you don't want it cached any more. All you have to do is call the `Cache::forget('config_{key}')` method wherever you want to delete from the cache. This will remove the the value from the cache and you are good to go!
## Conclusion
Some other benefits of caching endpoints with Laravel is it's so easy to switch caching services. Just swap out your config and your cache will go somewhere else without updating the code. You might have to "re-prime" your new cache but at least you won't have to re-factor a ton of code.
You can also cache values that may not be endpoints, but take a long time to compute. For example, we are working on "Net Worth Over Time" and monthly stats with [Financial Freedom](https://github.com/serversideup/financial-freedom){rel=""nofollow""}. Those can take a long time to compute if you have a ton of transactions. However, we can compute them when updated and save them to a cache to load them instantly when needed. You can even cache them after a queue process so your app is smooth and responsive!
Hopefully this helped shed some light on how to speed up API endpoints with Laravel and Caching. Any questions, reach out on Twitter ([@danpastori](https://twitter.com/danpastori){rel=""nofollow""}). Also, check out our [book on API Development](https://serversideup.net/products/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/). We have a ton of tricks and real world code available and the API is 100% Laravel!
# Capturing an image from an HTML5 Canvas or Video Element
Ever want to save your HTML5 canvas element as an image? You totally can! I've done this a few times to solve a variety of different issues. With this trick you can capture a screenshot from a video, save a rendered graphic, pretty much anything within the canvas element.
## Step 1: Make a Reference To your Canvas Element
There are two approaches to this. First, you may have a canvas element that actually appears on the screen. It will live within your HTML and will display on the screen. The second approach is to have the canvas element represented simply as a javascript object. I've used this approach to capture screen shots of HTML5 video elements.
If your canvas is within HTML, make a reference to it in your Javascript like this:
```javascript [Reference canvas element from HTML]
let canvas = document.getElementById('screenshot-canvas');
```
You can obviously use whatever reference way you choose to select your canvas from the screen.
You can also just have a canvas object behind the screen that can be used to capture what's within. I just set it up like this:
```javascript [Create canvas element programmatically]
let canvas = document.createElement('canvas');
```
## Step 2: Capturing your Canvas Element as an Image
Here's where the magic takes place. I create a method that captures the element like this:
```javascript [Convert canvas to image data URL]
let image = canvas.toDataURL('image/jpeg');
```
With `canvas` being the variable of your HTML5 canvas element we set up in step 1. That's all there is to it! You now have a capture of whatever is in the canvas as an image! You can set the `image` variable to the `src` of any image element and you will be good to go.
One thing to note. The size of your image will be the size of your canvas. This is much easier to see if the canvas is on your screen. However, if you are using a Javascript object, you need to set the `width` and `height` of the element like so:
```javascript [Set canvas dimensions]
let canvas = document.createElement('canvas');
canvas.width = 1920;
canvas.height = 1080;
```
## Taking a Screenshot of an HTML5 Video Element
One of the coolest features of this script is you can take a screen shot of an HTML5 video element using this as well. You just have to pipe the video to the canvas element. Just a heads up, this will work better if the canvas you are referencing is a javascript object, otherwise you will have 2 videos on your screen.
To do this, set up a reference to your HTML5 Video Element. I'm going to assume this element lives on your page. You can however, do the same object instantiation of the video element and hide both the canvas and video element and just output a screen shot. If you are interested in those examples, reach out on the [community forum](https://community.serversideup.net){rel=""nofollow""}.
Let's first set up the reference to our HTML5 video element:
```javascript [Reference video element]
let video = document.getElementById('my-video');
```
Once again, you can use whatever method you want to reference your video. We just need a reference to the element.
Now that we have our video, let's tie everything together. For best results, I'd recommend keeping the aspect ratio of your canvas to the size of your video. It doesn't have to be the full size if you want smaller, scaled images, but you also don't want any funky resizing.
```javascript [Capture video frame to canvas]
let canvas = document.createElement('canvas');
let video = document.getElementById('my-video');
canvas.width = 1920;
canvas.height = 1080;
let ctx = canvas.getContext('2d');
ctx.drawImage( video, 0, 0, canvas.width, canvas.height );
let image = canvas.toDataURL('image/jpeg');
```
That's all there is to it! The meat of the script is to create a "2D" context and draw an image to that context of the video. This will take whatever frame is currently being displayed on the video and capturing it as an image.
## Capturing an HTML5 Video Frame at a Timestamp
If you want to capture a frame at a certain timestamp in your video, there's one little gotcha. You have to use the `seeked` callback to make sure your video has finished seeking before you capture the frame.
All you have to do is modify your code to look like this:
```javascript [Capture video frame at specific timestamp]
let canvas = document.createElement('canvas');
let video = document.getElementById('my-video');
let image = '';
video.addEventListener('seeked', function(){
canvas.width = 1920;
canvas.height = 1080;
let ctx = canvas.getContext('2d');
ctx.drawImage( video, 0, 0, canvas.width, canvas.height );
image = canvas.toDataURL('image/jpeg');
});
video.currentTime = TIME_IN_SECONDS;
```
Now whatever you set for the current time on the video in seconds, the video will seek to that location and take a snap shot.
Hopefully this helps! It's kind of a neat feature of the HTML5 canvas element that you can save it as an image. If you have any questions, definitely feel free to ask on the [community forum](https://community.serversideup.net){rel=""nofollow""}, I'd be happy to help!
# Choosing Your Services and Database
The goal of creating a self-hostable application is, well, allowing it to be self-hosted. That means also allowing *others* to self host your app. It has to be straight forward to do and maintain. As mentioned in the previous article, [Importance of Repeatable Environments](https://serversideup.net/blog/importance-of-repeatable-environments/){rel=""nofollow""}, we use Docker for everything and recommend you do the same. However, there are still some considerations when choosing the database and what services you include.
## Basic Considerations
The power of Docker is amazing. You can add an extremely powerful full text, Meilisearch container to your project with minimal set up time. Configure your Docker compose file and you are off to the races.
However, that Meilisearch container requires a lot more resources, disk space, and maintenance. Even if you are licensing your self-hostable app to a business, it might be overkill for what they are using. They might only have 10-15 users and want the quickest solution to get up and running.
But that's one consideration. What if you want to re-use the same code for a cloud hosted version that does have a million users? These type of architecture and user decisions really have to come into play when designing your app. So what do you do? Well, there are options.
## Service Options (Queues, Search, etc.)
The consideration for full text searching is similar for other processes as well. When we approached designing [Bugflow](https://bugflow.io){rel=""nofollow""} we ran into this issue with our queues. We wanted to keep our self-hosted version as small as possible so it's easier to distribute, maintain and deploy on their own hardware. Why? Because the use case for our customers who want to self-host won't run into the same usage scenarios as we do (if they do, we offer an enterprise self-hosted version).
### Architecture
For reference, before moving forward, we use [Laravel](https://laravel.com){rel=""nofollow""} to build our apps, and they make these next steps a breeze. All connection information for services is handled through the ENV file. I'm sure other frameworks have this as well, but the beauty comes from the abstraction layer above. If we dispatch a queue job, like we do for every feedback request, Laravel reads the configuration connection and dispatches the job where it needs to go. If you wanted to use SQS, Horizon (Laravel's powerful queue runner), or just a database, the job gets routed where it needs to go.
If you aren't using a framework that supports this architecture, I'd recommend developing something similar for your app. I'd recommend a data transfer object, a contract, and some env configuration that allows you to easily swap out what service you need depending on infrastructure and usage requirements.
### How We Solved This
With Bugflow, on our cloud hosted version, we need to process multiple simultaneous queue jobs to ensure everything gets processed as quickly as possible. For self-hosted instances, we just use the database. This is all managed from our environment variable. That means we aren't bringing up an unnecessary server for a use-case that our customers don't have. It also means that users paying for our SaaS get the benefit of speedy feedback on our Horizon Queue.
You can also solve this similarly for full text searching depending on the database you choose (see next section).
## Database Options
With Docker, the database is, guess what, another service and container! This means that when you bring up your app, you might have to bring up another service as well. However, the database is slightly different and depending on your use case, you could possibly remove the container entirely.
Let's say you want to get an application as small as possible to make it as simple as possible to distribute. You need full text search, queuing, and a database. Forget cloud for now, you can do whatever you want since you manage the infrastructure, let's focus on self-hosting.
The first thing I'd do is move all queued tasks to a "jobs" table on the database. This will eliminate the need for a queue runner and another overkill container. Next, I'd choose PostgreSQL or another database that supports full text indexing. You can then eliminate the huge overhead of another full text search engine running in your app. This is just a consideration! If your app heavily relies on natural language processing or super advanced indexing, this might not be worth it. However, you'd be surprised how far you can get!
If you really want to cut down and won't have an app that requires an insanely powerful database, you can even use SQLite. This removes the need entirely for even a database container! You could literally get down to deploying just your app code to self-hostable instances. We actually do this with Bugflow as well. On self hosted instances, SQLite runs our database and our queues. It makes our deployment extremely light weight and easy to run anywhere.
## Building for Cloud and Self-Hosted
Now you've heard me talk about how we deploy to self-hosted environments and to cloud using the same code base. How does this work? Well, if you use Docker, it's a lot easier than you may think!
The most important pre-requisite to make your life easier is to allow your databases and services to be configurable and not hard coded. I mentioned this earlier, but it's even more important if you want to re-use your code and scale in the cloud while also keeping your footprint small in a self-hosted environment.
So how we do it with Bugflow and Docker is straight forward. All the configuration for our services and databases is held in the environment variables that are required for the app to run. The actual server configuration is set up in our `docker-compose.yml` file. This means, when we set our queues to run on Laravel Horizon, we can configure the service to run on Horizon container in production. The Horizon container comes on line when we bring up our app.
In the self-hosted version, we just have a database configuration file. We set our queues to run in the database. The combination of what services get deployed + how we access them are all managed through the environment variables and what gets brought up through Docker!
## Need A Hand?
If you have questions about how to architect your application to make it easy to self-host [or want us to do it for you](https://serversideup.net/hire-us){rel=""nofollow""}, feel free to reach out. We've done the same approach for multiple clients that we've worked with and are well-versed in the process. Hop on [Discord](https://serversideup.net/discord/){rel=""nofollow""} and we'd love to lend a hand!
# Cisco IOS Command Cheat Sheet for Routers and Switches
I've been receiving numerous questions about programming Cisco Routers and Switches. This document contains my notes from when I had to program a Cisco Router running Cisco IOS. While primarily focused on routers, most commands are also applicable to switches.
The commands in this cheat sheet are organized into two modes:
- Enable mode
- Config terminal mode
Make sure to use the commands according to the mode you're currently in.
This Cisco IOS Command Cheat Sheet is valuable for both advanced users and beginners. If you have any questions, please leave a comment!
## Enable Mode Commands
| Command | Description |
| ------------------------------------ | -------------------------------------------------------- |
| `write erase` | Erases config file |
| `reload` | Reloads config files |
| `enable` | Enters console mode |
| `config t` | Enters config mode |
| `clock set 10:50:00 Oct 26 2009` | Sets clock to 10:50AM and the date to October 26, 2009 |
| `show ip interface brief` | Shows interfaces |
| `copy running-config startup-config` | Copies running config and saves it to the startup config |
## Config T Mode Commands
| Command | Description |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
| `enable secret ` | Sets enable secret to password of choice |
| `enable password ` | Sets enable password to password of choice |
| `line con 0`:br` password `:br` logging synchronous`:br` exit` | Sets console password to password of choice and prevents router from displaying annoying messages |
| `line aux 0`:br` password `:br` exit` | Sets AUX port password to password of choice |
| `line vty 0 4`:br` password `:br` exit` | Sets Telnet port password to password of choice |
| `service password-encryption` | Enables password encryption |
| `hostname ` | Sets name of router to name of choice |
# VueJS Route Security and Authentication
In our last tutorial [Public and Private API with Laravel](https://serversideup.net/blog/public-private-api-laravel/), we converted the Laravel API Backend to be accessible for both an authenticated user and an unauthenticated user. This tutorial will go through the process of securing pieces of data that we don't want the user to to view if they are not authenticated.
Before we make any crazy claims it's very possible for users to just inject Javascript and tweak all sorts of things. We don't want to rely 100% on the security of front end JS to secure our data. We just want to block actions from users. Our server and API will handle the hardcore security like preventing users from inserting or updating data.
## Step 1: Add Redirect so Layout is Never in View
We are using the `/` route in VueJS to act as a global layout for all of our other routes. However, this is still a navigable route. We want to redirect this to our cafes home page.
Open up the `/resources/assets/js/routes.js` file and add the redirect key to your `routes` object underneath the path we are using for layout:
```javascript [Route configuration with redirect]
{
path: '/',
redirect: { name: 'home' },
name: 'layout',
component: Vue.component( 'Layout', require( './pages/Layout.vue' ) ),
children: [
```
Now whenever anyone visits this page, they are redirected to the home screen.
## Step 2: Create Modal for Logging In
Since we removed the login route from the PHP side in the last tutorial [Public and Private API with Laravel](https://serversideup.net/blog/public-private-api-laravel/), we are now allowing our social login through a modal that we can call from anywhere. This way every thing is contained in our single page application.
First, let's create the `/resources/assets/js/components/global/LoginModal.vue` component and enter the basic component stub:
```vue [Basic LoginModal component structure]
```
This component will essentially just provide links to our `/login/{social}` url. The structure, we will have copied from our now deleted `login.blade.php`. First, let's fill out the template with:
```vue [LoginModal template with social login links]
```
What this does is wrap a `login-box` element with a `login-modal` element. Each link in the login box allows the user to log in with whatever social network that they want similar to what we had before. One thing to note about those links is they have an attribute of `v-on:click.stop=""`. What this does is stop the propagation so the event stops after the user clicks. This is because the `login-modal` will close on a click and we don't want the modal to close on a child click.
Now let's add our functionality:
```javascript [LoginModal component script with event handling]
```
The two important features of the functionality are 1. The `show` variable in the `data()` and 2, the `prompt-login` event we listen to. The `show` data allows us to toggle the show/hide of the modal. When set to `true` the modal is shown. When set to `false` the modal is hidden.
The `prompt-login` event we listen to toggles the `show` to either true or false to show the login.
Now to style the form, we will use some of the same styles as we did on the `login.blade.php` . Add these styles to the `
```
## Step 3: Set Up LoginModal.vue
Now that we got our login modal created we need to set it up. First open up your `/resources/assets/js/pages/Layout.vue` file. We will be importing our login modal here so add to the top of the component script:
```javascript [Layout component imports]
import LoginModal from '../components/global/LoginModal.vue';
```
Then we will need to register it in our `components` object:
```javascript [Layout component registration]
components: {
Navigation,
AppFooter,
LoginModal
},
```
and place it right before the `` component in the template:
```vue [Layout component template]
```
We placed it in the `Layout.vue` template so we can call the login screen from any part of the app.
Now we need to open the `Navigation.vue` component. In this component, we will display a login link if the user isn't logged in. In the template of this component, adjust the code for the right side navigation:
```vue [Navigation component template with conditional rendering]
LogoutLogin
```
What this does is determine if we should display the avatar if the user is logged in and a logout button or if we should display the login button.
We also need to import the `EventBus`:
```javascript [Navigation component EventBus import]
import { EventBus } from '../../event-bus.js';
```
This way when the user clicks the `login()` link, we can emit the `prompt-login` method which opens our login modal.
Our `login()` method looks like:
```javascript [Navigation component login method]
login(){
EventBus.$emit('prompt-login');
},
```
We also added a logout method that dispatches an action to our Vuex state to clear any data referring to the user. Our logout method looks like:
```javascript [Navigation component logout method]
logout(){
this.$store.dispatch('logoutUser');
window.location = '/logout';
}
```
When we visit the `/logout` route our Laravel side of the application will log out the user and redirect them back to the application. We can also listen for the `logoutUser` event in any Vuex module and clear any data we don't want saved after the user has logged in.
I added the action to `/resources/assets/js/modules/users.js`:
```javascript [Vuex logout user action]
/*
Logs out a user and clears the status and user pieces of
state.
*/
logoutUser( { commit } ){
commit( 'setUserLoadStatus', 0 );
commit( 'setUser', {} );
}
```
## Step 4: Adjust Views Based On Authentication
There are a few views we have to adjust based on whether the user is authenticated or not. We need to hide the add button on the cafes homepage, the edit but on the individual cafe page and the like button on the individual cafe page.
First, let's open our `Home.vue` component. At the top of the page, adjust the template to look like:
```vue [Home component template with conditional rendering]
+ Add CafeWant to add a cafe? Create a profile and add your favorite cafe!
```
What this does is hide the add cafe button if the user is not authenticated. Now this doesn't prevent the user from typing in the URL and visiting the page. We will be dealing with Vue Router Navigation Guards in the next tutorial.
Now let's open the `Cafe.vue` page. I wrapped the `Edit` link in a container and hid it if the user is not logged in:
```vue [Cafe component edit link with conditional rendering]
Edit
```
I also added a toggle for the like button if the user is not logged in. It prompts the user to log in by dispatching the event to login to the Login modal. Our template update should look like:
```vue [Cafe component like button with conditional rendering]
Did you know you can "like" this cafe and save it to your profile? Just log in!
```
and we should add the `login()` method to the methods object on the page:
```javascript [Cafe component login method]
/*
Defines the methods used by the component.
*/
methods: {
login(){
EventBus.$emit('prompt-login');
}
}
```
This way we will be able to show the login modal when we click the link to log in.
## Conclusion
We now have cleaned up some of the VueJS front end and accounted for both public and private routes, we will add some navigation guards in the next tutorial. This will make sure that users do not navigate to pages they don't have access to when they are not authenticated. This combined with the changes we've made on the Laravel side of things make sure no data is leaked. Even if they were to inject javascript to view a page to add a cafe, we already block the API from adding the cafe if submitted.
Be sure to check out all of the code here: [GitHub - serversideup/roastandbrew: Helping the coffee enthusiast find their next cup of coffee. Also, helping aspiring web and mobile app developers build a single page app and converting it to a mobile hybrid. All tutorials can be found at https://serversideup.net](https://github.com/serversideup/roastandbrew){rel=""nofollow""}
# Collections, Blueprints, and Entries with Statamic 3
To be up front, this tutorial is guided at users who are entirely new to Statamic. The documentation on all of these concepts is incredible. Where I stumbled was just tying everything together. I'm also coming from a WordPress background and it really being the only CMS I know. I've been developing Laravel apps for about 8 years now and is one of the reasons I was so interested in Statamic. So what my intent is with this tutorial is to explain these aspects through my understanding. I will relate these to anyone coming from a WordPress background to hopefully bridge that gap.
## Collections
Let's start with collections. The [official documentation](https://statamic.dev/collections){rel=""nofollow""} is beautiful and well laid out. I'm going to explain how I grasped this concept coming from a WordPress background.
Coming from WordPress, these would be your "posts" or custom post types. In WordPress, you get a few post types out of the box such as Posts and Pages. However, when switching to Statamic, you won't get these defaults, unless you follow the [Quick Start Guide](https://statamic.dev/quick-start-guide){rel=""nofollow""}. Then you will have a "Pages" collection. If you just install Statamic to a Laravel install, you won't have any collections by default. That's the route I took when I first started Statamic.

So in order to have a place to write a post, article, tutorial, etc. you have to create a collection to hold those entries (what we will talk about next). My mindset when approaching a Statamic site is you are given a blank canvas where you can easily design the structure of your data, how it relates to each other, and how to display it. With WordPress, you get some out of the box, pre-configured ideas and you can use those to build what you need.
If I was going to re-build this blog (which is currently running WordPress) in Statamic, the first step I'd take is to create a collection called "Tutorials". Currently, We have a Post "post type" by default in WordPress where each of these tutorials live. Tutorials is more explicit to what we will be writing. We use a "Post" because that's what we get out of the box with WordPress.

Now that we have our collection, let's create some entries.
## Entries
Entries belong to collections. Or, from the [official docs](https://statamic.dev/collections){rel=""nofollow""}, "Collections are containers that hold groups of related entries." Entries in Statamic are the equivalent to an individual Post in WordPress. If you are really familiar with WordPress you could have created custom post types. An individual creation of a custom post type in WordPress is the equivalent to an entry in Statamic. The custom post type itself would be the collection.
When creating your first entry, you should see a few basic fields:

Almost inception like, isn’t it? For this tutorial, I’m going through the process of updating Server Side Up with Statamic as an experiment. Compared to what you get out of the box with WordPress, there are minimal fields. This is where the platforms really diverge and where I feel Statamic becomes EXTREMELY powerful. Yes, you only have a title, content, slug and status (published) as editable fields right away with Statamic. However, if you want to add fields super easily, you just have to adjust the blueprint (we are going there next). To add a field to a post, or custom post type in WordPress, you’d have to edit the theme code or install the Advanced Custom Fields plugin and create your extra field. With Blueprints, you just have customization options that are incredible and extremely powerful. Let’s take a look at those.
## Blueprints
If collections are equivalent to "posts" or custom post types in WordPress. And Entries are the individual post that belongs to a custom post type in WordPress. Blueprints would be the custom meta fields added by your custom theme, custom plugin, Advanced Custom Fields plugin, or other 3rd party extension. To add a custom field to a post type, or even make a custom post type, requires a lot of code and proper hooks into the WordPress loop. Then you have to handle the updating of this data and loading it in your template. With Statamic, if you want to add a custom field, you need to edit the Blueprint.
There are two ways to edit your blue print. First is on the left side bar by clicking "Blueprints" and finding the collection blueprint you’d like to edit. Otherwise, if you are on your collection, select "Edit Blueprints" from the dropdown.

From there, you will be brought to a list of the available Blueprints in your Statamic instance. If you click on the name of the collection, in our case "Tutorials", you will see a page where you can layout what the user will see when adding an Entry to your collection:

Now all of this is heavily documented in the Statamic documentation, including the [field types](https://statamic.dev/reference/fieldtypes){rel=""nofollow""} you can add to a collection. There are so many to choose from. After years of doing WordPress dev and having to link custom javascript to get advanced field functionality, this is a breath of fresh air. You can extend your collection extremely easily with a ton of options, natively supported out of the box.
## Conclusion
As I’m just getting started on my Statamic journey, I can already see so much I love about the CMS. First and foremost is the power to do amazing customizations, while staying out of the way and letting you make the decisions on implementation. I’m still learning a lot, but will be writing tutorials along the way. This one is very basic, but I hope this helps clear up some of the confusion I had when I first started! I plan to write a ton more as I take notes myself on certain features I'll be using. If you have a subject you’d like to see me cover, let me know on our [community](https://community.serversideup.net){rel=""nofollow""} or reach out on [Twitter](https://twitter.com/danpastori){rel=""nofollow""} and I’ll see what I can do!
# Configure a secure guest wireless network using VLANs, firewalls, and throttling
Learn how to create a secure guest wireless network that prevents guests from bringing malicious activity to your network. You'll also learn how to throttle the guest network down so that you do not worry about your guests using up all of your bandwidth.
See the entire "Complete Ubiquiti UniFi + Synology Network Build" course here: {rel=""nofollow""}
# Configure Stripe to Work with Laravel Cashier in Laravel 6
So we have [Laravel Cashier installed](https://serversideup.net/blog/installing-laravel-cashier-on-laravel-6-x/), now it's time to set up Stripe so we can actually bill for the app's services. This is where I felt there was a gap in documentation on the web. Laravel has their side documented beautifully, Stripe also has beautiful documentation, but they were kind of in separate columns. I hope this helps to merge the two together and make the billing process a breeze!
### Pre-Requisites
A functioning Stripe account.
## 1. Log Into Stripe
You should have at least signed up for Stripe and created an account. From there, it's pretty easy to create a "New Account" which would be your product. In our case, we have a sign in for multiple accounts, so I had to create a new account for the app I was making. Either way, you will need an account for the name of your app.
Once you log into the dashboard, you will see the account you are on in the top left Corner: 
## 2. Grab Your API Keys
This is the key (pun intended) to binding everything together. To do this, look at the bottom left of the dashboard and find `Developers`. Click that link.

When you open up that screen, you will see a sub menu called `API keys`. This is where we will grab our keys from. We will be doing a lot more work in this part in the upcoming tutorials when we discuss webhooks and events.
Once you land on the `API keys` page you will see 2 keys, a Publishable key and a Secret key. There are 2 sets of these keys, `Live` and `Test`. For this tutorial, we will be using the `Test` keys. MAKE SURE that you swap these out for the `Live` keys when you push your product to production or you WILL NOT get paid!

Make sure you keep your secret key secret! This is what gets passed along server side with your requests. The publishable key you will place in your Javascript file to use the Stripe.js javascript framework. We will get into that more when we start working with Stripe Elements.
## 3. Place API Keys in your .env
Now that you have your keys, go back to your Laravel install and open up the `.env` file in the root of your app. Place both of these keys in the placeholders we set up in the last tutorial. Your `.env` file should look like this:
```text [Stripe ENV Variables]
CASHIER_MODEL=App\User;
STRIPE_KEY=pk_test_TEST_KEY_RANDOM_STRING
STRIPE_SECRET=sk_test_SECRET_KEY_RANDOM_STRING
```
Once you have these in your `.env` file, you should be ready to continue the Stripe setup! In the next tutorial, we will go into the process of creating your subscriptions!
# Configuring Axios Globally with VueJS
In the [last tutorial](https://serversideup.net/blog/using-axios-to-make-api-requests-with-vuejs/), we went through installing Axios on VueJS and NuxtJS. We also made our first requests with each set up! However, there was one glaring difference between the VueJS and NuxtJS setups that will exponentially grow as you develop. That difference is the global access to Axios within NuxtJS compared to importing it with VueJS.
If you are using NuxtJS, this won't be of any use to you since global access is already available. I'd move on to the next tutorial where we submit some data!
## Why is this a big deal?
Simply put, the less you need to repeat yourself, the better. You don't want to have to import Axios every time you want to use the library. Let alone, when you need to configure all of your requests to have certain headers, or intercept errors and gracefully handle them, doing this EVERY time adds up.
I'm not for putting every library used in a global set up. As a matter of fact, I'm against it most of the time. However, Axios is an exception. There's global config and if you are building a Single Page Application, you will be making API requests all the time!
Luckily, this is fairly straight forward, so let's get started!
## Set Up
If you need to still install Axios within VueJS check out [Using Axios to Make API Requests With VueJS](https://serversideup.net/blog/using-axios-to-make-api-requests-with-vuejs/). If Axios is already installed, let's find the root javascript file where you set up your VueJS install. For me this is usually in a `/resources/js` directory and named `app.js` or `index.js`. Whatever it's named, find that file.
In the file, you will simply need to add the following line:
```javascript [Require axios globally]
window.axios = require('axios');
```
That's really it! What this did was bind the axios variable to the `window` variable which gives us access to the functionality within any VueJS component! This may look familiar if you have been using VueJS with Laravel. By default, Laravel sets up the VueJS install similarly on the front end to work with their framework.
You now can use `axios.get()` or `axios.post()` from within any component without having to import it every time.
## Other Global VueJS Axios Options
Even though that's my preferred method, there may be other ways you choose to structure your VueJS app. Honestly, this is why I prefer NuxtJS because it's an opinionated way on how to set up the VueJS ecosystem and extend it with other modules. And it makes a ton of logical sense!
The other options I'd consider are dependent upon what kind of app you are creating. If you will be using lots of request transformations (usually when working with multiple APIs) or working with various authentication methods, I'd add all of this in a separate file. This will make sure your VueJS initial file stays clean and easy to read.
## What's Next?
There's a lot! It's nice to have Axios globally, but even that won't be enough for larger applications. You will not only be re-using axios, but also API requests. I'll show you have to abstract those requests into nice re-usable modules with VueJS and NuxtJS.
However, next up, we will be going through a quick tutorial on how to send data to your API through a POST/PUT/PATCH request.
# Configuring JS and SASS for a Single Page App
So far we haven't made any front end changes. Our app so far is simply just a bunch of back end tools and configuration. We installed Laravel (/installing-configuring-laravel-spa/), configured Laravel Socialite ([Installing And Configuring Laravel Socialite - Server Side Up](https://serversideup.net/blog/installing-configuring-laravel-socialite/)), and configured Laravel Passport (/installing-configuring-laravel-passport/). Now we will begin structuring our front end for our app.
Once again Laravel comes in clutch with Laravel Mix [Compiling Assets (Laravel Mix) - Laravel - The PHP Framework For Web Artisans](https://laravel.com/docs/5.6/mix){rel=""nofollow""}. You don't have to worry much about any of the complex Webpack configurations, sass configurations, or any of the build tools. We will still be using NPM, but Laravel mix will handle all of the building and compiling. Developers can even use mix outside of Laravel which is awesome!
## Step 1: Inspect /webpack.mix.js
Laravel ships with a default configuration for Laravel Mix. If you visit `webpack.mix.js` in your root directory you will see a file that looks like:
```javascript [Default Laravel Mix Configuration]
let mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.js('resources/assets/js/app.js', 'public/js')
.sass('resources/assets/sass/app.scss', 'public/css');
```
For our app this will solve our needs. What this is doing is requiring the 'laravel-mix' NPM module in the first line.
The next group of code is the magic of Laravel Mix. It simply chains together a mix method. In mix.js, the first parameter is the location of your app.js file and the second is where that will compile and export to which will be the `public/js` directory. What Laravel Mix will do is use the `resources/assets/js/app.js` file as the entry point and build out a file named `app.js` and dump it in the `public/js` directory.
The sass command operates the exactly same way as the .js method. It takes the entry point from `resources/assets/sass/app.scss` and dumps a compiled css file to named `app.scss` to the `public/css` directory.
These we will keep the same since it will be easy to maintain. More complex apps can run multiple mix commands or change the directory, file names, etc.
## Step 2: Inspect package.json
First we will inspect the `package.json` file in the root directory of the app. You should see something like this:
```json [package.json]
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"axios": "^0.16.2",
"bootstrap-sass": "^3.3.7",
"cross-env": "^5.0.1",
"jquery": "^3.1.1",
"laravel-mix": "^1.0",
"lodash": "^4.17.4",
"vue": "^2.1.10"
}
}
```
This is preconfigured from your Laravel install and contains a few scripts and devDependencies. When building your app on your development machine, you will want to run either: `npm run dev` or `npm run watch`. I prefer to open a terminal window and start `npm run watch`. This will watch your JS/SCSS files and whenever you save a change it will re-compile your changes to the appropriate file. When you want to run production, simply run `npm run production` and your assets will be minified and ready for production use.
As for some of the packages, we will use all of what is in the devDependencies object except "bootstrap-sass". We will be removing Twitter Bootstrap in favor of Zurb Foundation [The most advanced responsive front-end framework in the world. | Foundation](https://foundation.zurb.com/){rel=""nofollow""}
Axios is an awesome HTTP client for JS and works seamlessly with Vue. It's documentation can be found here: {rel=""nofollow""}.
jQuery will be used by foundation, and of course vue will be the center point of our SPA.
## Step 3: Remove Bootstrap (optional)
If you want to follow along completely with our tutorial, run the following commands first to remove bootstrap: 1. `npm remove bootstrap --save-dev` 2. `npm remove bootstrap-sass --save-dev`
These will remove all of the Twitter Bootstrap functionality.
Next go to your `/resources/assets/js/app.js` file and change the remove the following line:
```javascript [Bootstrap Import to Remove]
require('./bootstrap');
```
This will remove the reference the file: `/resources/assets/js/bootstrap.js`. We will be merging what that file contains into our app.js file.
Next go to your terminal window and run `npm install foundation-sites --save-dev`. This will make sure foundation is included in your project.
> **Update 10/12/2017** I found a bug transpiling es5 with foundation and found the solution:{rel=""nofollow""}. The bug happens when running a production build. The solution is below and what your webpack.mix.js file should look like:
```javascript [Updated Webpack Mix Configuration with Foundation Support]
let mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.js('resources/assets/js/app.js', 'public/js')
.webpackConfig({
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules(?!\/foundation-sites)|bower_components/,
use: [
{
loader: 'babel-loader',
options: Config.babel()
}
]
}
]
}
})
.sass('resources/assets/sass/app.scss', 'public/css');
```
## Step 4: Configure app.js
We've got a little bit of work to do to structure our app for scalability. Laravel ships some of the default functionality for starting an SPA in the `/resources/assets/js/bootstrap.js` file. I don't like this file, and since we will be structuring our app in a way to organize Vue components, pages, Vuex modules, I say we will remove this file and merge it into `/resources/assets/js/app.js`
Navigate to your `resources/assets/js/bootstrap.js` file and grab the following line from the top:
```javascript [Lodash Import]
window._ = require('lodash');
```
and add that right away to the top of your `/resources/assets/js/app.js` file.
Next we will grab the following try, catch block and move it right under the last line copied on top of the app.js file:
```javascript [jQuery and Foundation Setup]
try {
window.$ = window.jQuery = require('jquery');
require('foundation-sites');
} catch (e) {}
```
Then change the following line from:
`require('bootstrap-sass')`
to
`require('foundation-sites')`
if you are removing Twitter Bootstrap.
Last, grab the next chunk of code that configures Axios and add that to your app.js file.
Your file will now look like this:
```javascript [app.js]
window._ = require('lodash');
try {
window.$ = window.jQuery = require('jquery');
require('foundation-sites');
} catch (e) {}
window.Vue = require('vue');
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Next we will register the CSRF Token as a common header with Axios so that
* all outgoing HTTP requests automatically have it attached. This is just
* a simple convenience so we don't have to attach every token manually.
*/
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('example', require('./components/Example.vue'));
const app = new Vue({
el: '#app'
});
```
What the axios configuration does is include the X-CSRF-TOKEN to the the headers for each request sent. This will automatically allow access to the API and proper CSRF protection.
Lastly, remove the following lines: `window.Vue = require('vue');` and
```text
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('example', require('./components/Example.vue'));
```
The first line removes Vue which you may be wondering why. Don't worry, in the next tutorial we will add it back in when we install the Vue ecosystem.
The second chunk of lines removes the default Vue component. This we just don't need.
Our `/resources/assets/js/app.js` file should look like:
```javascript [app.js]
window._ = require('lodash');
try {
window.$ = window.jQuery = require('jquery');
require('foundation-sites');
} catch (e) {}
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Next we will register the CSRF Token as a common header with Axios so that
* all outgoing HTTP requests automatically have it attached. This is just
* a simple convenience so we don't have to attach every token manually.
*/
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
const app = new Vue({
el: '#app'
});
```
Notice that Vue is binding to an el: '#app'? That is an element referenced in our `app.blade.php` file that we created in [Installing and Configuring Laravel SPA](https://serversideup.net/blog/installing-configuring-laravel-spa/). Vue will bind to this element and work it's magic with the Vue Router!
## Step 5: Remove Unnecessary JS
Now we remove the unnecessary javascript.
1. Remove: `/resources/assets/js/bootstrap.js` since we merged this into our app.js.
2. Remove: `/resources/assets/js/components` directory. We will re-add some of these directories in the next tutorial
## Step 6: Configure SASS
There are 2 things we need to do to configure SASS for our project.
First, if you are using Foundation, open up `/resources/assets/sass/app.scss` and clear out the contents. Then add the following line: `@import "node_modules/foundation-sites/assets/foundation.scss";`
Your entire `app.scss` file should look like:
```scss [Foundation SASS Import]
@import "node_modules/foundation-sites/assets/foundation.scss";
```
Next remove: `/resources/assets/sass/_variables.scss` . We will use a variables.scss file but not the one loaded with Laravel. We will be using a variables file, but we will place it in a different directory.
## Step 7: Set up SASS directories
SASS makes CSS so much easier, but structuring it by a standard is the icing on the cake. When building an app I follow the 7-1 Pattern for structuring SASS: [Sass Guidelines](https://sass-guidelin.es/#the-7-1-pattern){rel=""nofollow""}
The only difference is I keep app.scss instead of their recommended main.scss file.
To configure the app for the 7-1 Pattern build the following directories: 1. `/resources/assets/sass/base` 2. `/resources/assets/sass/components` 3. `/resources/assets/sass/layouts` 4. `/resources/assets/sass/pages` 5. `/resources/assets/sass/themes` 6. `/resources/assets/sass/abstracts` 7. `/resources/assets/sass/vendors`
In the Sass Guidelines: [Sass Guidelines](https://sass-guidelin.es/#the-7-1-pattern){rel=""nofollow""} it runs through what should be in each directory. This is super helpful when laying out your app. We will be heavily utilizing the components directory to style our web components.
## Conclusion
In the next tutorial we will be focusing on VueJS and all of the sweet Vue ecosystem (Vue-Router and Vuex) extensions. We will build some more folders and get that section structured behind the scenes as well. I also promise that Roast will start becoming more useful in more tutorials. We are just building the foundation for our Single Page App right now, but soon we will be adding routes, displaying data, implementing components, etc.
# Configuring Vue Router for a Single Page App
Single page apps are possible on the web due to the HTML5 history API: [History - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/History){rel=""nofollow""} What the history API does is allow the developer to adjust the history of the web browser without changing pages and can still have permanent URLs for deep linking. Why would developers want this? It allows developers a significant load time change and more control over transitions within their site. Each route only loads the data unique to the route after the initial load. So in a traditional application, if you have a CSS file, a JS file and some images. Those are loaded every time you make a page request. In a single page application, these are loaded initially on the first load and any other images, data, CSS is loaded through AJAX. In the end this saves lots of server requests and makes for a much speedier app. There are some downfalls when building apps like this discussed here: [The disadvantages of single page applications by Adam Silver](https://adamsilver.io/articles/the-disadvantages-of-single-page-applications/){rel=""nofollow""}. I agree with some of the statements, but when building smaller apps like Roast, the code is generally more maintainable and super easy to transfer to a hybrid mobile app which is our end goal. Single Page Applications do a good job of separating the visual display from the data source which is perfect for transferring to mobile.
Now learning the HTML 5 History API can be challenging, but thanks to Vue Router [Introduction · vue-router](https://router.vuejs.org/en/){rel=""nofollow""}, all of the hard stuff is taken care of such as pushing and popping states. And since we are building our app in VueJS, it works seamlessly with everything else in VueJS.
In this tutorial, we are simply going to set up our home route, a cafes list route, an individual cafe route, and a cafe submission route. These will be on our front end only routes and the next tutorial we will add the backend component for the API.
## Step 1: Configure Routes File
We will need to open our `/resources/assets/js/routes.js` file. This will be where we maintain every route in our application. We will need to import Vue and VueRouter first into the file so we can tell Vue that we are using VueRouter to handle the routes in our application. The top of your routes.js file should look like:
```javascript [routes.js]
/*
|-------------------------------------------------------------------------------
| routes.js
|-------------------------------------------------------------------------------
| Contains all of the routes for the application
*/
/*
Imports Vue and VueRouter to extend with the routes.
*/
import Vue from 'vue'
import VueRouter from 'vue-router'
/*
Extends Vue to use Vue Router
*/
Vue.use( VueRouter )
```
First we import Vue from the 'vue' package and then import VueRouter from the 'vue-router' package. Then we instructed Vue to use VueRouter. Pretty simple right off of the bat.
## Step 2: Add Routes
We will need to add 4 routes to our app right off of the bat. The routes will be:
- / -> Home
- /cafes -> Cafe Listing
- /cafes/new -> Add Cafe
- /cafes/\:id -> Display Individual Cafe
The Vue Router documentation is very thorough on how to add routes: [Getting Started · vue-router](https://router.vuejs.org/en/essentials/getting-started.html){rel=""nofollow""}. One thing I like to do is use named routes. What this means is I can reference routes by a specific name instead of typing out the entire path when I write links.
Each route is an object in a routes array that we use when we construct a new VueRouter. Since we have the routes in a separate file, we export the default module so we can import it into our app.js file.
The following is what we should have in our `/resources/assets/js/routes.js` file:
```javascript [routes.js]
/*
|-------------------------------------------------------------------------------
| routes.js
|-------------------------------------------------------------------------------
| Contains all of the routes for the application
*/
/*
Imports Vue and VueRouter to extend with the routes.
*/
import Vue from 'vue'
import VueRouter from 'vue-router'
/*
Extends Vue to use Vue Router
*/
Vue.use( VueRouter )
/*
Makes a new VueRouter that we will use to run all of the routes
for the app.
*/
export default new VueRouter({
routes: [
{
path: '/',
name: 'home',
component: Vue.component( 'Home', require( './pages/Home.vue' ) )
},
{
path: '/cafes',
name: 'cafes',
component: Vue.component( 'Cafes', require( './pages/Cafes.vue' ) )
},
{
path: '/cafes/new',
name: 'newcafe',
component: Vue.component( 'NewCafe', require( './pages/NewCafe.vue' ) )
},
{
path: '/cafes/:id',
name: 'cafe',
component: Vue.component( 'Cafe', require( './pages/Cafe.vue' ) )
}
]
});
```
There's a few things to point out. Each route has a name. For example, the home route has the name of 'home' which we can reference from any place in our app. We also have a key in each route object called 'component'. This defines the Vue component used to render each page. Like I mentioned before, I store these in a special directory: `/resources/assets/js/pages`. The other thing to point out, is that the last route (`/cafes/:id`) notice the '\:id' at the end. This is a dynamic route segment ([Dynamic Route Matching · vue-router](https://router.vuejs.org/en/essentials/dynamic-matching.html){rel=""nofollow""}) which allows us to define a pattern matched ID to dynamically load a specific cafe.
## Step 3: Add Page Components
In Vue Router, all of the pages being rendered are Components. In the last tutorial we created a directory to house these pages `/resources/assets/js/pages`. We need to add the following files:
- `/resources/assets/js/pages/Cafe.vue`
- `/resources/assets/js/pages/Cafes.vue`
- `/resources/assets/js/pages/Home.vue`
- `/resources/assets/js/pages/NewCafe.vue`
Each file should contain just the shell of a Vue single file component which looks like:
```vue [Basic Vue single file component template]
```
If you haven't used Single File Vue Components before, I'd read about them here: [Single File Components — Vue.js](https://vuejs.org/v2/guide/single-file-components.html){rel=""nofollow""}. Simply put they are your JS, HTML, and CSS all scoped within a single file. Since we are using Laravel and Laravel Mix, these compile to the appropriate files out of the box. I find this approach amazing for all apps, single page or not, because it really helps maintain a clean codebase for all components. You can even scope your CSS so it only applies to the component it's registered in.
## Step 4: Import Router into App.js
We now have our routes.js file ready to rock and roll and basic page template components outlined. We are now ready to import our VueRouter into our `/resources/assets/js/app.js` file.
To do that, all we have to do is add
```javascript [Router import statement]
import router from './routes.js'
```
right after
```javascript [Vue import statement]
import Vue from 'vue';
```
Next we will instruct our Vue instance to use the `router` we just imported by adding the `router` to the new Vue instruction like this:
```javascript [Vue instance with router configuration]
new Vue({
router
}).$mount('#app')
```
Our `app.js` file should now look like:
```javascript [app.js]
window._ = require('lodash');
try {
window.$ = window.jQuery = require('jquery');
require('foundation-sites');
} catch (e) {}
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Next we will register the CSRF Token as a common header with Axios so that
* all outgoing HTTP requests automatically have it attached. This is just
* a simple convenience so we don't have to attach every token manually.
*/
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
import Vue from 'vue';
import router from './routes.js'
new Vue({
router
}).$mount('#app')
```
We now define Vue, configure axios and configure the Vue Router so our app has a few pages. Our app still doesn't do too much yet, but we are getting there!
## Step 5: Build
This is just a reminder step that if you haven't been running `npm run watch` then you should run an `npm run dev` to build your app on your development environment.
If you are curious, you can log in socially and visit one of the routes defined. The next tutorial will go through adding the API routes and using Axios for HTTP requests to our API. If you want to take a look at the features of Axios check out their documentation: [GitHub - axios/axios: Promise based HTTP client for the browser and node.js](https://github.com/mzabriskie/axios){rel=""nofollow""}. It's super easy to use and we already have it configured out of the box with Laravel to attach the token so we can access our API. The next tutorials will cover building these API requests in Javascript, building the API endpoints on the Laravel side, then configuring Vuex to store the data we get returned. Once we have that done we can start making the app look pretty and doing some other cool stuff to the app! Let me know if there are any questions, suggestions, or opinions on the structure so far! Remember, you can follow along on GitHub: {rel=""nofollow""}
# Creating Cordova App Icon Sizes the Fast Way
Building a hybrid mobile app can save you a ton of time. But when it comes to making your icons pixel perfect, it is overwhelming with the amount of Cordova app icon sizes that are required to build for each platform. Luckily, there are tools that can help you make this process a lot less painful for you.
What we're going to achieve today is building a file that will create all of the icons needed for us. If I need to update the icons in the future, all I need to do is update one design and it will update the rest of the sizes for me. For this example, I am going to design for two platforms: Android and iOS.
## We can do all of this with Sketch
[Sketch is a design application](https://www.sketchapp.com/){rel=""nofollow""} similar to Adobe Photoshop. If you haven't heard of this before, it's been quite the tool to use amongst the most talented designers. The price is $99/year, but they also have a free trial. It's built for **Mac OS X only**, but it's totally worth the investment if you are doing UI design often. The product is built all around User Interface design (unlike Adobe Photoshop which is geared towards Photographers and static Graphic Designers). Everything you create is in a "vector" format so it makes it easy for your design to scale with all of the crazy pixel densities out there.
## Step 1: Cheat and [download my template](https://drive.google.com/uc?export=download&id=0B8VOU9nEC6Ybdjk3V2ltUWMxSUU){rel=""nofollow""}
Once you have Sketch installed, I made it really easy for you to do make your icons. [Download my template](https://drive.google.com/uc?export=download&id=0B8VOU9nEC6Ybdjk3V2ltUWMxSUU){rel=""nofollow""} and open it in Sketch.
## Step 2: Create your iOS icon
You should be able to toggle the guides on and off. Align everything perfectly and keep your design within the guides. The following abides to [Apple's App Icon Guidelines](https://developer.apple.com/ios/human-interface-guidelines/graphics/app-icon/){rel=""nofollow""}.

## Step 3: Create your Android icon
[Android has it's own set of guidelines](https://material.io/guidelines/style/icons.html#app-shortcut-icons){rel=""nofollow""}. Same as before, keep your design within the guides. I made it all nice and cute for the Android Oreo circular pattern.

## Step 4: Let the magic happen
This is where Sketch becomes **really** powerful. All you need to do now is hit `Export` in the upper right. **Make sure you set the guides to HIDDEN** (otherwise they will appear in your exported icon). Then choose the folder of your Cordova project where you want them placed.

The magic is all happening within the file that I gave you. Sketch allows us to export specific widths.

## Step 5: Update your config.xml
Once you have the images exported into your project folder, all you will need to do is update your `/config.xml` in your Cordova Project to be able to access your images. The [Cordova App Icon Sizes can be found in their documentation](https://cordova.apache.org/docs/en/latest/config_ref/images.html){rel=""nofollow""} for every file size you need (incase you need to add other platforms).

You should now be able to run a `cordova build` and both platforms should build now.
If you enjoyed this article, please let me know by leaving a comment below or hitting me up on Twitter!
# Creating a Stripe Subscription with Laravel Cashier + Laravel Passport
Now we are getting down to business! We have just finished allowing the user to save a payment method with their account ([Managing Stripe Payment Methods in VueJS SPA and Laravel API](https://serversideup.net/blog/managing-stripe-payment-methods-in-vuejs-spa-and-laravel-api/)) and we are now ready for the user to create a Stripe subscription to the plan within our app!
Before we display any options for the plan and what we are allowing the users to subscribe to, we will have to display the possible payment options that they have available to select. Luckily in the last tutorial ([Managing Stripe Payment Methods in VueJS SPA and Laravel API](https://serversideup.net/blog/managing-stripe-payment-methods-in-vuejs-spa-and-laravel-api/)), we have everything we need to get started!
## 1. Allow user to select stored payment option
So we have a simple form where we allow the user to store payment methods. After the payment method is stored, we save it as the default and then load available payment methods for the user to select. The user will have to select one of these to start their subscription.
In the last [tutorial](https://serversideup.net/blog/managing-stripe-payment-methods-in-vuejs-spa-and-laravel-api/) we added the proper API route and VueJS component method to load these payments. Let's open up the `SubscriptionManagement.vue` component and build a simple template to allow these payment methods to be selected. To do that, add the following HTML to the template after the `Save Payment Method` button. We will post the final component's code in the last tutorial to see how it all works.
```vue [Payment Methods Selection Template]
OR
No payment method on file, please add a payment method.
```
A couple things to notice. First, there are a few more variables to add. Those are the `paymentMethodsLoadStatus` and `paymentMethods` variables. Let's get those added to our local `data()` object as well:
```javascript [Component Data Properties]
data(){
return {
...
paymentMethods: [],
paymentMethodsLoadStatus: 0,
paymentMethodSelected: {}
}
},
```
The `paymentMethodsLoadStatus` simply keeps track of where the loading of the payment methods is in respect to the state of the request. We need to update our `loadPaymentMethods()` method to look like:
```javascript [Load Payment Methods Method]
/*
Loads all of the payment methods for the
user.
*/
loadPaymentMethods(){
this.paymentMethodsLoadStatus = 1;
axios.get('/api/v1/user/payment-methods')
.then( function( response ){
this.paymentMethods = response.data;
this.paymentMethodsLoadStatus = 2;
}.bind(this));
},
```
Now, this gets updated with the state of where the payment methods loading is and we display when it equals `2` which means everything is loaded.
The other variable, `paymentMethodSelected` which is initialized to an empty object, allows the user to select a payment method to use when they are paying for their subscription. If you look at this piece of the template:
```html [Payment Method Selection Template]
```
where we iterate over the payment methods, when the user clicks the payment method, it sets the `paymentMethodSelected` to be what they clicked on. This is what we pass to our API to subscribe a user. I also have a simple helper class that when the user selects their payment method, the background is `bg-success` which is green in Bootstrap 4.
Finally, if you look at:
```html [Remove Payment Method Button]
Remove
```
You will see we finally link our `removePaymentMethod()` method to our template, allowing the user to remove a saved payment method. Remember, this was created in the last tutorial [tutorial](https://serversideup.net/blog/managing-stripe-payment-methods-in-vuejs-spa-and-laravel-api/).
## 2. Display the subscriptions the user can subscribe to
Now that we have the payment method to be selected, we have to display the subscription options. Let's first add our template to display the options.
```html [Subscription Plans Template]
Basic
$10/mo.
Professional
$15/mo.
Enterprise
$20/mo.
```
For sake of example, I just made little `div` elements that name the subscription and when clicked set it locally. You just need to record what is selected from the user and send that to the API route we are building.
Let's break this down. These are the 3 subscription plans that we made in [Creating SAAS Products in Stripe to Sell with Laravel Cashier](https://serversideup.net/blog/creating-saas-products-in-stripe-to-sell-with-laravel-cashier/). The first thing to look at is the `plan_XXX` key. We will have to grab those from our Stripe Dashboard under Billing → Products → Product Name → Pricing Plans. Then you click on the Pricing Plan you created and find the plan ID:

Once you have those inserted into your template, add the `selectedPlan` to the local data:
```javascript [Selected Plan Data Property]
data(){
return {
...
selectedPlan: '',
}
},
```
Now when the user clicks the row, the local `selectedPlan` will be set to what the user chose to subscribe to. The user can now select a plan and a payment method. That's what we need for them to subscribe!
**NOTE:** There are multiple ways you could choose to display these plans. Even making an API route to save and load them where you can dynamically display them and adjust their features. That'd involve some extra integration with Stripe outside the scope of this tutorial. This tutorial should provide enough to subscribe to a plan through an API drive app type structure.
## 3. Add route to process subscription
Now it's time to send the request to the API to subscribe a customer! We first need to add a route to our `routes/api.php` file to handle our subscription.
This will be a multi-purpose route that allows the user to update their subscription. Let's add the following route to the API routes:
```php [API Subscription Route]
Route::put('/user/subscription', 'API\UserController@updateSubscription');
```
This will respond to a `PUT` request on `/api/v1/user/subscription`. The reason we have it respond to a `PUT` is it will update the subscription. If the user isn't subscribed, a subscription will be created. If the user is subscribed and they change their subscription it will update them to the new package.
Let's build the handler method in our `UserController.php`:
```php [Subscription Update Controller Method]
/**
* Updates a subscription for the user
*
* @param Request $request The request containing subscription update info.
*/
public function updateSubscription( Request $request ){
$user = $request->user();
$planID = $request->get('plan');
$paymentID = $request->get('payment');
if( $user->subscribed('Super Notes') ){
$user->newSubscription( 'Super Notes', $planID )
->create( $paymentID );
}else{
$user->subscription('Super Notes')->swap( $planID );
}
return response()->json([
'subscription_updated' => true
]);
}
```
So there's **A LOT** of dense functionality in this method, let's break it down!
First, we grab the 3 pieces of data we need to fulfill the request the `$user`, the `$planID` which is what the user is subscribing to, and the `$paymentID` which is the method the user is using to pay for the subscription.
The first thing we check is if the user is already subscribed to our product with the `subscribed()` method added to the user by the `Billable` trait. The parameter is the product name `Super Notes` found under Billing → Products → Product Name in the Stripe Dashboard:

If the user is **NOT** subscribed, we create a new subscription using the `newSubscription()` method that accepts the product name as the first argument and the `$planID` as the second argument. We then chain the `create()` method to the subscription which will create the new subscription and pass the `$paymentID` as the parameter. This will create the subscription and pay for it using the saved payment ID.
If the user **IS** already subscribed to the product, then we `swap` the plans. This allows the user to change the plan they are subscribed to easily and effectively. Stripe will take care of the rest! What we do is find the `subscription` with the name of our product and run the `swap()` method with the new `$planID` as the argument. This is a very simple implementation that you can expand on in a lot of ways by looking at the docs: {rel=""nofollow""}.
That's all we need to do on the API side!
One thing I'd like to clarify is the way we structured our SAAS product in Stripe is one product with multiple plans. You can structure it a variety of ways, just make sure the user is subscribing to the right product through the API and passing the plan associated with that product.
## 4. Add Frontend Subscribe Method
This will be the last piece tying everything together! What we need to do is simply add this method to our `methods` object in the `SubscriptionManagement.vue` component:
```javascript [Update Subscription Method]
updateSubscription(){
axios.put('/api/v1/user/subscription', {
plan: this.selectedPlan,
payment: this.paymentMethodSelected
}).then( function( response ){
alert('You Are Subscribed!');
}.bind(this));
},
```
What this does, is submit a request to our API and alerts the user when they are subscribed! Obviously, upon the successful response, modify it to be a good UX such as an alert banner, but this is the final goal!
Before submitting this method, I'd also validate that both the plan and payment are selected. Just things that are more outside the scope of getting a user subscribed through an SPA + API.
Upon success you should see this recorded in 2 places. The first in your local database in the `subscriptions` table created with Laravel Cashier: 
And MOST importantly, in your Stripe Dashboard under Billing → Subscriptions:

## Conclusion
That's it! We now have our Single Page Application set up to handle and manage payments and allow the user to subscribe to a plan. There's a lot of moving parts but once you have it set up, everything will flow with ease!
Our final `SubscriptionManagement.vue` file should look like:
```vue [Complete Subscription Management Component Template]
Manage Your Subscription
OR
No payment method on file, please add a payment method.
```
```javascript [Complete Subscription Management Component Script]
export default {
data(){
return {
stripeAPIToken: 'pk_test_XXX',
stripe: '',
elements: '',
card: '',
intentToken: '',
name: '',
addPaymentStatus: 0,
addPaymentStatusError: '',
paymentMethods: [],
paymentMethodsLoadStatus: 0,
paymentMethodSelected: {},
selectedPlan: '',
}
},
mounted(){
this.includeStripe('js.stripe.com/v3/', function(){
this.configureStripe();
}.bind(this) );
this.loadIntent();
this.loadPaymentMethods();
},
methods: {
/*
Includes Stripe.js dynamically
*/
includeStripe( URL, callback ){
var documentTag = document, tag = 'script',
object = documentTag.createElement(tag),
scriptTag = documentTag.getElementsByTagName(tag)[0];
object.src = '//' + URL;
if (callback) { object.addEventListener('load', function (e) { callback(null, e); }, false); }
scriptTag.parentNode.insertBefore(object, scriptTag);
},
/*
Configures Stripe by setting up the elements and
creating the card element.
*/
configureStripe(){
this.stripe = Stripe( this.stripeAPIToken );
this.elements = this.stripe.elements();
this.card = this.elements.create('card');
this.card.mount('#card-element');
},
/*
Loads the payment intent key for the user to pay.
*/
loadIntent(){
axios.get('/api/v1/user/setup-intent')
.then( function( response ){
this.intentToken = response.data;
}.bind(this));
},
/*
Uses the intent to submit a payment method
to Stripe. Upon success, we save the payment
method to our system to be used.
*/
submitPaymentMethod(){
this.addPaymentStatus = 1;
this.stripe.confirmCardSetup(
this.intentToken.client_secret, {
payment_method: {
card: this.card,
billing_details: {
name: this.name
}
}
}
).then(function(result) {
if (result.error) {
this.addPaymentStatus = 3;
this.addPaymentStatusError = result.error.message;
} else {
this.savePaymentMethod( result.setupIntent.payment_method );
this.addPaymentStatus = 2;
this.card.clear();
this.name = '';
}
}.bind(this));
},
/*
Saves the payment method for the user and
re-loads the payment methods.
*/
savePaymentMethod( method ){
axios.post('/api/v1/user/payments', {
payment_method: method
}).then( function(){
this.loadPaymentMethods();
}.bind(this));
},
/*
Loads all of the payment methods for the
user.
*/
loadPaymentMethods(){
this.paymentMethodsLoadStatus = 1;
axios.get('/api/v1/user/payment-methods')
.then( function( response ){
this.paymentMethods = response.data;
this.paymentMethodsLoadStatus = 2;
// this.setDefaultPaymentMethod();
}.bind(this));
},
removePaymentMethod( paymentID ){
axios.post('/api/v1/user/remove-payment', {
id: paymentID
}).then( function( response ){
this.loadPaymentMethods();
}.bind(this));
},
updateSubscription(){
axios.put('/api/v1/user/subscription', {
plan: this.selectedPlan,
payment: this.paymentMethodSelected
}).then( function( response ){
console.log( response );
}.bind(this));
},
}
}
```
Our `api.php` should look like:
```php [API Routes Configuration]
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::group(['prefix' => 'v1', 'middleware' => 'auth:api'], function(){
Route::get('/user/setup-intent', 'API\UserController@getSetupIntent');
Route::put('/user/subscription', 'API\UserController@updateSubscription');
Route::post('/user/payments', 'API\UserController@postPaymentMethods');
Route::get('/user/payment-methods', 'API\UserController@getPaymentMethods');
Route::post('/user/remove-payment', 'API\UserController@removePaymentMethod');
});
```
And finally our `UserController.php` should look like:
```php [Complete User Controller]
namespace App\Http\Controllers\API;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* Creates an intent for payment so we can capture the payment
* method for the user.
*
* @param Request $request The request data from the user.
*/
public function getSetupIntent( Request $request ){
return $request->user()->createSetupIntent();
}
/**
* Updates a subscription for the user
*
* @param Request $request The request containing subscription update info.
*/
public function updateSubscription( Request $request ){
$user = $request->user();
$planID = $request->get('plan');
$paymentID = $request->get('payment');
if( !$user->subscribed('Super Notes') ){
$user->newSubscription( 'Super Notes', $planID )
->create( $paymentID );
}else{
$user->subscription('Super Notes')->swap( $planID );
}
return response()->json([
'subscription_updated' => true
]);
}
/**
* Adds a payment method to the current user.
*
* @param Request $request The request data from the user.
*/
public function postPaymentMethods( Request $request ){
$user = $request->user();
$paymentMethodID = $request->get('payment_method');
if( $user->stripe_id == null ){
$user->createAsStripeCustomer();
}
$user->addPaymentMethod( $paymentMethodID );
$user->updateDefaultPaymentMethod( $paymentMethodID );
return response()->json( null, 204 );
}
/**
* Returns the payment methods the user has saved
*
* @param Request $request The request data from the user.
*/
public function getPaymentMethods( Request $request ){
$user = $request->user();
$methods = array();
if( $user->hasPaymentMethod() ){
foreach( $user->paymentMethods() as $method ){
array_push( $methods, [
'id' => $method->id,
'brand' => $method->card->brand,
'last_four' => $method->card->last4,
'exp_month' => $method->card->exp_month,
'exp_year' => $method->card->exp_year,
] );
}
}
return response()->json( $methods );
}
/**
* Removes a payment method for the current user.
*
* @param Request $request The request data from the user.
*/
public function removePaymentMethod( Request $request ){
$user = $request->user();
$paymentMethodID = $request->get('id');
$paymentMethods = $user->paymentMethods();
foreach( $paymentMethods as $method ){
if( $method->id == $paymentMethodID ){
$method->delete();
break;
}
}
return response()->json( null, 204 );
}
}
```
If you have any questions or need any help, feel free to reach out in the comment section below or on Twitter [@danpastori](https://twitter.com/danpastori){rel=""nofollow""}.
# Creating SAAS Products in Stripe to Sell with Laravel Cashier
Now we are getting to the meat of the application! This is where we will be adding our products and plans in Stripe so we can allow users to subscribe to them through Laravel. Up to this point, we have [Laravel Cashier](https://serversideup.net/blog/installing-laravel-cashier-on-laravel-6-x/) (the official Stripe package installed) and we've grabbed our API tokens from Stripe and placed them in our `.env` file. Next up, we need to add some products and plans for users to put a card on file and pay for!
This is specifically where I got lost when I first did this so I hope it helps fill the gaps with other people's implementation.
## 1. Adding a Product
The first thing we will need to do is add a product. To do this, make sure you are in the Stripe dashboard and go to `Billing`. Once on the `Billing` page, you will get a sub-menu where there's an link called `Products`. Click that link.

You might get confused with `Subscriptions` since we are working with users subscribing to features. However, `Subscriptions` is a list of users who are subscribed to a product.
The way I look at products in Stripe is this is your App. You have an account with Stripe and then you have a product. Say you are building a note taking platform that has 3 levels of pricing. The app's name is "Super Notes". You will be creating a product named "Super Notes" that has 3 different pricing plans. Don't worry, we will be going through all of these steps.
## 2. Creating a Product
Stripe is set up to handle a lot of different subscription type products. We are going to look at this from the lens of an app. The first thing you need to do is click the + New button on the top of the table.

A modal will appear. In there you will enter the following fields: - Product Name → This would be your app. The other 2 fields `Unit label` and `Statement descriptor` are optional. Enter them only if you need to.

When you are ready, click `Create product` and your first product will be created!
## 3. Add Pricing Plans to Your Product
Now it's time to add a few pricing plans to your product! These are what your users will be signing up for. So let's think about a few different plans. Say we have a 3 tiered service with a Basic, Professional, Enterprise and we will charge $10.00, $15.00, $20.00 per month accordingly.

Let's start with "Basic". For the plan nickname, this will be "Basic". We will leave the ID blank so Stripe will generate this for us. We will also keep 'Recurring quantity' checked so we will charge the user at the proper recurring time. Finally, we will enter $10.00 per unit and our plan will be ready!

All we have to do now, is click `Add pricing plan` and we will be ready to move to the next one! We just have to repeat the above process for the next 2 pricing tiers, swapping what is necessary such as name and price.
That's all we need to do for this step! We now have Laravel ready to work within Stripe. In Stripe we are set up to receive requests and some products available for the user to subscribe to. The next part of this series will be to begin connecting everything together.
# Creating Stripe Setup Intents With Laravel API and VueJS SPA
We are making progress in implementing our Subscription component! Next up, we have to get our setup intent.
When I first started implementing Stripe into applications for recurring transactions, the concept of the set up intent was a little confusing to me. In the Laravel 6.x Cashier docs ({rel=""nofollow""}) it states that "A "Setup Intent" indicates to Stripe the intention to charge a customer's payment method." What kind of made sense to me was to look at the setup intent as "hey, we need to save your card because you **intend** on letting us charge it for our service at a certain rate". It sounds a lot more complicated than it is, but Stripe and Laravel make it a breeze. We are just going to put it in an API + SPA form.
The setup intent will be sent to Stripe along with the credit card info so Stripe will store the information on behalf of the user and return the proper data to us to store locally so we can charge accordingly.
Before you can actually charge for a subscription and create that subscription, you have to have a card on file. In this tutorial we are taking the first steps to getting there! The next step will be actually persisting the card and getting the payment ID so we can charge the customer accordingly.
## 1. Add API Route To Grab Setup Intent
This is right away where this tutorial diverges from the official Laravel documentation. In the official Laravel 6.x docs on Storing Payment Methods ({rel=""nofollow""}), the documentation goes through returning a blade view in a call and response type structure, with a variable that is the setup intent token. We don't have that available in our Single Page Application, so we have to do it a little bit differently.
As long as you have Laravel Passport installed ({rel=""nofollow""}), and configured, open up `routes/api.php`. All of the API routes we are using in this course will be behind the `auth:api` middleware which requires a valid authentication token to be present. This is VERY important so no matter how you secure the routes (group, middleware in constructor, etc) make sure they are behind the `auth:api` middleware.
I like putting all of my secure API routes within a secure route group. I find it easier to organize and view so I can place secure routes in that group easier.
This is what my routes/api.php file looks like:
```php [API Routes Configuration]
Route::group(['prefix' => 'v1', 'middleware' => 'auth:api'], function(){
});
```
If you are implementing in an existing application, you might have a ton of existing routes. Just make sure the routes we add next are blocked by the `auth:api` middleware. I also have a `v1` prefix for my API so it makes versioning easier. That's a different subject entirely, but you won't have to worry about that in the scope of this course.
Next, add the following route within your secure route group:
```php [Setup Intent Route Definition]
Route::get('/user/setup-intent', 'API\UserController@getSetupIntent');
```
2 things to note. The route will resolve to a `GET` request on `/api/v1/user/setup-intent` with the `v1` prefix that I have (may be different on your API) and I am using an `API` namespaced `UserController.php`. I put this in a controller and specifically namespaced it for API usage. It will respond on the method `getSetupIntent()`.
## 2. Add getSetupIntent method to UserController
Now we need to handle the response to the `/api/v1/user/setup-intent` route. Open up your controller, in this case the `API\UserController` and add the following code:
```php [UserController Setup Intent Method]
namespace App\Http\Controllers\API;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* Creates an intent for payment so we can capture the payment
* method for the user.
*
* @param Request $request The request data from the user.
*/
public function getSetupIntent( Request $request ){
return $request->user()->createSetupIntent();
}
}
```
The `createSetupIntent()` is a trait on the `User` model that returns the setup intent object `client_secret`. We will pass an object back to our SPA (unlike in the documentation where it binds it to a blade view). Since we are calling our API, this returns a beautiful, JSON formatted response that we can easily integrate into our SPA.
## 3. Using the Setup Intent in the VueJS SPA
Let's switch back to the `SubscriptionManagement.vue` component that we started making in [Using Stripe Elements in a VueJS Component](https://serversideup.net/blog/using-stripe-elements-in-a-vuejs-component/).
First, let's add a local variable to our `data()` object named `intentToken`. This will house the response from the API.
```javascript [Vue Component Data Property]
data(){
return {
...
intentToken: ''
}
},
```
Next, let's add a method that loads the intent from the API and add this to the `methods` object in the local component:
```javascript [Load Intent Method]
/*
Loads the payment intent key for the user to pay.
*/
loadIntent(){
axios.get('/api/v1/user/setup-intent')
.then( function( response ){
this.intentToken = response.data;
}.bind(this));
},
```
Finally, let's look at our `mounted()` lifecycle hook. In there, we will add a call to the `loadIntent()` method like this:
```javascript [Vue Component Mounted Hook]
mounted(){
...
this.loadIntent();
},
```
Now when our component is mounted, we will load the intent to setup a new payment. There are a LOT of different UX things you can do with this. Maybe you don't want to display the form right away to add a payment, that way you don't need a setup intent. If that's the case, don't call the `this.loadIntent()` from inside `mounted`. Call it when you want the user to begin adding their payment.
The flow of getting the setup intent from within the SPA is as follows:
1. Our Subscription Management component is `mounted()`
2. We call the `loadIntent()` method which loads the setup intent from `/api/v1/user/setup-intent` .
3. Upon completion, this token (the object returned) is stored to the local `intentToken` variable. The `client_secret` key will be used when we call the Stripe API.
A few final notes. Since we are getting a `client_secret` from the API for the setup intent, it's good to have protections in place (such as being behind the `auth:api` middleware). It's also okay to note that we are saving this to a local variable. Our Stripe client secret is still housed in our `.env` server side. Even in the examples provided, this intent token is stored in the HTML.
Let me know your thoughts and if you have any questions in the comment section below! In the next tutorial we will be actually saving the payment and creating a subscription!
# Custom Component v-model attribute with Vue 3
The core of VueJS and why it's so awesome is the ease of making reactive interfaces. To watch input on an element you can use the `v-model` attribute. This attribute will automatically update when the user changes a value. However, when you start to create more advanced VueJS functionality, you will want to abstract that functionality into components that you can use throughout the app. You also want these complex components to be able to accept a `v-model` attribute and react to what the user enters.
In this tutorial, we will create a component that searches our transaction categories in [Financial Freedom](https://github.com/serversideup/financial-freedom){rel=""nofollow""}. This component will contain a decent amount of logic that makes sense to abstract into a re-usable piece that we will use in multiple places. When the user selects a category through a variety of methods, we will update the `v-model` attribute. This allows us to access the data from a parent component.
## `v-model` Documentation
While this functionality is [fully documented on VueJS Documentation](https://v3.vuejs.org/guide/migration/v-model.html#v-model-arguments){rel=""nofollow""}, I wanted to make a more consumable version with an example. I feel this is a super powerful way to make re-usable elements for your app or as open source projects. Hopefully this quick tutorial helps a little bit!
## What We Are Building
Just for a proof of concept, we are creating a separate VueJS component called `CategorySelect.vue`. In this component, we will be allowing the user to search through a list of provided categories, autocomplete the results that match, and select a category. These categories will be for financial transactions, such as "Groceries", "Gas", "Rent".
The reason we abstracted this functionality is so we can re-use this `CategorySelect.vue` component anywhere we import or add a transaction. This means that we need to allow `v-model` to bind to what the user selects.
If you are like me, and just want to see the final code, see below. Otherwise keep reading and I'll step you through the process. In the example we are also using [InertiaJS](https://inertiajs.com/){rel=""nofollow""}. You will see reference to `this.$page.props.categories` which is a global array of the categories present in our app.
### CategorySelect.vue
```vue [CategorySelect Component Implementation]
{{ category.name }}
```
### Using The Component
```vue [Component Usage Example]
```
## Step 1: Add `modelValue` Prop to Component
There are only three steps to make allow for `v-model` usage on a custom component. Step 1 is to add the `modelValue` prop the type of a `String` to your component:
```javascript [Add the model value]
props: {
modelValue: String
},
```
This will allow you to accept the `v-model` attribute on your custom component.
## Step 2: Register The Events Emitted By Your Component
Next, add the following property:
```javascript [Registering Component Events]
emits: ['update:modelValue'],
```
This property registers what events are emitted and listened to by the
parent component. When the property we want to set as the model changes, we
will emit the `update:modelValue` event.
## Step 3: Emit Event When Value Updates
Finally, whenever we update the value, in this case when the user selects
the appropriate category, we need to emit the `update:modelValue` event:
```javascript [Emitting Update Event]
this.$emit('update:modelValue', this.selectedCategory.id);
```
This event will update the model property so the parent can two-way sync and
read the value of the component. Now you can encapsulate complex code and
logic into reusuable components and directly bind the value from a parent
component.
## Conclusion
Even though this is a smaller tutorial, it's extremely helpful to order to break out functionality into smaller parts. Especially with complex form fields and selectors. Let me know if you have any questions by leaving a comment or reaching out to me on Twitter ([@danpastori](https://twitter.com/danpastori){rel=""nofollow""}).
# Custom Google Maps Info Windows
Building off of our last tutorial where we added custom markers to the map: [Custom Markers on Google Map](https://serversideup.net/blog/custom-markers-google-map/), the next step is to add an info window to these markers when clicked. This should be a pretty quick tutorial since we will just be binding an info window to a marker on click.
## Step 1: Open CafeMap.vue
The `/resources/assets/js/components/cafes/CafeMap.vue` is where all of our map logic will be located. In the `buildMarkers()` method, we create all of the markers. Well we need to bind the info windows to the marker, so this is where our coding will take place.
First, we will need to add an `infoWindows` array to the data for the `CafeMap.vue` component. We will store all of our info windows in that array so we can call methods on those when needed. Our data should now look like:
```javascript [Component Data Structure]
data(){
return {
markers: [],
infoWindows: []
}
},
```
Next, navigate to the `buildMarkers()` method in the `for` loop. In there, find the marker declaration. Below that, we will add the following code:
```javascript [Info Window Creation and Event Binding]
/*
Create the info window and add it to the local
array.
*/
let infoWindow = new google.maps.InfoWindow({
content: this.cafes[i].name
});
this.infoWindows.push( infoWindow );
```
What this does is declare an info window object that declares the name of
the cafe. If you look at the Google Docs: \[Info Windows | Google Maps
JavaScript API | Google Developers]\({rel=""nofollow""}
documentation/javascript/infowindows) for info windows, you can add HTML
mark up and really customize the look and feel of the info window. We will
be doing this in a style update update, but for now, we are just getting an
info window to display.
Now, we just need to bind a click to the marker to open the info window. To
do that, right below the last code added add:
```javascript [Add event listener to open the info window for the marker]
/*
Add the event listener to open the info window for the marker.
*/
marker.addListener('click', function() {
infoWindow.open(this.map, this);
});
```
What this does is when the user clicks on the marker, it opens the info window on the map, above `this` which is referencing the marker.
When you click on one of the markers on the map, you should see an info window with the name of the cafe it represents like below:

## Conclusion
To add an info window is pretty simple, making them look good isn't hard either since we will be using HTML. The next part of the tutorial, we will be doing a design overhaul and any tweaks I'll document in that tutorial. For now, enjoy the Google Maps Info Windows on your map and make sure to check out the code here: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""}
# Custom Markers on Google Map
One of the coolest aspects of Google Maps is how easy it is to customize EVERY detail. You can change the colors, the info windows, the data displayed, and even the markers! This is a small tutorial that continues off of of the previous tutorial of displaying resources on a Google Map with VueJS [Displaying Resources on a Google Map With Vue JS](https://serversideup.net/blog/displaying-resources-google-map-vue-js/). This tutorial can work with any implementation of a Google Maps but we will be using the one from our last tutorial which is in VueJS to add our custom markers. All of the documentation for Javascript Google Maps Markers is here [Markers | Google Maps JavaScript API | Google Developers](https://developers.google.com/maps/documentation/javascript/markers){rel=""nofollow""} for your reference!
## Step 1: Open The CafeMap.vue component
First thing we need to do is open our `/resources/assets/js/components/cafes/CafeMap.vue` component. In this component is where we have our Google Maps implementation and we add all of our markers.
We need to find the `buildMarkers()` method. In there, you will see a a few lines of code like this:
```javascript [Basic Google Maps marker implementation]
/*
Create the marker for each of the cafes and set the
latitude and longitude to the latitude and longitude
of the cafe. Also set the map to be the local map.
*/
var marker = new google.maps.Marker({
position: { lat: parseFloat( this.cafes[i].latitude ), lng: parseFloat( this.cafes[i].longitude ) },
map: this.map
});
```
What this does is create a new Google Maps Marker and in that creation, we need to set an image. For this tutorial I created a map icon made from the coffee png found here: {rel=""nofollow""} and map marker svg found here: [Fill, map, pin icon | Icon search engine](https://www.iconfinder.com/icons/118702/fill_map_pin_icon#size=128){rel=""nofollow""}.
The image we are using lis located in `/public/img/coffee-marker.png`. First we need to set a variable with the image URL above our new marker declaration:
```javascript [Adding custom marker image variable]
/*
Create the marker for each of the cafes and set the
latitude and longitude to the latitude and longitude
of the cafe. Also set the map to be the local map.
*/
var image = '/img/coffee-marker.png';
var marker = new google.maps.Marker({
position: { lat: parseFloat( this.cafes[i].latitude ), lng: parseFloat( this.cafes[i].longitude ) },
map: this.map
});
```
Next, we need to tell the new marker to use the image. We do this in the JSON passed to create a new marker like this:
```javascript [Implementing custom marker with image]
/*
Create the marker for each of the cafes and set the
latitude and longitude to the latitude and longitude
of the cafe. Also set the map to be the local map.
*/
var image = '/img/coffee-marker.png';
var marker = new google.maps.Marker({
position: { lat: parseFloat( this.cafes[i].latitude ), lng: parseFloat( this.cafes[i].longitude ) },
map: this.map,
icon: image
});
```
We just set the icon key to the new image and voila! Now when we view our map, we should see our new icon!

There's a lot of other cool things we will be doing with the markers, but changing the image is a huge step to making the map look and feel more like your site!
## Conclusion
This is a pretty simple tutorial on just changing the map markers, but we will continue to customize our map to make it look and feel like our app. Keep following along and check out the repo for all of the source code: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""}
# Customize Google Map Info Windows
In the last tutorial, we re-used a few mixins to filter the Google Map that contained all of the cafes: [Re-using VueJS Mixins and Filtering Google Map Data](https://serversideup.net/blog/re-using-vuejs-mixins-filtering-google-map-data/). The filtering helped because it helped us visualize locally where a cafe was that matched our search parameters was located. However when you click on a marker, the only thing that displays is the cafe name. In this tutorial we will be Customize the Google Map Info Windows for these markers so they display a little more information and a link to the individual cafe page.
## Step 1: Plan what we want
We don't want a super complex info window, we just want it styled a little bit than we did here: /custom-google-maps-info-windows/. Maybe add some more information such as the address of where the cafe is located. If we add pictures in the future we can display these in the pop up as well.
## Step 2: Modify the buildMarkers() method
First, let's open up the `/assets/js/components/cafes/CafeMap.vue` component. This is where the `buildMarkers()` method is. If you examine the method we have, there's a line where we create an info window like this:
```javascript [Basic Info Window Creation]
/*
Create the info window and add it to the local
array.
*/
let infoWindow = new google.maps.InfoWindow({
content: this.cafes[i].name
});
```
In this tutorial we will be editing the content of the info window. The cool thing is you can add HTML to this field. If you look at Google Map's InfoWindow documentation ([Info Windows | Google Maps JavaScript API | Google Developers](https://developers.google.com/maps/documentation/javascript/infowindows){rel=""nofollow""}) they provide a more in-depth example.
When we are building our info windows, we have access to the cafe and can grab certain attributes. So before we define our info window let's create a `contentString` variable:
```javascript [Create the content string]
/*
Create the info window and add it to the local
array.
*/
var contentString = '
' + '
';
```
Right now, this contains a little container for our cafe information that
will display in our pop up window.
Now let's add some html to display the data:
```javascript [Enhanced Info Window Content]
/*
Create the info window and add it to the local
array.
*/
var contentString = '
';
```
This will be what we insert into each info window. Now, all we have to do is adjust the content of the InfoWindow that we create to reference the `contentString`:
```javascript [Add the content string to the info window]
let infoWindow = new google.maps.InfoWindow({
content: contentString
});
```
I also added a few styles to make the content in the info window look nice. So on top, in the `
```
## Step 6: Add the Google Map
Now here's the thing with Google Map, it's a variable, but we do NOT want Vue to apply it's reactive watching to all of the methods, it doesn't need it. So we will do a trick to define it locally outside of our data object. The other trick is, we need the HTML to be rendered by the component before we can build a map. Every Google Map needs a predefined width and height. This means we need to look at the lifecycle hooks provided by VueJS. The one that works perfectly is the `mounted()` hook.
First we will need to add the `mounted()` hook to our component like this:
```vue [Vue component with mounted hook]
```
Next, we will need to add a default width and height to the map so in our scss, add:
```scss [SCSS styles for map dimensions]
div#cafe-map{
width: 100%;
height: 400px;
}
```
Now the last thing we need to do is add a few properties for the default latitude and longitude required for the map. I added these as properties so we can change them if needed when we initialize the map. Our CafeMap component should look like:
```vue [Complete CafeMap component with props and styles]
```
As you can see I provided some defaults and types to the properties. VueJS allows the user to define what type each property should be so when making a re-usable component, the developer doesn't get bad data passed to the component. For a full list of validation types check out the Vue docs here: [Components — Vue.js](https://vuejs.org/v2/guide/components.html#Prop-Validation){rel=""nofollow""}.
I also added a zoom level to the properties so the developer can add the zoom they wish the map was zoomed into.
Now, we have enough data we can finally initialize our map. As we mentioned before, we don't want the map to to be reactive so we will not define it in our `data()` method, but we do want it to be scoped and referenced inside of our component. So in our `mounted()` lifecycle hook add the following:
```javascript [Map initialization in mounted hook]
mounted(){
/*
We don't want the map to be reactive, so we initialize it locally,
but don't store it in our data array.
*/
this.map = new google.maps.Map(document.getElementById('cafe-map'), {
center: {lat: this.latitude, lng: this.longitude},
zoom: this.zoom
});
}
```
When we use `this.map` we are assigning the map to a local variable that we can use inside our component's scope. We also initialize the map to be the latitude and longitude of the properties that get passed in, along with the default zoom level.
Now, we have our map ready to rock and roll, just a few more steps and we can display our cafes!
## Step 7: Add the CafeMap.vue component to the Cafes Page
We want to display the map on the cafes page. First, we will open up the `/resrouces/assets/js/pages/Cafes.vue` page.
Now before we export our default module, we need to import our `CafeMap` component.
Add the import above the export default:
```javascript [Import statement for CafeMap component]
import CafeMap from '../components/cafes/CafeMap.vue';
```
Now we need to tell our Cafes page we will use the CafeMap component by defining it within the components like this:
```javascript [Component registration in Cafes page]
export default {
components: {
CafeMap
}
}
```
Last but not least, we need to add the component to the page so our updated Cafes page should look like:
```vue [Complete Cafes page with map component]
```
If you visit the page you should see a nice Google Map waiting for you like this:

## Step 8: Adding the Cafes to the Map
This is the final step in the tutorial. We need our cafes we have loaded added to the map. To do this we will be adding the cafes as markers. Right now the markers will just be placeholders, in the next tutorials we will customize the markers, customize the infowindows, filter markers, etc.
First we will need to add a markers array to our data in the `CafeMap.vue` component like this:
```javascript [Adding markers array to component data]
data(){
return {
markers: []
}
},
```
Now we need to import all of the cafes we loaded. This is stored in our Vuex module ([Build a Vuex Module - Server Side Up](https://serversideup.net/blog/build-vuex-module/)) that gets loaded in our layout from our layout (/building-page-layout-vue-router/).
To add our Vuex to the Cafes page, we need to load the data from Vuex as a computed property:
```javascript [Computed property for cafes from Vuex]
computed: {
/*
Gets the cafes
*/
cafes(){
return this.$store.getters.getCafes;
}
},
```
We can now reference the cafes from within our Cafes page. Next we need to add a few methods.
The first method is the `buildMarkers()` method. What this will do is create a Google Maps Marker [Markers | Google Maps JavaScript API | Google Developers](https://developers.google.com/maps/documentation/javascript/markers){rel=""nofollow""} for each of our Cafes. So add a `methods` object and a `buildMarkers()` method:
```javascript [Methods object with buildMarkers method]
methods: {
buildMarkers(){
}
}
```
In this method, we will iterate over all of our cafes we loaded, create a Google Maps Marker and set it to the map that we defined. Then we will add the marker to the markers array in the data. Our method should look like this:
```javascript [Complete buildMarkers method implementation]
/*
Builds all of the markers for the cafes
*/
buildMarkers(){
/*
Initialize the markers to an empty array.
*/
this.markers = [];
/*
Iterate over all of the cafes
*/
for( var i = 0; i < this.cafes.length; i++ ){
/*
Create the marker for each of the cafes and set the
latitude and longitude to the latitude and longitude
of the cafe. Also set the map to be the local map.
*/
var marker = new google.maps.Marker({
position: { lat: parseFloat( this.cafes[i].latitude ), lng: parseFloat( this.cafes[i].longitude ) },
map: this.map
});
/*
Push the new marker on to the array.
*/
this.markers.push( marker );
}
}
```
What this does is initialize the markers to an empty array. This is so if a cafe gets added, we can add it to a fresh array. Next, we iterate over all of the cafes in the Vuex module. With each cafe, we create a Google Maps Marker and set the latitude and longitude to the Cafe's latitude and longitude. We also set the map to our Google Map. We then push the marker to the markers array.
Now that we have the markers being built, we should write a `clearMarkers()` method. This will clear the markers from the map. This is useful when we re-render the page, load a new cafe, etc. This method is very simple, it just sets each marker in our our array's map to null using the `setMap()` method available on each marker. The clear markers method should look like:
```javascript [clearMarkers method implementation]
/*
Clears the markers from the map.
*/
clearMarkers(){
/*
Iterate over all of the markers and set the map
to null so they disappear.
*/
for( var i = 0; i < this.markers.length; i++ ){
this.markers[i].setMap( null );
}
},
```
Now we have to tie the two together. At the end of the `mounted()` method, we should clear and re-build the markers. What this will do is clean up any old markers first before we reset the markers array, and build it with the cafes. Add the following lines to the end of the `mounted()` method:
```javascript [Marker initialization in mounted hook]
/*
Clear and re-build the markers
*/
this.clearMarkers();
this.buildMarkers();
```
Finally, we need to add a watcher to our `cafes`. When the cafes get updated, we need to clear the markers and rebuild them as well. For more information on Watchers, check out: [Computed Properties and Watchers — Vue.js](https://vuejs.org/v2/guide/computed.html#Watchers){rel=""nofollow""}. To do this add a watcher to the CafeMap.vue component like this:
```javascript [Add a watcher to the cafe map]
watch: {
cafes(){
}
},
```
This will watch the cafes computed property and when it changes, fire a method. In this case, we want to clear and re-build the markers, so we add the same two lines of code:
```javascript [Watcher for cafes property]
watch: {
/*
Watches the cafes. When they are updated, clear the markers
and re build them.
*/
cafes(){
this.clearMarkers();
this.buildMarkers();
}
},
```
Now we have the component set up so when the cafes change, we re-build the markers and when the component is mounted we re-build the markers. This way if a cafe gets added, it's right on our map and if we navigate away from the cafes page and navigate back we will still have our markers. If you visit the `/cafes/` route you should see the markers for the cafes added:
[](https://serversideup.net/wp-content/uploads/2017/10/Screen-Shot-2017-10-21-at-3.21.44-PM.png)
Our CafeMap.vue component should look like:
```vue [Complete CafeMap component implementation]
```
## Conclusion
Google Maps is extremely powerful and has great documentation. In future tutorials, we will be customizing the look and feel of the map. For now, feel free to check out all of the source code here: [GitHub - serversideup/roastandbrew](https://github.com/serversideup/roastandbrew){rel=""nofollow""}
# Docker Compose Bind Mounts vs Named Volumes: When to Use Each
A [question came up on GitHub](https://github.com/serversideup/docker-php/discussions/637){rel=""nofollow""} that I've seen asked many times over the years: a user had a Laravel app running with multiple containers (web, task scheduler, Horizon), and the bootstrap cache generated by `artisan optimize` in the web container wasn't visible to the other containers.
Their fix was a named volume shared across all containers. That totally works. But it made me realize a lot of people don't know when to reach for a bind mount vs a named volume. Let's clear it up.
## What's the difference?
Docker Compose gives you two ways to persist and share data: **bind mounts** and **named volumes**.
A **bind mount** maps a path on your host directly into the container. You see both sides. Edit a file on your laptop, the container sees it immediately.
```yaml [Bind mount]
volumes:
- ./src:/var/www/html
```
A **named volume** is managed by Docker. You give it a name, Docker handles where the data lives on disk.
```yaml [Named volume]
volumes:
- app-data:/var/www/html
```
They look similar in YAML but behave very differently.
## Comparison table
| | **Bind Mount** | **Named Volume** |
| ---------------------------------- | ----------------------------------------- | -------------------------------------------------- |
| **Syntax** | `./host/path:/container/path` | `volume-name:/container/path` |
| **Where data lives** | You choose the host path | Docker manages it |
| **File visibility** | Easily accessible from host and container | More difficult to access from host |
| **Performance (macOS/Windows)** | Slower due to file system translation | Faster (stays inside the Docker VM) |
| **Performance (Linux)** | Native speed | Native speed |
| **Permissions** | Host UID\:GID must match container user | Docker handles ownership |
| **Portability** | Tied to host directory structure | Works anywhere Docker runs |
| **Best for** | Source code in development | Caches, databases, shared state between containers |
| **Survives `docker compose down`** | Always (files are on your host) | Yes, unless you pass `-v` |
## The permissions gotcha with bind mounts
Bind mounts are great for development because you can edit code locally and see changes live. This is how we use [Spin](https://serversideup.net/open-source/spin/){rel=""nofollow""} for local development. But there's a catch.
When you bind mount a directory, the container sees files owned by whatever UID\:GID they have on your host. By default, our [serversideup/php](https://serversideup.net/open-source/docker-php/){rel=""nofollow""} images run as `www-data` with UID\:GID `33:33` on Debian (or `82:82` on Alpine). If your host user is `1000:1000`, the container might not be able to write to your mounted files. Or the container creates files owned by `33:33` and now you need `sudo` to delete them on your host.
We built tooling to fix this. At **build time**, you can align the container's user ID with your host:
```dockerfile [Dockerfile]
FROM serversideup/php:8.5-fpm-nginx-bookworm AS base
FROM base AS development
USER root
ARG USER_ID
ARG GROUP_ID
RUN docker-php-serversideup-set-id www-data $USER_ID:$GROUP_ID && \
docker-php-serversideup-set-file-permissions --owner $USER_ID:$GROUP_ID
USER www-data
```
Then pass your host UID/GID as build args in your `compose.yml`:
```yaml [compose.yml]
services:
php:
build:
context: .
target: development
args:
USER_ID: ${UID}
GROUP_ID: ${GID}
volumes:
- .:/var/www/html
```
No permission conflicts, no `sudo` needed. Check out our full guide on [Understanding File Permissions](https://serversideup.net/open-source/docker-php/docs/guide/understanding-file-permissions){rel=""nofollow""} for more details.
::note
[Spin](https://serversideup.net/open-source/spin/){rel=""nofollow""} handles the UID/GID alignment automatically, so you don't have to think about any of this.
::
## Solving the multi-container cache problem with named volumes
Named volumes don't have the permissions headache because Docker manages the ownership. They're the right tool when multiple containers need to share generated data.
Back to the [GitHub discussion](https://github.com/serversideup/docker-php/discussions/637){rel=""nofollow""}: the user runs `artisan optimize` in the web container via [Laravel Automations](https://serversideup.net/open-source/docker-php/docs/framework-guides/laravel/automations){rel=""nofollow""}, which caches config, routes, and events into `bootstrap/cache/`. The task scheduler and Horizon containers need to read that same cache. A named volume makes this simple:
```yaml [compose.yml]
services:
web:
image: my/laravel-app
environment:
- AUTORUN_ENABLED=true
volumes:
- bootstrap-cache:/var/www/html/bootstrap/cache
- app-storage:/var/www/html/storage
task:
image: my/laravel-app
command: ["php", "/var/www/html/artisan", "schedule:work"]
volumes:
- bootstrap-cache:/var/www/html/bootstrap/cache
- app-storage:/var/www/html/storage
horizon:
image: my/laravel-app
command: ["php", "/var/www/html/artisan", "horizon"]
volumes:
- bootstrap-cache:/var/www/html/bootstrap/cache
- app-storage:/var/www/html/storage
volumes:
bootstrap-cache:
app-storage:
```
Named volumes work well here because the files are generated by the container, not edited by a developer. All containers run as the same user inside the same image, so permissions just work. Other good candidates: database data (`mysql-data:/var/lib/mysql`), Redis persistence, and any shared storage directory.
## Wrapping up
Bind mounts for code you're editing. Named volumes for data the containers generate. Most projects end up using both depending on the environment. If you're running into file permission issues with bind mounts, match the container's UID\:GID to your host user, or let [Spin](https://serversideup.net/open-source/spin/){rel=""nofollow""} handle it for you.
Have questions? Jump into our [Discord](https://serversideup.net/discord/) and let us know.
# Drag and Drop File Uploads with VueJS and Axios
When it comes to uploading files for the web, dragging and dropping files from a directory is one of the most convenient ways to accomplish this task. Almost any modern web application allows you to do this. Up until this point, we have all the different tricks to upload files with VueJS and Axios. We have standard file uploads where users can do a single file, multiple files, and even edit the files before submitted: [Uploading Files With VueJS and Axios - Server Side Up](https://serversideup.net/blog/uploading-files-vuejs-axios/), we have file previews: [Preview File Uploads with Axios and VueJS](https://serversideup.net/blog/preview-file-uploads-with-axios-and-vuejs/), and we have a status bar of the upload process: [File Upload Progress Indicator with Axios and VueJS](https://serversideup.net/blog/file-upload-progress-indicator-with-axios-and-vuejs/) .
In this tutorial we are going to combine all of these tricks to make the ultimate file uploader with VueJS and Axios. We will allow users to select any amount of files, remove the ones they don't, show a preview, and show a status bar for uploading the files. The user will be able to select which files they want through dragging and dropping or selecting through the standard uploader.
A lot of the drag and drop functionality comes from [Osvaldas Valutis](https://osvaldas.info/){rel=""nofollow""} and his guest post on CSS tricks: [Drag and Drop File Uploading | CSS-Tricks](https://css-tricks.com/drag-and-drop-file-uploading/){rel=""nofollow""}. Mad props to Osvaldas, the tutorial was the best I've seen. I've adapted what we need to be inside a Vue component and not with jQuery and added some of the bells and whistles from the last couple articles.
This tutorial will flow in steps, we will: :br
1\. Build our drag and drop base. :br
2\. Add previews for the files selected if they are images :br
3\. Allow users to remove the files they don't want any more :br
4\. Upload the selected files :br
5\. Add a progress bar :br
6\. Take a short derivative for the direct to upload process. This will upload the files when they are dropped onto the drop base.
Lots of cool stuff in this tutorial, but it can get a little complicated at times so reach out if you need a hand during any of the steps!
## 🚨 UPDATE 10/14/2021
We've created a [Github repo](https://github.com/serversideup/uploading-files-vuejs-axios){rel=""nofollow""} that contains a functioning VueJS component for both [drag and drop](https://github.com/serversideup/uploading-files-vuejs-axios/blob/main/src/components/DragAndDrop.vue){rel=""nofollow""} and also an [instant drag and drop](https://github.com/serversideup/uploading-files-vuejs-axios/blob/main/src/components/DragAndDropInstant.vue){rel=""nofollow""}. Both of these components have been updated for Vue 3.0 and Axios 0.21.1. There are also a few significant structural changes, such as removing the `$refs` attribute and dividing up the functionality into more maintainable methods. Definitely recommend heading over to Github and checking out these two components!
## Building Your Drag And Drop Base
This is the guts of the tutorial, but also where we should start. The key to having a drag and drop file uploader is to have an element where you can drag and drop files.
First, we need to create an empty Vue Component. In this component add the following component stub:
```vue [Basic Vue Component Structure]
```
In here is where we will build all of our functionality. I added a real basic style for the form which just centers it in the screen and added some text to instruct the users where to drag and drop the files. Osvaldas has a beautiful UX in his tutorial here: [Drag and Drop File Uploading | CSS-Tricks](https://css-tricks.com/drag-and-drop-file-uploading/){rel=""nofollow""}. I'll be focusing just on porting some of the functionality to VueJS and Axios. Feel free to style the elements anyway you'd like!
The only real thing to point about about the template is the `ref` attribute on the `