Phonebook app in Vue
#
IntroductionIn this tutorial we are going to make simple phonebook app. We want our users to be able to create, update, list and delete contacts. We will demonstrate that using Architect SDK
as a backend service we are able to make complete app (although simple) in no time. By delegating all backend logic to Architect SDK
we can fully focus on making UI and let Architect SDK
handle the rest.
#
SetupWe will use vite for initial setup of our project. Open your terminal and enter following code:
You will be prompted to install create-vite@latest
. Press y
. Everything should install now. After installation has finished inside your terminal type these three commands:
Open our new project with your editor of choice (I am using vscode1. After running npm dev
open your browser and go to localhost:3000. Your screen should look like this:
Next lets add Architect SDK
to our app.
#
Add Architect SDKFirst let's install architect-sdk
package. In your terminal add following line:
Then let's create .env
file and add following line:2
Then inside src
folder create new file architectSDKConfig.ts
and paste following code:
First we imported our package and ArchitectResource
. We defined types Contact
and ArchitectSchema
. Then we imported VITE_BASE_URL
that we defined earlier in .env
file. Finally we initialized Architect SDK
by passing baseUrl
and ArchitectSchema
and exported it.
#
Global stylesWe will have some css
that we want to share across all components. To so that we first need to create style.css
file inside src/assets
folder. Then we will define some style
Then inside main.ts
add following line
main.ts
should now look like this
Now lets create some components
#
ComponentsOur app will have three pages:
- Login page - for user login
- Contact list page - for displaying list of contacts
- Contact form - for updating and creating contacts
Let's create them. Inside components create three new files Login.vue
, ContactForm.vue
and ContactList.vue
. Inside Login.vue
add following line:
Inside ContactForm.vue
put:
And finally add
to ContactList.vue
.
Now go to App.vue and remove import HelloWorld from './components/HelloWorld.vue
and instead add this three lines:
Delete everything between template tags add instead these line:
App.vue
should now look like this
and localhost:3000
should now look like this:
.
It makes no sense to render all three components at once .So lets add a router and render them one component for each route. We will use vue-router to achieve this.
#
RoutingFirst install vue-router@4
Then inside src
create new folder called router
and inside that folder add a file called index.ts
. Add following import statements:
Let's define routes, create router and export it. After we added all of this index.js
should have following code:
First we defined routes as an array of objects. Each object has a name (we will use this later), route path (this is a path at which component will be rendered) and component (which determines which component will be rendered). So when user access /
path Login
component will be rendered. We then create router and export it.
Now we should register our router. In main.ts
import router and register it as global like this:
main.ts
should look like this:
Now go back to App.vue
, remove all imports, delete everything inside template
tags and add <router-view></router-view>
. App.vue
should now look like this:
Now go and check each route individually. At localhost:3000
you should see Login works
. At localhost:3000/contact-list
you should see Contact list works
, and at localhost:3000/contact
you should see Contact form works
. We've added routing. Now lets make our components do something more than displaying simple text data. We'll start with Login
component.
#
LoginGo to Login.vue
and past code below.
After saving your screen should look like this:
To enable login we first have to collect user input using v-model
on our inputs and then use Architect SDK
for authentication. For that we will use architectSDK
constant we defined earlier in architectSDKConfig.ts
file. Inside <script>
tag add these imports:
ref
will be used to make variables reactive, useRouter
will be used to redirect user after login, and architectSDK
will be used to handle authentication. Now let's add, below imports, three new variables:
Let's also add login
method.
That is all we need to handle login. Now we need to bind email
and password
variables to email and password input fields by using v-model
directive. When we do this whatever user type inside those input fields will be reflected in email
and password
variables. Bind email
to email input and password
to password input.
We also need to add submit event handling. Inside form tag add following:
On submit our login
method will be called. We bind our login method using @submit
directive. We also add event modifier .prevent
to prevent default action (which will in this case result in page refresh).
With all this Login.vue
should have following code:
Let's try our code now. Type your credentials3 and click Login
. If everything is ok you should be redirected to /contact-list
.
#
Contact listWe want to be able to display, add, remove and edit contacts. In Contact List
component we will fetch all contacts, display them, enable contact deleting and routing to Contact Form
component where we will handle actual contact editing and creating. First let's add some styles and markup. Delete everything inside ContactList.vue
file and paste code below:
Notice that we added router-link
with to="/contact"
. This will navigate user to contact form. After we pasted everything you should see something like this:
Add following imports inside <script>
tags.
We seen all of this except Contact
, which is a type and onMounted
, which is a lifecycle hook that is called when the bound element's parent component is mounted. We will use onMounted
to fetch all contacts. First let's define contacts
to store our contacts and router
for routing.
We pass Contact
type to type contacts
correctly. Let's define function that will handle contacts fetching and updating our contacts
variable.
It is important to notice that we don't assign to contacts directly but through value
property of contact
. This is because contact
is not an array but an object whose value
property is an array. Next we will define method that handles contact deletion.
Here we first delete contact using architectSDK
then, if we are successful ,remove that contact from contacts array and assign to contact (through value
property) filtered values. We will also define one more method that routes user to contact form.
We need to call getContacts
method in mounted
lifecycle. We do this by giving onMounted
function getContacts
as argument.
We now have everything we need to make our component work. To iterate over contact list we need to use v-for
directive. Inside <li>
tag add this chunk of code:
We iterated over contacts
array using v-for
directive, added :key
attribute and binned @click
event to editContact
method and passed that method contact.id
parameter. We will now interpolate contact data by using double curly bracers {{ }}
. We will also bind img src
attribute and only show image if our contact has one. After all of this li tag look like this:
We also need to add deleteContact
method to handle delete. On button with class btn btn-danger
register deleteContact
like this:
We also added stop
event modifier that stops event propagation. If we didn't then on every click on delete button will also trigger editContact
handler through event bubbling.
With all we added our code should now look like this:
Contacts (if we have them) should be listed. My screen look like this (yours may not look the same if ,for example, someone else is used our open api)
Now we should start working on contact form.
#
Contact FromAs always let's start by adding some styles and markup. Go to ContactForm.vue
delete everything in it and paste:
You should see this:
We have a from with a couple of fields in it. 4 inputs are of type text and one is of type file. We will use this form for both updating and creating users. If the url is of without parameter /contact
, we will create contact and if it has parameter /contact/id
we will update it. First add following imports in <script>
tag:
Then we will define a couple of variables:
We will use route
to parse id
parameter, router
for navigation, firstName
, lastName
, phone
, email
and file
to store user input. pictureUrl
is the url of contact image.
We will also define computed property. Whenever user choose image file we will create new url representing blob object using createObjectURL and then show url to the user.
We first check if image.value has some value (which is a URL representing blob object) and revoke that object and then check if file has a value and if it does we use URL.createObjectURL that returns string. Keep in mind that everything returned from function passed to computed
will become a new value for image
. This function will trigger whenever file.value
changes.
Next we define file input handler:
When user clicks on discard button we will just navigate him to /contact-list
:
To get contact we will use this method:
We first get route parameter. And if parameter is not null ( typeof id === "string"
is added as type guard since id type can be string
|| string[]
) we get the contact by calling get
method on architectSDK
and update variables we defined earlier. We will call this method inside onMounted
function (during mounted lifecycle).
Now we need to handle submit event. We will call this method handleSubmit
:
There are a couple of steps here so let's explain them. First we collect all user input (except file) and store it inside object. Then we check if file is defined and if it is we upload it. We receive back an object with url
(which is a url of our image) prop. After we take that prop, using destructuring, we monkey patch it to newContact
object as pictureUrl
prop. Then we check if there is route parameter id
and if it is we call update
method on architectSDK
and if there it is not we call create
method. After we have done all of that we navigate user to /contact-list
.
We have everything we need to make our page functional. We only need to bind everything to template. After we do that this is what should we get:
Let's add one contact and check if everything is working.4
After that I am navigated to /contact-list
, where my contact is show. By clicking on contact item (anywhere but on delete button) you should be navigated to the same form but this time everything should fill up with data you defined earlier. Play around a bit to get a feeling how our app works. With all our code ContactForm
looks like this:
Let's also add a header component which we will use for logout.
#
HeaderLet's add a header that will handle logout. Inside component
directory create a new file called Header.vue
. Inside that file we will have following code:
Here we have some markup and styles, and inside script we imported userRouter
and architectSDK
. In logout
method we first logout user using architectSDK
and then we navigate the user back to login screen. Now only thing that is left to do is to import and render Header
component.
Inside ContactForm.vue
import Header
component:
and render it by putting it just bellow opening <template>
tag:
Do the same for ContactList.vue
. Click logout button to see if it works. It should navigate you to login screen.
Only thing that is left for us are route guards.
#
Route guardsWe don't want unauthorized user to access /contact-list
or /contact
route. We also don't want for user to access root route (/
) unless he has logged out. To achieve this behavior we need to implement route guards. We will use beforeEnter
hook which is called before resolve completes. beforeEnter
is a function that accept three arguments to
, which is a route to which user is navigating, from
route from which user is coming from and next
which is a function that need to be called for navigation to continue.
Let's add guards to our router. Go to routes/index.ts
file and import architectSDK
.
We will use isAuthenticated
method that is available on architectSDK
object. Now inside first object in routes array (with path /
), add this function:
This function check if user is authenticated and if he is he will be redirected to route whose name is Contact List
(ContactList
component), but if he isn't route will resolve as expected.
For all other components we want opposite, if user is authenticated route will resolve, and if he isn't he will be redirected to Login
component. This function should be added to all route objects but first one (login route object)
After all our changes routes/index.ts
should look like this:
All our routes are protected. Play around a bit to check if everything is working. Try login out and the accessing ContactForm
and ContactList
components.
#
ConclusionWe have created fully functional (although simple) application in Vue in no time by utilizing powerful Architect SDK
for handling all backend heavy lifting and enabling us to focus only on frontend logic and beautiful UI.
- If you are using VsCode be sure to install [Volar][https://marketplace.visualstudio.com/items?itemname=johnsoncodehk.volar]↩
- In case you don't have an account you can use
https://architect_demo.essentialz.cloud
.This domain is free and is used for demonstration purposes.↩ - If you don't have an account you can use
nikola@essentialz.io
for email andarchitectdemo2021
for password.↩ - If the text is not properly aligned be sure to remove
text-align: center
by deleting it inApp.vue
(in style tag in#app
selector)↩