Nuxt Project from Scratch
Nuxt.js is really easy to get started with. A simple project only needs the nuxt
dependency.
Using Tooltwist Nuxt.js Starter Template
To get started quickly, the Tooltwist has created a starter template.
Download the .zip starter template or install it with vue-cli:
$ vue init tooltwist/nuxt-starter-template my-project |
If vue-cli is not installed, please install it with npm install -g vue-cli
then install the dependencies:
$ cd <project-name> |
and launch the project with:
$ npm run dev |
The application is now running on http://localhost:3000.
Nuxt.js will listen for file changes inside the pages
directory, so there is no need to restart the application when adding new pages.
To discover more about the directory structure of the project: Directory Structure Documentation.
Steps To Create a Project Manually
These steps were used to create the template directory in this repo.
Before Starting
Notes for Atom:
- Use ⌘-t to jump to a file quickly.
- Use shift-⌘-t-\ to show the currently open file in the tree on the left.
Create a default Nuxt project
$ vue init nuxt/starter <project-name> |
Info: replace <project-name>
by the name of the project.
Open browser at http://localhost:3000.
Notes:
- Look at pages/index.vue
- Make a change and see it appear on the browser.
- Notice the 3 sections to the page - <template> contains html, <script> contains Javascript, and <style> contains CSS.
- Look at components/AppLogo.vue
- Contains sections, similar to index.vue, but missing <script> (he <script> and <style> sections are optional).
- Notice that index.vue:
- imports AppLogo.vue
- registers it as a component
- uses it in the HTML, as <app-logo></app-logo>
Add Pug, SCSS
npm install --save-dev pug@2.0.0-beta6 pug-loader node-sass sass-loader |
Modify pages/index.vue
to use Pug:
<template lang="pug"> |
Check the page still works.
Notes:
- uses indentation to indicate nesting, which removes the need for closing tags.
- uses CSS-like syntax for tags definitions:
h1.title#heading Hello
is equivalent to<h1 class=“title” id=“heading”>Hello</h1>
- Everything is assumed a tag unless prfixed with
‘|’
.- Indentation must correctly reflect the nesting of the html elements.
Details about ‘pug’ (previously called ‘jade’) can be found at https://pugjs.org.
Add Bulma / Buefy
npm install nuxt-buefy --save |
In nuxt.config.js
add:
modules: [ |
Bulma = CSS library, like Bootstrap but newer and simpler. It makes creating responsive pages easy. See https://bulma.io/documentation
Modify pages/index.vue
:
Remove the styles (we’ll use Bulma instead):
<style>
</style>Delete the .links section and it’s two buttons.
Do alignment using Bulma classes. Modify the template to match this div section.container.has-text-centered br br app-logo
section.container.has-text-centered
br
h1.title.is-1
| z58b
h2.subtitle.is-4
| Nuxt.js project
Restart the server and see how this looks in the browser.
- Add a box using Bulma, below the subtitle, with the same indent as h2:
br
.box
| Some nice text can go inside this box.
Buefy = Reusable components for VueJS, using Bulma https://buefy.github.io/#/documentation/start
Bulma only provides CSS. Buefy provides components that extends Bulma’s CSS and may also contain Javascript code.
Add the following below the box code from above (with the same indent):br
b-collapse(:open="false")
button.button.is-primary(slot="trigger") Click me!
br
.notification
.content
h3.subtitle.is-4 Blurb
p
| Lorem ipsum dolor sit amet, consectetur adipiscing elit.
| Nulla accumsan, metus ultrices eleifend gravida, nulla nunceu lectus.
| Ut vulputate semper dui. Fusce erat odio, sollicitudin vel.
Note that the button defined here is NOT the same as the default buttons that came with the generated project. The button—green and button—grey came from layouts/default.vue
, whereas classes button and is-primary come from Bulma.
Add Font-awesome
npm install @nuxtjs/font-awesome --save |
In nuxt.config.js
add:
modules: [ |
In pages/index.vue
:
.box |
Restart the server and see how it looks.
Adjusting the Site
Add a Header and Footer
- Copy
/assets/images/*
from new-nuxt-project.zip into your project Add
components/PublicHeader.vue
:<template lang="pug">
// styles defined in assets/scss/main.cscc
nav.navbar.is-dark.is-fixed-top.my-header
.container
.navbar-brand
router-link.navbar-item(to="/")
img(src="@/assets/images/tooltwist-logo-white.png", alt="My Project")
.navbar-burger.burger(data-target="navbarExampleTransparentExample", @click="toggleNavbar()", v-bind:class="{'is-active': isActive}")
span
span
span
//- navbar-brand
#navbarExampleTransparentExample.navbar-menu(v-bind:class="{'is-active': isActive}")
.navbar-start
router-link.navbar-item(to="/publicPage") publicPage
router-link.navbar-item(to="/user/privatePage") privatePage
//- navbar-start
.navbar-end
.navbar-item.has-text-grey-light.is-size-7.has-text-weight-light(v-show="isLoggedIn") [ {{userTitle}} ]
router-link.navbar-item(v-show="!isLoggedIn", to="/login") Login
a.navbar-item(v-show="isLoggedIn", @click="doLogout") Sign out
//- navbar-end
//- navbar-menu
//- container
//- nav
</template>
<script>
export default {
data: function() {
return {
isActive: false
}
},
methods: {
toggleNavbar() {
this.isActive = !this.isActive
},
doLogout: function () {
this.$loginservice.logout()
this.$router.push('/')
}
},
computed: {
isLoggedIn: function () {
if (this.$loginservice && this.$loginservice.user) {
return true;
}
return false;
},
userTitle: function () {
if (this.$loginservice && this.$loginservice.user) {
if (this.$loginservice.user.username) {
return this.$loginservice.user.username
} else {
return this.$loginservice.user.email
}
}
return null
}
}
}
</script>Add
components/PublicFooter.vue
:<template lang="pug">
// styles defined in assets/scss/main.cscc
footer.footer.has-background-dark.has-text-light.has-text-centered.my-footer
| Copyright © ToolTwist 2018
</template>Add
assets/scss/main.scss
:
This contains styles that will be applied across the website.// Positioning of the footer
$my-header-height: 52;
$my-footer-height: 42;
.above-my-footer {
display: block;
top: 0px;
min-height: calc(100vh - #{$my-footer-height}px);
padding-top: #{$my-header-height}px;
}
.my-footer {
height: $my-footer-height;
padding-top: 8px;
padding-bottom: 10px;
border-top: solid 1px #e0e0e0;
border-bottom: solid 1px #e0e0e0;
}Load the styles globally, by adding it to
nuxt.config.js
:module.exports = { … css: [ { src: '@/assets/scss/main.scss', lang: 'sass' } ], }
Update layouts/default.vue
:
- remove .button—green and .button-grey and also their hover classes.
- import the header and footer.
- use header and footer on the page
- use the .above-my-footer and .my-footer classes to position the footer div .above-my-footer public-header nuxt
public-footer.my-footer
The page should now have a header and footer.
Add a Couple of Pages
Add
pages/publicPage.vue
:section.container br br h1.title Public Page br | This page can be seen irrespective of whether the user is logged in. <script> </script> <style> </style>
Add
pages/user/privatePage.vue
:section.container br br h1.title Private Page br | This page should only be seen when the user is logged in. <script> </script> <style> </style>
You should now be able to navigate to these pages using the navbar at the top of the page.
Resize the screen and see how it adjusts. When the screen gets small enough it replaces the menu options with a “burger”, for mobile devices. This functionality is provided by a combination of Bulma and the toggleNavbar() method in PublicHeader.vue.
Install libraries
npm install axios debounce v-hotkey vue-drag-drop vue-froala-wysiwyg vue-split-panel vue-awesome --save |
In nuxt.config.js:
const nodeExternals = require('webpack-node-externals')
const webpack = require('webpack')
module.exports = {
…
build: {
…
extend…
plugins: [
/*
* These are the Webpack plugins (not to be mistaken for Nuxt plugins)
* https://nuxtjs.org/faq/webpack-plugins/
*/
// https://nuxtjs.org/faq/webpack-plugins/
// https://github.com/nuxt/nuxt.js/issues/843
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
})
],
},
…
plugins: [
/*
* These are our Nuxt plugins, defined in the /plugins directory.
*/
{ src: '~plugins/vue-awesome.js', ssr: true },
{ src: '~plugins/vue-loginservice.js', ssr: false },
{ src: '~plugins/vue-contentservice.js', ssr: false },
{ src: '~/plugins/nuxt-drag-drop.js', ssr: true },
{ src: '~/plugins/nuxt-froala.js', ssr: false },
{ src: '~/plugins/nuxt-vue-split-panel.js', ssr: false },
{ src: '~plugins/vue-hotkey.js', ssr: false },
],
modules…
Note that there are two plugins sections. The section inside build refers to webpack plugins, while the other refers to Nuxt plugins.Copy
/plugins/*
from new-nuxt-project.zip into your project- Copy
/lib
from new-nuxt-project.zip into your project
Using Tooltwist Components
- Copy
/protected-config/websiteConfig.js
from new-nuxt-project.zip into your project - If you already have a Tooltwist account, define a new application, get an APIKey, and update
websiteConfig.js
with your new APIKey.
Login page
Create
pages/login.vue
<template lang="pug">
.my-login-page
section
br
br
loginservice-login(@userchange="onUserChange")
</template>
<script>
import PublicHeader from '@/components/PublicHeader.vue'
import PublicFooter from '@/components/PublicFooter.vue'
export default {
name: 'a3-login-page',
components: {
PublicHeader,
PublicFooter
},
methods: {
onUserChange () {
if (this.$loginservice.user) {
this.$router.push('/user/privatePage')
}
}
}
}
</script>
Notice how the menu bar changes when the user logs in:
- Restart the server, and log in with demo/demo.
- The computed value ‘isLoggedIn’ changes from false to true, causing the label to switch from ‘Login’ to ‘Sign out’.
- The computed value ‘userTitle’ gets displayed when the user is logged in.
Redirect Middleware
a) Some pages should be visible only when a user is logged in. |
- Copy
/middleware/only-if-logged-in.js
from new-nuxt-project.zip into your project. - Copy
/pages/loginservice-redirect
from new-nuxt-project.zip into your project. - Add the following to
nuxt.config.js
:module.exports = {
…
css: […],
router: {
middleware: 'only-if-logged-in'
}
}
Note that if you now try to go to privatePage without being logged in, you will be forwarded to the login page. Take a look at the output of npm run dev, and also the Javascript console of your browser and you will see debug messages explaining what the router is doing and why.
If you ever wonder why some pages cannot be accessed, this is the first place to look.
We should stop privatePage from being displayed in the user is not logged in.
- Add the following code in
components/PublicHeader.vue
:#navbarExampleTransparentExample.navbar-menu(v-bind:class="{'is-active': isActive}")
.navbar-start
router-link.navbar-item(to="/publicPage") publicPage
router-link.navbar-item(to="/user/privatePage", v-show="isLoggedIn") privatePage
//- navbar-start
Bounce Page (required for social media login)
When logging in with Facebook/Google/Github/Twitter/LinkedIn the browser redirects to their authentication server, to loginservice, and finally back to an application page with the credentials provided as a URL parameter. > Our application (via vue-loginservice) saves these credentials in a cookie and will stay logged in until the user logs out.
This leaves a problem… after logging out, if the user returns to that page via the browser history or the back button, the URL parameters will log the user back in. To avoid this problem, we need to prevent the URL with the authentication parameters being saved in the browser history.
We do this with a page called loginservice-bounce. Loginservice will jump to this page with the credentials, and this page will save the credentials as a cookie then immediately redirect to the actually required application page, without the URL parameters for the credentials.
Create
/pages/loginservice-bounce.vue
with the following:<template>
<loginservice-bounce-component/>
</template>In
plugins/vue-loginservice.js
un-comment the faceboook line:hints: {
…
sitename: 'ToolTwist',
login: {
facebook: true, <----- here
// google: true,
// github: true,
…
Account Page
Add a page
pages/user/account
:<template lang="pug">
.a3-account-page
.a3-above-the-footer
a3-header
section.my-header
.container
br
.my-heading
| My Profile
br
br
.container
loginservice-user-details(:tenant="$loginservice.user.tenant", :user-id="$loginservice.user.id", change-password)
a3-footer.a3-the-footer
</template>Add a link to this new page to
/components/PublicHeader
:router-link.navbar-item(to="/user/account", v-show="isLoggedIn") Account
Next Steps
You now have a demonstration application running, however it’s running against the demonstration user database. To create your own application:
Go to tooltwist.com and register, click on the link in the verification email, create a new application, and allocate an APIKey. Replace the default ApiKey in /protected-config/websiteConfig.js and restart your server.
Create a Mailchimp/Mandrill account, and install the registration and forgot-password templates from new-nuxt-project.zip. You will also need to verify that you own the domain used to send emails to users.
If you wish to allow users to log in with a social media account, you will need to register the application with Facebook/Github/etc and enter the details into A3. The help links on the config pages explains how to configure Facebook, etc.