Pearon Vue Please Ensure That Authentication Web Service Is Up Then Try Login Again

Look for the 🛠️️ emoji if you'd like to skim through the content while focusing on the build steps.

In this commodity, you'll learn how to build a Vue website that will showcase events hosted by an organization. Users will sign in to view the details folio of an event, then you'll also add together hallmark to the website. Yous'll larn some Vue background, Vue application structure, creating components, setting upwards routing, and styling with Bulma.

You can notice the final code in this GitHub repository if you'd like to see the terminate event at present.

❗ If y'all already have a Vue awarding and simply desire to add authentication, bound to this section to learn how.

Vue events authentication app

⚡️ This is a two-part tutorial! Function ane does not embrace authorization. You lot will set the stage for office two by edifice the website and integrating user login. And then in part two, yous'll larn how to only show certain data if a user is signed in and has been authorized to access that data.

Requirements

This awarding is using the latest versions (at the time of writing — viii/eight/2020) of the following:

  • Vue.js ( 2.6 .xi )
  • Node.js ( 12.eighteen .ii )

Note: This tutorial uses Vue 2. The release for Vue 3 is scheduled for the third quarter of 2020, but that doesn't mean yous shouldn't start with Vue 2! This tutorial lays down the foundations of Vue that will comport over into the next release every bit well.

Yous tin download Node.js hither, which automatically installs npm, a Node packet director, as well.

Npm allows you to chop-chop install millions of libraries and easily manage whatever dependencies you have in your applications. Y'all'll meet how benign this is soon.

This tutorial will walk you through how to create everything from scratch, so you don't need to have any prior knowledge of Vue or Node to follow along! You lot will, yet, need a code editor (VS Code is my favorite) and access to the terminal to follow this tutorial.

Let's go started!

Why Learn Vue?

Vue history and popularity

Vue.js is a JavaScript framework created past Evan Y'all that has blown up in popularity over the last few years.

Vue State of JS Survey - Popularity Source: 2018 State of JS Survey Interest in Vue.js has virtually tripled from 2016 to 2018 with an increment in those who are interested in learning it and those who would use it again.

Vue State of JS Survey - Framework comparison Source: 2018 State of JS Survey In 2018, the majority of people surveyed accept heard of Vue.js and would like to learn it or accept already used it and would use it over again.

Evan You launched the project subsequently having worked on several AngularJS projects when he worked for Google. Vue isn't backed past huge companies like Google (Angular) or Facebook (React), only every bit you can meet in the charts above, Vue is still able to stand on its own amid the superlative JavaScript frameworks.

Because of Evan You'due south history at Google, you might fifty-fifty observe some similarities between Vue and AngularJS (old Angular).

I figured, what if I could just extract the part that I really liked about Athwart and build something really lightweight.

— Evan Yous on creating Vue.js

Vue vs. Angular vs. React

One of the best things about Vue is the low barrier to entry.

Vue State of JS Survey - Most liked Source: 2018 Land of JS Survey For developers who chose "would employ again", their favorite Vue.js features were the like shooting fish in a barrel learning curve, elegant programming style, and good documentation.

A lot of other frameworks may require (or strongly suggest) that you learn their own syntax or integrate other technologies (east.yard., TypeScript for Angular or JSX for React). Of course, at that place's nix incorrect with this, merely it does make information technology a little harder for a beginner to go started when they have to know all the things right away. Vue allows y'all to use TypeScript or even JSX if y'all'd like, but it's not required. This isn't to say one method is ameliorate than the other, but this HTML-like syntax is 1 possible explanation equally to why developers detect Vue easier to learn initially.

Once again, when it comes to comparing the pop frameworks, none of them are better than the others. At the end of the twenty-four hours, it comes downward to what yous experience most comfy using.

Vue is a cracking choice if you're looking to get something upwards and running quickly. It has great community back up, robust documentation in several languages, and information technology's just fun to utilize!

Setting Up

Alright, permit'south get started with some code.

First, open up up your terminal and switch to the directory that you desire to store the project in.

You're going to create this new project using the Vue CLI.

🛠️️ The Vue CLI tool helps get up and running with Vue speedily by letting you lot choose from pre-configured build setups. To utilise the CLI, run the following in your terminal:

          npx @vue/cli create events-app        

This creates a new folder called events-app and starts the installation procedure in that folder.

Note: npx allows you to use the Vue CLI without installing information technology globally. It's available in versions of npm >= v.2.0.

The CLI will now ask you a few questions and then that it tin can fix up the app correctly. The options that this tutorial uses are listed below. Press ENTER to select.

🛠️️ Footstep 1: Option a preset — Manually select features

          ? Please pick a preset:   default            (babel, eslint)            >            Manually            select            features        

🛠️️ Step 2: Check the features needed — Babel, Router, CSS Pre-processors, Linter/Formatter

Printing Infinite to select multiple features and ENTER once you have all features selected.

          ? Cheque the features needed            for            your project:            (Press            <space>            to select,            <a>            to toggle all,            <i>            to invert selection)            ◉ Boom-boom  ◯ TypeScript  ◯ Progressive Spider web App            (PWA)            Support  ◉ Router  ◯ Vuex  ◉ CSS Pre-processors  ◉ Linter / Formatter  ◯ Unit Testing  ◯ E2E Testing        

🛠️️ Step 3: History mode — Y

          ? Use            history            mode            for            router?            (Requires proper server setup            for            index fallback            in            production)            (Y/n)            Y        

Note: This will remove the default hash (#) from URLs.

🛠️️ Step 4: CSS pre-processor — Sass/ SCSS ( with sprint-sass)

          ? Pick a CSS pre-processor            (PostCSS, Autoprefixer and CSS Modules are supported by default):            (Use arrow keys)            ❯ Sass/SCSS            (with dart-sass)            Sass/SCSS            (with node-sass)            Less   Stylus        

Notation: Dart Sass is the chief implementation of Sass.

🛠️️ Step 5: Pick a linter/formatter —ESLint with fault prevention only

          ? Pick a linter / formatter config:            (Utilise arrow keys)            ❯ ESLint with error prevention only   ESLint + Airbnb config   ESLint + Standard config   ESLint + Prettier        

🛠️️ Pace 6: Additional lint features — Lint and fix on commit

          ? Option additional lint features:   ◯ Lint on salvage ❯ ◉ Lint and set up on commit        

🛠️️ Footstep 7: Config location — In dedicated config files

          ? Where            do            you prefer placing config            for            Babel, PostCSS, ESLint, etc.?            (Use arrow keys)            ❯ In dedicated config files   In packet.json        

🛠️️ Step 8: Salvage as preset — N

          ? Save this as a preset            for            future projects?            (y/North)            N        

Now the CLI will configure your awarding. It volition likewise install the dependencies, so requite it a little time (~30 seconds).

🛠️️ Once information technology'south finished, you tin beginning the application by running npm run serve or yarn serve:

                      cd            events-app            npm            run serve        

See it in action at http: / / localhost:8080 / ! Go ahead and keep this running in the groundwork so that you lot can see the app's progress every bit you go through the tutorial.

"The Vue CLI helps you become up and running with Vue.js in seconds!"

Tweet

Tweet This

Vue App Architecture

Now open up up your project in your code editor, and you lot'll see some default files that the CLI created. Allow's make clean some of this upwardly.

🛠️️ Delete these files:

src/components/HelloWorld.vue

src/assets/logo.png

🛠️️ Now open upward src/views/Home.vue and supersede information technology with the following:

                                                    <template              >                                                      <div              class                              =                "habitation"                            >                                                      </div              >                                                      </template              >                                                      <script              >                                                      export                default                {                name:                'home'                ,                components:                {                }                ,                }                ;                                                                    </script              >                              

This will leave y'all with a blank homepage and a nav bar with two links: Home and About.

Vue CLI starter

Let's have a quick look at the of import remaining files and so that you tin meet how they work together.

main.js

The first file to note is the src/main.js file. This volition exist the entry betoken for your Vue awarding.

                      import            Vue            from            'vue'            ;            import            App            from            './App.vue'            ;            import            router            from            './router'            ;            Vue.config.productionTip            =            false            ;            new            Vue            (            {            router,            render            :            (            h            )            =>            h            (App)            ,            }            )            .            $mount            (            '#app'            )            ;                  

You lot have your imports at the top of the file:

  • Vue itself
  • The App component (which we'll go over in a moment)
  • The router (created by the CLI).

Adjacent, you lot have Vue.config.productionTip = false , which sets the app in development mode.

And finally, you're creating a new Vue instance at the bottom.

A Vue instance is required for every Vue application. The Vue instance will accept an options object that contains information about your application, such as the DOM chemical element that the Vue instance will be mounted on, data the instance volition employ, functions that run at some point during the instance's lifecycle, and more.

In this instance, the Vue instance is using the router, rendering the app with the App template (more on this before long), and and then mounting it to the DOM element with the id of app.

index.html

Next, open up up public /index.html, and you'll run across a fairly standard index HTML file.

                                    <!              DOCTYPE              html              >                                                      <html              lang                              =                "en"                            >                                                      <head              >                                                      <meta              charset                              =                "utf-viii"                            />                                                      <meta              http-equiv                              =                "X-UA-Compatible"                            content                              =                "IE=border"                            />                                                      <meta              name                              =                "viewport"                            content                              =                "width=device-width,initial-scale=1.0"                            />                                                      <link              rel                              =                "icon"                            href                              =                "<%= BASE_URL %>favicon.ico"                            />                                                      <title              >            <%= htmlWebpackPlugin.options.title %>                              </title              >                                                      </head              >                                                      <body              >                                                      <noscript              >                                                      <potent              >            We're sorry simply <%= htmlWebpackPlugin.options.title %> doesn't work         properly without JavaScript enabled. Please enable it to         continue.                              </strong              >                                                      </noscript              >                                                      <div              id                              =                "app"                            >                                                      </div              >                        <!-- built files volition exist auto injected -->                                          </trunk              >                                                      </html              >                              

If you look toward the bottom of the file, y'all'll see where that mysterious #app in the src/main.js file comes into play. That div is where the Vue instance will be injected.

App.vue

The adjacent important file is src/App.vue. This is your get-go component!

You don't demand to know all of the details of this file nonetheless, just know that this is the side by side "building block" of your Vue application. When you reviewed the src/main.js file, yous saw it was importing and rendering a template called App, which is what this file is.

Before diving into the details of this file, permit's first larn about what a component fifty-fifty is by reviewing Vue's component system.

Using Components in Vue

The concept of components, in general, is sometimes over-complicated, but in reality, it's quite simple. A component is a modular and reusable block of code. It contains all of the HTML, JavaScript, and CSS that it requires for its functionality.

Imagine y'all have a elementary website that has two pages: a abode page and a portfolio page.

  • Home page — Contains some images, bones information, and a carousel with some testimonials
  • Portfolio page — Contains some images and descriptions of your work

You decide that you want your Portfolio page to include all of the testimonials that are on your homepage as well.

In this scenario, you can copy/paste all of the code required for the testimonials onto both pages, but and then you'd be breaking the gilt Dry out ("Don't Repeat Yourself") rule.

This is where the idea of components starts to make sense. Instead of creating duplicate code, you can pull that testimonial lawmaking out and package it into its own file. This standalone clamper of code volition contain the HTML, the styles, and whatsoever JS needed to make the carousel work.

Now, whenever you want to insert that testimonial "component" somewhere, all you take to do is import that component!

Your awarding will begin to take on a sort of tree structure. You'll have your "root" component, which in this case is src/App.vue, and that component will import other components. All of these small chunks of code come together to build your application.

Vue component tree structure

Source: Vuejs.org

Now that you have the idea of components down, allow'south see what a Vue component looks like.

Vue components

There are a lot of ways to structure components in Vue. Allow'south look at the most popular way: Single file components.

Unmarried file components bundle up the template, logic, and styles used by a component into one file with a .vue extension. This method requires a build setup, which the CLI has already fix for y'all. Allow's have a look at an example component.

                                                    <template              >                                                      <div              course                              =                "our-component"                            >                                                      <h1              >            I'yard a component!                              </h1              >                                                      <ChildComponent              />                                                      </div              >                                                      </template              >                                                      <script              >                                                      import                ChildComponent                from                '@/components/ChildComponent'                ;                export                default                {                name:                'MyComponent'                ,                components:                {                ChildComponent,                }                ,                }                ;                                                                    </script              >                                                      <style              lang                              =                "scss"                            scoped              >                                                      .our-component                {                text-align                :                center;                }                                                                    </style              >                              

The HTML is enclosed in a <template> < /template> tag. Inside, you'll see <ChildComponent / > . This is an example of how you tin use another component inside of this 1.

Note: You must always have a parent <div> chemical element that encloses the residuum of the HTML later on the template tag.

Next, there is a script tag, which includes:

  • An import statement that allows y'all to import and utilize other components within this component.
  • An export object that allows you lot to define and export this named component to reuse it beyond your application.
  • A components object inside the export where yous can list all of the child components used in this component.

Note: This is a pretty bones example, but in that location are loads of other component options you lot can include in your component object.

Next, at that place is the <way> tag. The styles defined here apply to this component and all of its children. In this example, however, there is a scoped holding added, which restricts these styles to only this component. And finally, you're also specifying that you're using SCSS every bit the stylesheet linguistic communication.

Let's have a wait at that App.vue file once more now that you take a lilliputian more background nearly component structure.

App.vue

Open up up src/App.vue.

                                                    <template              >                                                      <div              id                              =                "app"                            >                                                      <div              id                              =                "nav"                            >                                                      <router-link              to                              =                "/"                            >            Home                              </router-link              >                        |                                          <router-link              to                              =                "/about"                            >            About                              </router-link              >                                                      </div              >                                                      <router-view              />                                                      </div              >                                                      </template              >                                                      <style              lang                              =                "scss"                            >                                                      #app                {                font-family                :                'Avenir'                ,                Helvetica,                Arial,                sans-serif;                -webkit-font-smoothing                :                antialiased;                -moz-osx-font-smoothing                :                grayscale;                text-marshal                :                centre;                color                :                #2c3e50;                }                #nav                {                padding                :                30px;     a                {                font-weight                :                bold;                color                :                #2c3e50;       &.router-link-verbal-active                {                color                :                #42b983;                }                }                }                                                                    </style              >                              

This is the first view template to be rendered in your awarding. The residual of the components will start from here.

Take a look at <router-link> and <router-view> . This is how your awarding will handle routing. If you chose to have the router born using the CLI, yous already accept a nice template here to use!

We'll go more in-depth on routing soon, but merely know that when you click on i of those links, this aforementioned template will still be rendered, but <router-view / > will be replaced with whatever route is active. In this case, either the Home or Nearly component. This is how you'll be able to reuse this same layout and navbar across the unabridged awarding.

Attempt clicking those links to see for yourself!

Building Application Components

Now that you know the anatomy of a Vue application and how to build a component, it'south fourth dimension to showtime edifice!

🛠️️ Make sure yous're still in the events-app folder in the terminal and then create these files/folders:

                      cd            src/views            touch            EventSingle.vue        

Notation: If you're on Windows, the affect command may not piece of work, depending on your setup. You tin can always create the file manually instead.

🛠️️ At present make the components that volition be shared among these pages:

                      cd            ../components            affect            EventsList.vue            touch            EventCard.vue            mkdir            partials            bear on            partials/Nav.vue        

That should do it for now! Allow'south start filling these files in and become over the purpose of each file.

Using Bulma for Styling

To make styling a little easier, y'all're going to utilize Bulma, which is an open-source CSS framework.

🛠️️ Switch to the events-app binder then enter the command below to install Bulma:

                      cd            ../..            npm            install            bulma        

🛠️️ Now open up src/main.js in your editor and import Bulma by adding this to the top of the file afterward all of the other imports:

                      // ...            import            'bulma/css/bulma.css'            ;            // ...                  

🛠️️ Start the app upwardly one more than time then that you tin can encounter your piece of work in progress as you keep building in the next section.

                      npm            run serve        

Y'all can just get out this running in the background for the rest of this tutorial, and you lot'll always be able to view it in the browser at http: / /localhost: 8080 .

Home Component

Now let's start working on the components.

🛠️️ Open up the Home Component in src/views/Home.vue and paste in the following:

                                                    <template              >                                                      <div              class                              =                "dwelling house"                            >                                                      <section              class                              =                "hero is-night"                            >                                                      <div              class                              =                "hero-body"                            >                                                      <div              class                              =                "container"                            >                                                      <h1              class                              =                "championship"                            >            Welcome to the Animal Rescue League                              </h1              >                                                      <h2              class                              =                "subtitle"                            >                        Make sure you check out our upcoming events below                                          </h2              >                                                      <div              course                              =                "button-block"                            >                                                      <button              class                              =                "button is-xl is-dark"                            >                        Sign Upwardly to Scan Events                                          </button              >                                                      </div              >                                                      </div              >                                                      </div              >                                                      </department              >                                                      </div              >                                                      </template              >                                                      <script              >                                                      export                default                {                name:                'home'                ,                components:                {                }                ,                }                ;                                                                    </script              >                                                      <style              lang                              =                "scss"                            scoped              >                                                      .hero                {                text-align                :                center;                groundwork-epitome                :                                  url                  (                  'https://cdn.auth0.com/weblog/vue-meetup/event-banner.png'                  )                                ;                background-size                :                cover;                background-position                :                center;                background-repeat                :                no-repeat;                tiptop                :                400px;                }                .hero-torso .championship                {                text-shadow                :                4px 4px 4px                rgba                (0,                0,                0,                0.6)                ;                padding                :                40px 0 20px 0;                font-size                :                60px;                }                .subtitle                {                text-shadow                :                4px 4px 4px                rgba                (0,                0,                0,                0.seven)                ;                font-size                :                30px;                }                .push-block                {                text-align                :                heart;                margin-left                :                auto;                margin-right                :                automobile;                width                :                100%;                position                :                absolute;                lesser                :                -150px;     .button                {                margin-correct                :                50px;                padding-left                :                50px;                padding-right                :                50px;                }                .welcome                {                width                :                400px;                padding                :                10px;                margin-left                :                car;                margin-right                :                auto;                }                }                .is-twoscore                {                font-size                :                1.7rem;                }                                                                    </style              >                              

This will give yous a large banner and push at the peak of the folio. The button will lead to the signup form, which you'll wire upwards later.

About Component

The Virtually component comes default with the Vue install, only let's just bandbox it up a trivial.

🛠️️ Open up src/views/About.vue, and supercede all of it with this:

                                                    <template              >                                                      <div              class                              =                "nigh"                            >                                                      <div              class                              =                "hero is-primary"                            >                                                      <div              class                              =                "hero-torso"                            >                                                      <div              grade                              =                "container"                            >                                                      <h1              form                              =                "title is-size-one"                            >            About Creature Rescue League                              </h1              >                                                      </div              >                                                      </div              >                                                      </div              >                                                      <div              class                              =                "container"                            >                                                      <p              form                              =                "org-clarification is-size-4"                            >                        Lorem ipsum dolor sit amet consectetur adipisicing elit. Eius quia         aperiam eligendi dolorum reprehenderit ea amet, aliquid dolorem beatae,         iste aliquam ullam. Sequi ab eligendi consectetur neque laudantium,         libero asperiores.                                          </p              >                                                      <p              course                              =                "org-description is-size-iv"                            >                        Lorem ipsum dolor sit amet consectetur adipisicing elit. Eius quia         aperiam eligendi dolorum reprehenderit ea amet, aliquid dolorem beatae,         iste aliquam ullam. Sequi ab eligendi consectetur neque laudantium,         libero asperiores.                                          </p              >                                                      </div              >                                                      </div              >                                                      </template              >                                                      <style              lang                              =                "scss"                            scoped              >                                                      .org-description                {                margin-top                :                50px;                }                                                                    </style              >                              

Nav Component

🛠️️ Now fix up the nav. Open up src/components/partials/Nav.vue and paste this in:

                                                    <template              >                                                      <nav              form                              =                "navbar container"                            function                              =                "navigation"                            aria-label                              =                "principal navigation"                            >                                                      <div              class                              =                "navbar-make"                            >                                                      <a              course                              =                "navbar-particular"                            href                              =                "/"                            >                                                      <potent              class                              =                "is-size-4"                            >            Animate being Rescue League                              </strong              >                                                      </a              >                                                      <a              function                              =                "button"                            grade                              =                "navbar-burger burger"                            aria-label                              =                "card"                            aria-expanded                              =                "false"                            data-target                              =                "navbarBasicExample"                            >                                                      <span              aria-hidden                              =                "truthful"                            >                                                      </span              >                                                      <span              aria-subconscious                              =                "truthful"                            >                                                      </bridge              >                                                      <span              aria-hidden                              =                "true"                            >                                                      </span              >                                                      </a              >                                                      </div              >                                                      <div              id                              =                "navbar"                            class                              =                "navbar-bill of fare"                            >                                                      <div              class                              =                "navbar-offset"                            >                                                      <router-link              to                              =                "/"                            grade                              =                "navbar-item"                            >            Home                              </router-link              >                                                      <router-link              to                              =                "/nigh"                            class                              =                "navbar-item"                            >            Well-nigh                              </router-link              >                                                      </div              >                                                      <div              class                              =                "navbar-end"                            >                                                      <div              form                              =                "navbar-item"                            >                                                      <div              class                              =                "buttons"                            >                                                      <a              class                              =                "button is-night"                            >                                                      <strong              >            Sign In                              </strong              >                                                      </a              >                                                      </div              >                                                      </div              >                                                      </div              >                                                      </div              >                                                      </nav              >                                                      </template              >                                                      <script              >                                                      export                default                {                name:                'Nav'                ,                }                ;                                                                    </script              >                                                      <style              lang                              =                "scss"                            scoped              >                                                      nav                {                margin-tiptop                :                25px;                margin-lesser                :                30px;     a                {                font-weight                :                bold;                colour                :                #2c3e50;       &.router-link-exact-active                {                color                :                #d88d00;                }                }                }                                                                    </style              >                              

If you refresh, you'll discover the nav doesn't change. That's because you're not really using this component anywhere.

🛠️️ Open src/App.vue and replace it with this:

                                                    <template              >                                                      <div              id                              =                "app"                            >                                                      <nav              />                                                      <router-view              />                                                      </div              >                                                      </template              >                                                      <script              >                                                      import                Nav                from                './components/partials/Nav.vue'                ;                consign                default                {                proper name:                'app'                ,                components:                {                Nav,                }                ,                }                ;                                                                    </script              >                                                      <style              lang                              =                "scss"                            >                                                      #app                {                font-family unit                :                'Avenir'                ,                Helvetica,                Arial,                sans-serif;                -webkit-font-smoothing                :                antialiased;                -moz-osx-font-smoothing                :                grayscale;                colour                :                #2c3e50;                }                                                                    </style              >                              

Since y'all're defining the router links in the Nav component at present, you can become rid of them in this file. All y'all need to do is import the Nav component. The old #nav styles accept as well been deleted since the Nav component has its own styles.

Now, if you refresh, y'all should see your new nav! Y'all can even click around, and information technology'll open upwards the dissimilar routes you've specified in the Nav component.

Next upward, you'll add a component to your Home page.

EventsList Component

🛠️️ Open src/components/EventsList.vue and paste this in:

                                                    <template              >                                                      <div              class                              =                "events container"                            >                                                      <h2              class                              =                "subtitle is-3"                            >            Cheque out our upcoming events                              </h2              >                                                      <div              class                              =                "columns is-multiline"                            >                                                      <div              class                              =                "column is-one-quarter"                            >                                                      <EventCard              />                                                      </div              >                                                      </div              >                                                      </div              >                                                      </template              >                                                      <script              >                                                      import                EventCard                from                '@/components/EventCard'                ;                export                default                {                name:                'EventsList'                ,                components:                {                EventCard,                }                ,                }                ;                                                                    </script              >                                                      <style              lang                              =                "scss"                            scoped              >                                                      .events                {                margin-summit                :                100px;                text-align                :                heart;                }                                                                    </style              >                              

This will create a block of cards that takes up 1/4 of the row (by using Bulma'due south column with is-one-quarter). Each of those one/iv slots will be filled with the EventCard component.

🛠️️ For at present, just repeat that menu code a couple of times and then that y'all can run across how the structure will wait.

                                                    <div              class                              =                "columns is-multiline"                            >                                                      <div              class                              =                "column is-one-quarter"                            >                                                      <EventCard              />                                                      </div              >                                                      <div              class                              =                "column is-1-quarter"                            >                                                      <EventCard              />                                                      </div              >                                                      </div              >                              

Side by side you lot need to:

  1. Import this into the Domicile component
  2. Add it to the list of components that Home uses in consign default { }
  3. Slot it into the appropriate place in the Dwelling house template

🛠️️ Open up src/views/Home.vue and replace the script section with this:

                                                    <script              >                                                      import                EventsList                from                '../components/EventsList'                ;                consign                default                {                proper noun:                'home'                ,                components:                {                EventsList,                }                ,                }                ;                                                                    </script              >                              

🛠️️ Now in the template department, call the component with <EventsList / > .

                                                    <template              >                                                      <div              class                              =                "domicile"                            >                                                      <section              class                              =                "hero is-nighttime"                            >                        <!-- ... -->                                          </section              >                                                      <EventsList              />                                                      </div              >                                                      </template              >                              

You should now meet the subtitle text, "Bank check out our upcoming events", rendered below the homepage banner. Still, none of the individual cards are showing all the same. That'due south because y'all haven't created them.

Vue and auth app partial homepage

Event Bill of fare Component

🛠️️ Open up src/components/EventCard.vue and paste in:

                                                    <template              >                                                      <div              class                              =                "event-card"                            >                                                      <div              class                              =                "card"                            >                                                      <div              class                              =                "card-content"                            >                                                      <h2              class                              =                "is-size-iv has-text-weight-assuming"                            >            Event name                              </h2              >                                                      <minor              grade                              =                "upshot-date"                            >            Consequence date                              </small              >                                                      <bridge              >            Event location                              </span              >                                                      </div              >                                                      </div              >                                                      </div              >                                                      </template              >                                                      <script              >                                                      export                default                {                }                ;                                                                    </script              >                                                      <style              lang                              =                "scss"                            scoped              >                                                      .carte                {                groundwork-image                :                                  url                  (                  'https://placekitten.com/400/400'                  )                                ;                height                :                200px;                background-position                :                centre;                background-size                :                embrace;                text-marshal                :                center;                }                .card-content                {                padding-top                :                50px;                position                :                accented;                colour                :                #fff;                background-colour                :                rgba                (0,                0,                0,                0.35)                ;                peak                :                0;                padding                :                10px;                height                :                200px;                width                :                100%;     span                {                font-size                :                18px;                text-align                :                centre;                width                :                100%;                position                :                accented;                lesser                :                10px;                correct                :                0;                }                h2                {                margin-meridian                :                10px;                }                }                .result-date                {                background-color                :                #151515;                colour                :                #fff;                font-size                :                0.75em;                padding                :                2px 10px;                position                :                accented;                peak                :                0;                right                :                0;                }                                                                    </style              >                              

You should now run across the two cards filled in with some mock content.

Vue events card component

Of grade, you're going to want each bill of fare to be unique and represent an bodily event. You'll come up back to this component soon to run into how y'all can feed data in and accomplish this.

For now, finish setting up the residuum of your components.

Event Single Component

🛠️️ Open up src/views/EventSingle.vue and paste this in:

                                                    <template              >                                                      <div              class                              =                "event-single"                            >                                                      <department              class                              =                "hero is-primary"                            >                                                      <div              form                              =                "hero-trunk"                            >                                                      <div              class                              =                "container"                            >                                                      <h1              class                              =                "title"                            >            Event proper name                              </h1              >                                                      <h2              grade                              =                "subtitle                "                            >            Outcome date                              </h2              >                                                      </div              >                                                      </div              >                                                      </section              >                                                      <department              class                              =                "event-content"                            >                                                      <div              grade                              =                "container"                            >                                                      <p              class                              =                "is-size-four description"                            >            Outcome description                              </p              >                                                      <p              form                              =                "is-size-four"                            >            Location:                              </p              >                                                      <p              class                              =                "is-size-4"                            >            Category:                              </p              >                                                      <div              class                              =                "event-images columns is-multiline has-text-centered"                            >                                                      <div              form                              =                "column is-ane-third"                            >            IMAGE PLACEHOLDER                              </div              >                                                      </div              >                                                      </div              >                                                      </department              >                                                      </div              >                                                      </template              >                                                      <script              >                                                      export                default                {                }                ;                                                                    </script              >                                                      <mode              lang                              =                "scss"                            scoped              >                                                      .result-single                {                margin-height                :                30px;                }                .hero                {                margin-bottom                :                70px;                }                .event-images                {                margin-height                :                50px;                }                .clarification                {                margin-bottom                :                30px;                }                                                                    </way              >                              

This will be the page that appears when a user clicks on an event from the list on the homepage. Considering this folio doesn't actually be yet, this is a groovy time to revisit your router.

Router paths

🛠️️ Open up src/router/index.js and replace the contents with this:

                      import            Vue            from            'vue'            ;            import            Router            from            'vue-router'            ;            import            Habitation            from            '../views/Home.vue'            ;            Vue.            use            (Router)            ;            export            default            new            Router            (            {            manner:            'history'            ,            base:            process.env.            BASE_URL            ,            routes:            [            {            path:            '/'            ,            name:            'home'            ,            component:            Home,            }            ,            {            path:            '/virtually'            ,            proper name:            'about'            ,            component            :            (            )            =>            import            (            '../views/About.vue'            )            ,            }            ,            {            path:            '/result/:id'            ,            name:            'eventSingle'            ,            component            :            (            )            =>            import            (            '../views/EventSingle.vue'            )            ,            }            ,            ]            ,            }            )            ;                  

Press save and navigate to http: / /localhost: 8080 /event/ 1 . You should now see the contents of the EventSingle component!

Event single page placeholder

If yous expect under path in the router, you lot'll see a route parameter, :id. This is how you'll create separate pages for every event. Once you add in the information, you tin navigate to whatever event past appending its id to the end of / consequence / .

Allow's piece of work on calculation information now.

Adding Data to your Vue App

To keep this tutorial focused on the basics of Vue, yous're but going to create an array of objects (where each object is an upshot) and store information technology in the component that needs it.

Ideally, you would want to pull information from an API to dynamically fill in the block of cards, but that's a fiddling outside the scope of this tutorial. If yous're interested in making calls to an API using Vue and securing whatever data yous have, make sure yous read office two of this tutorial where y'all'll exercise merely that!

Allow'due south revisit those 3 components that were missing dynamic data and see how to make full them in.

EventsList Component

🛠️️ Head dorsum to the EventsList component in src/components/EventsList.vue and ringlet downwardly to the bottom where the <script> tag starts. Replace the entirety of <script> < /script> with this:

                                                    <script              >                                                      import                EventCard                from                '@/components/EventCard'                ;                export                default                {                name:                'EventsList'                ,                components:                {                EventCard,                }                ,                data                (                )                {                render                {                event:                {                }                ,                events:                [                {                id:                1                ,                proper noun:                'Charity Ball'                ,                category:                'Fundraising'                ,                clarification:                'Spend an elegant night of dinner and dancing with usa as we enhance money for our new rescue farm.'                ,                featuredImage:                'https://placekitten.com/500/500'                ,                images:                [                'https://placekitten.com/500/500'                ,                'https://placekitten.com/500/500'                ,                'https://placekitten.com/500/500'                ,                ]                ,                location:                '1234 Fancy Ave'                ,                appointment:                '12-25-2019'                ,                time:                '11:30'                ,                }                ,                {                id:                ii                ,                name:                'Rescue Center Appurtenances Drive'                ,                category:                'Adoptions'                ,                description:                'Come to our donation bulldoze to help us furnish our stock of pet food, toys, bedding, etc. Nosotros will have alive bands, games, nutrient trucks, and much more.'                ,                featuredImage:                'https://placekitten.com/500/500'                ,                images:                [                'https://placekitten.com/500/500'                ]                ,                location:                '1234 Domestic dog Alley'                ,                date:                'xi-21-2019'                ,                time:                '12:00'                ,                }                ,                ]                ,                }                ;                }                ,                }                ;                                                                    </script              >                              

The only affair that's irresolute here is the improver of a data ( ) function that returns an empty object chosen event and an array called events.

  • The events array holds 2 objects that each correspond to a different event
  • The consequence object will hold a single outcome to be passed to the child component, EventCard

Next, you need to modify the HTML portion of this file to loop through these events and then send each one downward the chain to exist rendered by the child component, EventCard.

🛠️️ Replace everything betwixt the <template> < /template> tags with:

                                                    <template              >                                                      <div              class                              =                "events container"                            >                                                      <h2              class                              =                "subtitle is-3"                            >            Check out our upcoming events                              </h2              >                                                      <div              grade                              =                "columns is-multiline"                            >                                                      <div              v-for                              =                "event in events"                            :effect                              =                "event"                            :central                              =                "result.id"                            course                              =                "cavalcade is-one-quarter"                            >                                                      <router-link              :to                              =                "                '/upshot/'                + upshot.id"                            >                                                      <EventCard              :event                              =                "event"                            />                                                      </router-link              >                                                      </div              >                                                      </div              >                                                      </div              >                                                      </template              >                              

Let's take a closer look at the cake of code that inverse (spaced out for meliorate readability).

                                                    <div              v-for                              =                "consequence in events"                            :issue                              =                "event"                            :key                              =                "event.id"                            course                              =                "column is-one-quarter"                            >                                                      <router-link              :to                              =                "                '/effect/'                + event.id"                            >                                                      <EventCard              :consequence                              =                "event"                            />                                                      </router-link              >                                                      </div              >                              

You lot can loop over the events in the events array using the Vue directive five- for . This acts as a for loop and pulls out each event individually then that information technology can be rendered separately by the EventCard component.

This is also the first fourth dimension you're seeing a directive!

A directive is a special type of markup that you can utilise to practice something to a DOM element. In Vue, these all begin with v- . Then, in this case, you're using the shorthand 5- for to create a for loop. Another common instance is v- if , which creates a conditional inside the template. You lot tin encounter a total list of Vue directives hither.

Here, y'all're using Vue'due south :key attribute to bind a unique fundamental (in this instance, id) to each consequence.

Note: In Vue, : is shorthand for v-bind.

You lot're also binding the value of the current result in the for loop to the event object yous created earlier with :event= "event" . This will allow you to send this object downward to the child component to be rendered separately.

Next, yous're using <router-link> to make each card into a clickable link. Back in the router.js file, you created the route / event / :id with a road parameter of id. This road uses the EventSingle component, which volition render the folio for each specific event. You lot're using the id from each event to make certain information technology links to the correct effect page.

Go ahead and click ane, and you'll run across that it links to the single event page that you created.

Finally, you're calling the EventCard component to fill in each individual card. Y'all're sending a variable called effect down to the card and then passing in the current event from the for loop.

Right now, the EventCard component isn't prepared to take data, so allow'south gear up that.

EventCard Component

🛠️️ Open up the EventCard component in src/components/EventCard.vue. Scroll down to where you take the <script> < /script> tags and replace it with this:

                                                    <script              >                                                      consign                default                {                props:                [                'upshot'                ]                ,                }                ;                                                                    </script              >                              

This component has something new: props.

Whenever your component is expecting data, you should add that data'south variable name to the props option. props is kind of a funny word, but information technology really just ways property.

A prop is a belongings on a component.

This is where you define the information that the component should be expecting from its parent component.

This result prop was passed to the current component from the parent component. This allows you to display information technology in the HTML template using curly braces:

          {{ effect }}        

This is known as interpolation. Using string interpolation, yous're able to display the value of event in your template. This is a course of data-binding, which means that whenever the value of outcome in data changes, the displayed value in the template will besides change.

🛠️️ To see this in action, go alee and update the template department of EventCard.vue with this:

                                                    <template              >                                                      <div              class                              =                "event-card"                            >                                                      <div              course                              =                "carte du jour"                            >                                                      <div              class                              =                "card-content"                            >                                                      <h2              form                              =                "is-size-four has-text-weight-bold"                            >            {{ event.name }}                              </h2              >                                                      <small              class                              =                "event-appointment"                            >            {{ consequence.date }}                              </small              >                                                      <bridge              >            {{ result.location }}                              </span              >                                                      </div              >                                                      </div              >                                                      </div              >                                                      </template              >                              

If you become back to the homepage in your browser, you lot'll see the cards now have the correct data!

Merely to summarize, the parent component, EventsList, sent the event data down from the for loop two split up times. Each time the EventCard component was called, it received the issue data into props, which allowed you to render the proper noun, appointment, and location.

EventSingle Component

The final part of the application that needs updated data is the EventSingle component. Open up src/views/EventSingle.vue.

Y'all can see the current state of this component in the browser at http: / /localhost: 8080 /issue/ 1 . Everything is withal hard-coded in and waiting for data.

Vue single page placeholder

This component is a great case of how passing data starts to get tricky.

Call back dorsum to how the link to this page was fix in EventsList.vue:

                                                    <router-link              :to                              =                "                '/outcome/'                + result.id"                            >                                                      <EventCard              :issue                              =                "event"                            />                                                      </router-link              >                              

It uses the effect.id from the for loop to create the link for each card/outcome, but how practice you ship the actual information through?

You tin use a method similar to the i you used before to fill in the data from EventCard and transport some data down into a prop on EventSingle. But and so you'd be using the router to manage data, which is a little foreign.

You also have to consider what would happen if instead of clicking on the link, someone went directly to http: / /localhost: 8080 /outcome/ one . How would it receive the props that information technology's expecting if that specific <router-link> code was never fired?

It wouldn't.

And so for this application, your best selection is to simply pull the data once again.

Here's the gist of what needs to happen:

  1. Grab the id from the route parameter (in the URL)
  2. Use that to pull out the correct result from the list of all events
  3. Fill in the template with data from that specific event

🛠️️ To do this, open upward EventSingle.vue and supersede it entirely with:

                                                    <template              >                                                      <div              class                              =                "issue-single"                            >                                                      <section              class                              =                "hero is-primary"                            >                                                      <div              grade                              =                "hero-body"                            >                                                      <div              form                              =                "container"                            >                                                      <h1              class                              =                "title"                            >            {{ event.name }}                              </h1              >                                                      <h2              form                              =                "subtitle                "                            >                                                      <strong              >            Engagement:                              </strong              >                        {{ event.engagement }}                                          <br              />                                                      <potent              >            Time:                              </stiff              >                        {{ event.time }}                                          </h2              >                                                      </div              >                                                      </div              >                                                      </section              >                                                      <department              class                              =                "effect-content"                            >                                                      <div              class                              =                "container"                            >                                                      <p              grade                              =                "is-size-four clarification"                            >            {{ event.description }}                              </p              >                                                      <p              class                              =                "is-size-5"                            >                                                      <strong              >            Location:                              </strong              >                        {{ event.location }}                              </p              >                                                      <p              class                              =                "is-size-5"                            >                                                      <strong              >            Category:                              </strong              >                        {{ consequence.category }}                              </p              >                                                      <div              class                              =                "event-images columns is-multiline has-text-centered"                            >                                                      <div              v-for                              =                "image in issue.images"                            :key                              =                "image.id"                            class                              =                "column is-one-3rd"                            >                                                      <img              :src                              =                "prototype"                            :alt                              =                "upshot.proper noun"                            />                                                      </div              >                                                      </div              >                                                      </div              >                                                      </section              >                                                      </div              >                                                      </template              >                                                      <script              >                                                      export                default                {                name:                'EventSingle'                ,                data                (                )                {                return                {                events:                [                {                id:                one                ,                name:                'Charity Ball'                ,                category:                'Fundraising'                ,                description:                'Spend an elegant night of dinner and dancing with us as we heighten money for our new rescue farm.'                ,                featuredImage:                'https://placekitten.com/500/500'                ,                images:                [                'https://placekitten.com/500/500'                ,                'https://placekitten.com/500/500'                ,                'https://placekitten.com/500/500'                ,                ]                ,                location:                '1234 Fancy Ave'                ,                engagement:                '12-25-2019'                ,                time:                'xi:30'                ,                }                ,                {                id:                2                ,                name:                'Rescue Center Goods Drive'                ,                category:                'Adoptions'                ,                description:                'Come to our donation drive to help united states of america replenish our stock of pet food, toys, bedding, etc. Nosotros will accept live bands, games, nutrient trucks, and much more.'                ,                featuredImage:                'https://placekitten.com/500/500'                ,                images:                [                'https://placekitten.com/500/500'                ]                ,                location:                '1234 Dog Alley'                ,                date:                '11-21-2019'                ,                time:                '12:00'                ,                }                ,                ]                ,                result:                {                }                ,                }                ;                }                ,                created                (                )                {                const                ID                =                Number                (                this                .$route.params.id)                ;                let                event                =                this                .events.                find                (                (                event                )                =>                event.id                ===                ID                )                ;                this                .outcome                =                event;                }                ,                }                ;                                                                    </script              >                              

Scroll down to the <script> section and you lot'll run across that the data you lot've been using has also been added to this component. But this time, there'due south a new chunk of code:

                      created            (            )            {            const            ID            =            Number            (            this            .$route.params.id)            ;            const            consequence            =            this            .events.            find            (            event            =>            event.id            ===            ID            )            ;            this            .event            =            event;            }                  

Starting time, permit's become over what created ( ) is used for.

The Vue instance goes through many steps when it's created. It sets upwards data observation, compiles the template, mounts the Vue example to the DOM, and updates the DOM when changes are detected.

But what if y'all need to jump in at some bespeak between these steps and run some code? Vue offers functions chosen lifecycle hooks that permit you lot do just that.

The created ( ) hook lets yous run some code right after the case is created.

There are quite a few lifecycle hooks available, as you can see in the image beneath.

Vue lifecycle hooks

Image Source: Vue.js Guide - Case

And then right later on the example is created, the created ( ) role runs.

                      created            (            )            {            const            ID            =            Number            (            this            .$route.params.id)            ;            const            event            =            this            .events.            find            (            event            =>            upshot.id            ===            ID            )            ;            this            .event            =            event;            }                  

This part creates a variable called ID that volition hold the id of the effect yous want to render. You lot're able to pull this id from the route parameter id that yous set up earlier in src/router/index.js.

Next, you're creating a variable called event, which will hold the event object. Information technology uses the JavaScript function find ( ) on the events array, which volition loop through the assortment until it finds an event with the id of ID (the route parameter).

And so y'all're setting the event variable to the event of this. And now, you accept the data needed to render this specific result!

Ideally, y'all'd want to pull this data from an API. In a case like this, yous would just hit the endpoint that returns the specific upshot that you desire past sending over the route parameter, but since you don't have an API yet, this method is fine. In the follow up of this tutorial, you'll refactor this code to utilise an actual API.

Another absurd thing you lot're doing is looping over the images in the specific consequence. Let's take a closer look at the block of code that displays the images.

                                                    <div              form                              =                "result-images columns is-multiline has-text-centered"                            >                                                      <div              v-for                              =                "paradigm in event.images"                            :key                              =                "image.id"                            class                              =                "cavalcade is-one-third"                            >                                                      <img              :src                              =                "`${image}`"                            :alt                              =                "`${effect.proper name}`"                            />                                                      </div              >                                                      </div              >                              

This uses Vue's v- for directive again to loop through the images. In one case information technology grabs a single paradigm, it binds the image URL, img, to the :src attribute. Information technology'south expert practice to always set an alt attribute, then you tin bind the value for upshot.name to the alt attribute of these images.

Vue single event page

At present head dorsum to the homepage and click around. All of the components are rendering the correct data, so information technology'due south time to add authentication!

Vue auth app homepage

Adding Hallmark to your Vue App

The final thing left to do is add login functionality to the application. Y'all're going to use Auth0'southward authentication service to do this.

One time you're registered, you'll exist taken to the Auth0 management dashboard.

  • Click on "Applications" in the left sidebar
  • Click on the big ruby-red button that says "Create Application"
  • Name information technology "Vue Events" (or anything y'all'd like)
  • Click on "Single Page Web Applications" for "application type"
  • Click "Create"

Auth0 Dashboard

🛠️️ Next, click into "Settings" to fill in some information that Auth0 needs to configure authentication for your application.

Allowed Callback URLshttp: / /localhost: 8080

This is where Auth0 will redirect the user afterward they have authenticated.

Allowed Logout URLshttp: / /localhost: 8080

This is the URL that users volition render to after they log out of your application.

Allowed Web Originshttp: / /localhost: 8080

This is the URL that Auth0 uses for Cross-origin Hallmark.

Curlicue down and click the "Save Changes" push button.

That's all you lot need from the dashboard for now, but don't click out yet. You'll need to pull some of these values from the dashboard into your application soon.

Install Auth0 SPA package

🛠️️ Head dorsum to the terminal and install Auth0's auth0-spa-js parcel in the awarding'south root directory.

                      npm            install            @auth0/auth0-spa-js        

Create an authentication wrapper

Next, you're going to create a reusable wrapper Vue object around the Auth0 SDK. You'll also create a Vue plugin that exposes this wrapper to the rest of the application.

🛠️️ Create a new file and folder for this. Make sure you're yet in the events-app root directory and enter:

                      mkdir            src/auth            touch on            src/auth/index.js        

🛠️️ Now open up the newly created src/auth/index.js file and paste in the following:

                      import            Vue            from            'vue'            ;            import            createAuth0Client            from            '@auth0/auth0-spa-js'            ;            /** Define a default action to perform after authentication */            const            DEFAULT_REDIRECT_CALLBACK            =            (            )            =>            window.history.            replaceState            (            {            }            ,            document.championship,            window.location.pathname)            ;            let            instance;            /** Returns the electric current example of the SDK */            export            const            getInstance            =            (            )            =>            case;            /** Creates an instance of the Auth0 SDK. If ane has already been created, it returns that instance */            export            const            useAuth0            =            (                          {              onRedirectCallback              =              DEFAULT_REDIRECT_CALLBACK              ,              redirectUri              =              window.location.origin,              ...options              }                        )            =>            {            if            (instance)            return            instance;            // The 'instance' is simply a Vue object            example            =            new            Vue            (            {            information            (            )            {            render            {            loading:            true            ,            isAuthenticated:            false            ,            user:            {            }            ,            auth0Client:            null            ,            popupOpen:            fake            ,            fault:            zippo            ,            }            ;            }            ,            methods:            {            /** Authenticates the user using a popup window */            async            loginWithPopup            (            o            )            {            this            .popupOpen            =            true            ;            attempt            {            wait            this            .auth0Client.            loginWithPopup            (o)            ;            }            take hold of            (e)            {            // eslint-disable-next-line            console.            error            (e)            ;            }            finally            {            this            .popupOpen            =            false            ;            }            this            .user            =            await            this            .auth0Client.            getUser            (            )            ;            this            .isAuthenticated            =            true            ;            }            ,            /** Handles the callback when logging in using a redirect */            async            handleRedirectCallback            (            )            {            this            .loading            =            true            ;            endeavor            {            await            this            .auth0Client.            handleRedirectCallback            (            )            ;            this            .user            =            expect            this            .auth0Client.            getUser            (            )            ;            this            .isAuthenticated            =            true            ;            }            grab            (e)            {            this            .mistake            =            eastward;            }            finally            {            this            .loading            =            simulated            ;            }            }            ,            /** Authenticates the user using the redirect method */            loginWithRedirect            (            o            )            {            return            this            .auth0Client.            loginWithRedirect            (o)            ;            }            ,            /** Returns all the claims nowadays in the ID token */            getIdTokenClaims            (            o            )            {            render            this            .auth0Client.            getIdTokenClaims            (o)            ;            }            ,            /** Returns the access token. If the token is invalid or missing, a new one is retrieved */            getTokenSilently            (            o            )            {            return            this            .auth0Client.            getTokenSilently            (o)            ;            }            ,            /** Gets the admission token using a popup window */            getTokenWithPopup            (            o            )            {            return            this            .auth0Client.            getTokenWithPopup            (o)            ;            }            ,            /** Logs the user out and removes their session on the authorization server */            logout            (            o            )            {            render            this            .auth0Client.            logout            (o)            ;            }            ,            }            ,            /** Use this lifecycle method to instantiate the SDK client */            async            created            (            )            {            // Create a new case of the SDK customer using members of the given options object            this            .auth0Client            =            wait            createAuth0Client            (            {            domain:            options.domain,            client_id:            options.clientId,            audition:            options.audience,            redirect_uri:            redirectUri,            }            )            ;            effort            {            // If the user is returning to the app after authentication..            if            (            window.location.search.            includes            (            'code='            )            &&            window.location.search.            includes            (            'state='            )            )            {            // handle the redirect and retrieve tokens            const            {            appState            }            =            wait            this            .auth0Client.            handleRedirectCallback            (            )            ;            // Notify subscribers that the redirect callback has happened, passing the appState            // (useful for retrieving any pre-hallmark state)            onRedirectCallback            (appState)            ;            }            }            catch            (e)            {            this            .error            =            e;            }            finally            {            // Initialize the internal authentication country            this            .isAuthenticated            =            look            this            .auth0Client.            isAuthenticated            (            )            ;            this            .user            =            await            this            .auth0Client.            getUser            (            )            ;            this            .loading            =            false            ;            }            }            ,            }            )            ;            render            instance;            }            ;            // Create a simple Vue plugin to expose the wrapper object throughout the application            consign            const            Auth0Plugin            =            {            install            (            Vue,              options            )            {            Vue            .prototype.$auth            =            useAuth0            (options)            ;            }            ,            }            ;                  

The comments in this file cover the details of what's happening, but to summarize, you are start creating (or returning) an instance of the Auth0 SDK. The case is just a Vue object.

  • The example contains the following data: loading, isAuthenticated, user, auth0Client, popupOpen, and fault
  • It also includes several methods that will be called later, merely have note of them now: loginWithPopup, handleRedirectCallback, loginWithRedirect, getIdTokenClaims, getTokenSilently, getTokenWithPopup, and logout

During the created ( ) lifecycle claw, you're creating an instance of the SDK.

When a user clicks "Log in", they're redirected to the Auth0 Universal Login folio (more on this later). They volition enter their credentials there and so be redirected back to the application. This is where the "Allowed Callback URLs" from the Auth0 dashboard come into play. handleRedirectCallback ( ) will run, which will go the authenticated user data, retrieve tokens, and update isAuthenticated to true.

This example also contains an options object (pulled out and pasted below). This object will hold the values for the Auth0 clientId, domain, and audience from the Auth0 dashboard.

                      // Create a new example of the SDK client using members of the given options object            this            .auth0Client            =            await            createAuth0Client            (            {            domain:            options.domain,            client_id:            options.clientId,            audience:            options.audience,            redirect_uri:            redirectUri,            }            )            ;                  

These values aren't peculiarly sensitive, only it'southward nevertheless a skilful do to get out them out of your source control (e.g. GitHub). So let's create a file that can be added to .gitignore so that Git volition ignore information technology.

🛠️️ Make sure you're still in the events-app directory and run:

                      touch            auth_config.json        

If you're using Git or some other version control, open up .gitignore or its equivalent. Paste in auth_config.json on whatever line. Now this file volition be ignored the side by side time y'all button to your repo.

🛠️️ Next, open upward auth_config.json and paste in the following:

                      {            "domain"            :            "your-domain.auth0.com"            ,            "clientId"            :            "yourclientid"            }                  

Finding your auth_config values:

  • Head to the Auth0 dashboard
  • Click on "Applications" and select your application
  • Click on "Settings"
  • Copy the value for "Domain" and paste it into domain in auth_config.json
  • Copy the value for "Client ID" and paste it into clientId in auth_config.json

Next, open src/primary.js and install the plugin with Vue.use. This plugin will allow y'all to access the hallmark land globally (from anywhere in the application). Vue.use is a global method used to call plugins, and information technology must be placed before new Vue ( ) .

🛠️️ Supersede all of src/primary.js with the post-obit:

                      import            Vue            from            'vue'            ;            import            App            from            './App.vue'            ;            import            router            from            './router'            ;            import            'bulma/css/bulma.css'            ;            // Import the Auth0 configuration            import            {            domain,            clientId            }            from            '../auth_config.json'            ;            // Import the plugin here            import            {            Auth0Plugin            }            from            './auth'            ;            // Install the authentication plugin hither            Vue.            use            (Auth0Plugin,            {            domain,            clientId,            onRedirectCallback            :            (            appState            )            =>            {            router.            push            (            appState            &&            appState.targetUrl            ?            appState.targetUrl            :            window.location.pathname,            )            ;            }            ,            }            )            ;            Vue.config.productionTip            =            false            ;            new            Vue            (            {            router,            return            :            (            h            )            =>            h            (App)            ,            }            )            .            $mountain            (            '#app'            )            ;                  

Here you're just importing the Auth0 configuration file that you created to get admission to the domain and clientId values. Next, you're importing the Auth0Plugin that was created earlier.

Finally, you install and configure the plugin.

Wiring up login and logout buttons

Now that you take the Auth0 hallmark plugin configured, it'due south time to fix up the "Sign in" button so that it actually does something.

🛠️️ Open up src/components/partials/Nav.vue. Find the cake of lawmaking that starts with <div course = "navbar-end" > and replace it with this:

                                                    <div              class                              =                "navbar-terminate"                            >                                                      <div              class                              =                "navbar-item"                            >                                                      <div              class                              =                "buttons"                            >                        <!-- Bank check that the SDK client is not currently loading before accessing is methods -->                                          <div              v-if                              =                "!$auth.loading"                            >                        <!-- show login when not authenticated -->                                          <a              5-if                              =                "!$auth.isAuthenticated"                            @click                              =                "login"                            class                              =                "button is-dark"                            >                                                      <stiff              >            Sign in                              </strong              >                                                      </a              >                        <!-- show logout when authenticated -->                                          <a              v-if                              =                "$auth.isAuthenticated"                            @click                              =                "logout"                            grade                              =                "push button is-dark"                            >                                                      <stiff              >            Log out                              </strong              >                                                      </a              >                                                      </div              >                                                      </div              >                                                      </div              >                                                      </div              >                              

The buttons are now wrapped in !$auth.loading to make certain that the SDK client has finished loading before you to try admission the user'due south state. Next, you lot're using @click, which will handle the click event past calling the login or logout methods when a user clicks on the respective button.

🛠️️ Allow'due south create those methods now. In that same file, curl down to the <script> tag and replace it with this:

                      <script>            export            default            {            name:            'Nav'            ,            methods:            {            // Log the user in            login            (            )            {            this            .$auth.            loginWithRedirect            (            )            ;            }            ,            // Log the user out            logout            (            )            {            this            .$auth.            logout            (            {            returnTo:            window.location.origin            }            )            ;            }            }            }            <            /script>                  

Now head dorsum to the application and click "Sign In" and you should exist redirected to the Auth0 Universal Login page.

If you run into an issue, double-bank check that your values in auth_config.json are right. If you're still having issues, leave a comment beneath, and I'll assistance you work through information technology.

Once you've striking the Auth0 Universal Login folio, sign up with a mock user account. You'll then encounter a screen telling you that the application is requesting access to your profile and email.

Auth0 Vue login permissions

Click the checkmark, and you'll be redirected back to the "Immune Callback URL" you specified in the dashboard, which is your application homepage. At present instead of the "Sign in" button, yous should encounter a "Log out" button.

Optional: Auth0 offers social login options directly from the dashboard! Google is activated past default, and you can turn more on individually in the Auth0 management dashboard.

Click on "Connections" > "Social" in the sidebar. Be sure to employ your ain dev keys if you'd like to integrate social sign-on. The default Auth0 dev keys are fine for testing, but may cause unexpected errors (such every bit being signed out on refresh), and then we still recommend using your ain.

Accessing user information

Auth0 lets you access the information of the logged-in user in your templates with the following:

                      {            {            $auth.user;            }            }                  

The contents of $auth.user look something similar this:

                      {            "nickname"            :            "hollylloyd"            ,            "name"            :            "Holly Lloyd"            ,            "picture"            :            "https://gravatar.com/somefancyimage.png"            ,            "updated_at"            :            "2019-ten-09T15:49:28.181Z"            ,            "email"            :            "holly-lloyd@example.com"            ,            "email_verified"            :            faux            ,            "sub"            :            "auth0|xxxxxxxxxxxxxxx"            }                  

So if you want to add a profile page in the future, you lot accept access to this information (and more) to display.

At present that you know how to add together a login push, allow's wire up that 2d button on the homepage.

🛠️️ Open up src/views/Home.vue and supersede everything betwixt <div class = "push-cake" > < /div> with the post-obit:

                      <div            class            =            "push-cake"            >            <push v-            if            =            "!$auth.isAuthenticated"            @click=            "login"            class            =            "button is-xl is-dark"            >Sign Up to Scan Events<            /push button>            <h3 v-            if            =            "$auth.isAuthenticated"            class            =            "is-size-3 has-background-nighttime welcome"            >Welcome,            {            {            $auth.user.proper name            }            }            !            <            /h3>            <            /div>                  

🛠️️ At present you just need to add together the methods section with the login ( ) function. Coil downwards to where the <script> tag is and replace export default { } with this:

                      export            default            {            name:            'domicile'            ,            components:            {            EventsList,            }            ,            methods:            {            // Log the user in            login            (            )            {            this            .$auth.            loginWithRedirect            (            )            ;            }            ,            }            ,            }            ;                  

At present a user tin sign in with this button every bit well, and once they're signed in, it will be replaced with a welcome bulletin with their name using $auth.user.name.

Vue homepage logged in message

Set up a Road Baby-sit

So now, the final thing yous demand to do is redirect unauthenticated users away from the Unmarried Event page. This means they should be able to view the homepage with the list of cards/events, but equally shortly as they click through to the result detail page, they should exist kicked to a login page.

⚡️ IMPORTANT ⚡️ This only prevents the page from loading; it doesn't foreclose the data from loading. Yous should NEVER rely on the frontend to protect your data.

Since you oasis't created a backend API yet, you lot're only storing all of the information in the component on the frontend. Fifty-fifty though you're going to ready a road guard to redirect an unauthenticated user abroad from a unmarried outcome folio, they can still read through the JavaScript files and notice it! I'll show you how in a moment.

{% if page.url contains '/amp/' %} In part 2, you'll build out the API and then that this information is stored and protected on the backend instead and only pulled into the frontend if the user is authenticated. {% else %} In function two, you'll build out the API then that this data is stored and protected on the backend instead and only pulled into the frontend if the user is authenticated. {% endif %}

Permit'due south set up the route guard at present so that when the data is coming from the backend, the UI for kicking the user to the login folio is already in place.

🛠️️ Create a file called authGuard.js in the src/auth directory.

{% prism fustigate %} affect src/auth/authGuard.js {% endprism %}

🛠️️ Open that up in your editor and paste in the following:

                      import            {            getInstance            }            from            './index'            ;            export            const            authGuard            =            (            to,              from              ,              next            )            =>            {            const            authService            =            getInstance            (            )            ;            const            fn            =            (            )            =>            {            // If the user is authenticated, proceed with the route            if            (authService.isAuthenticated)            {            return            side by side            (            )            ;            }            // Otherwise, log in            authService.            loginWithRedirect            (            {            appState:            {            targetUrl:            to.fullPath            }            }            )            ;            }            ;            // If loading has already finished, cheque the auth state using `fn()`            if            (            !authService.loading)            {            return            fn            (            )            ;            }            // Scout for the loading property to alter earlier checking isAuthenticated            authService.            $watch            (            'loading'            ,            (            loading            )            =>            {            if            (loading            ===            false            )            {            return            fn            (            )            ;            }            }            )            ;            }            ;                  

This uses the getInstance method from the src/auth/index.js file, which volition implement the role that prevents a route from being accessed if a user is not logged in.

If the user is authenticated, next ( ) is returned, which allows the user to continue to the clicked route. If the user isn't authenticated, they're redirected to the Auth0 Universal Login page.

Side by side, you need to add this auth guard to the router so that this runs before any view is returned.

You'll only bank check if the user is authenticated. If they are, permit them through, if not, send them to the login page.

🛠️️ Open up the router file in src/router/alphabetize.js and add supervene upon it with:

                      import            Vue            from            'vue'            ;            import            Router            from            'vue-router'            ;            import            Dwelling house            from            '../views/Home.vue'            ;            import            {            authGuard            }            from            '../auth/authGuard'            ;            Vue.            use            (Router)            ;            consign            default            new            Router            (            {            manner:            'history'            ,            base:            procedure.env.            BASE_URL            ,            routes:            [            {            path:            '/'            ,            proper noun:            'home'            ,            component:            Habitation,            }            ,            {            path:            '/nearly'            ,            name:            'nearly'            ,            component            :            (            )            =>            import            (            '../views/About.vue'            )            ,            }            ,            {            path:            '/event/:id'            ,            proper name:            'eventSingle'            ,            component            :            (            )            =>            import            (            '../views/EventSingle.vue'            )            ,            beforeEnter:            authGuard,            }            ,            ]            ,            }            )            ;                  

The authGuard is imported at the superlative. Since you only want to crave authentication for the event details road, beforeEnter: authGuard has been added to that route.

At present, if yous've already logged in previously, you lot can click on one of those issue cards and you lot should still be able to come across the event single folio.

But if you open an incognito window and try to admission that same road, y'all'll be kicked to the login page, which is exactly what's expected!

Vue Auth0 login page

Equally discussed before, this doesn't prevent a user from finding the data for that folio.

If y'all're curious how this is possible, sign out or open up up an incognito window, and so become to http: / /localhost: 8080 /js/app.js in your browser. Now search for 1 of the issue descriptions similar "Spend an elegant night of dinner and dancing with u.s.a. equally we raise coin for our new rescue farm.", and sure plenty, there is the data!

So at this indicate, you may be asking, "Holly, then what is the signal of the redirect?"

I'm glad you asked! This is mostly meant for user feel! Imagine that the information is protected on the backend. You're going to want to betoken that a user should be signed in to view that specific page. If you simply but don't render any data on that page, the user volition be faced with an empty page and think something is broken. In this scenario, redirecting them to the sign-in folio will easily signal that they need to log in to view it!

In part 2 of this tutorial, you'll learn how to create an API in Limited and pull the data into your Vue application from there. It uses the same Vue application that you merely built, so if you're interested, you tin striking the footing running!

Wrap Up

If this was your starting time time working with Vue.js, hopefully this helped you understand how everything comes together in a small application. But to recap, here are some of the topics that were covered in this tutorial:

  • Using the Vue CLI
  • Creating reusable components
  • Setting upwards routing with Vue Router
  • Styling with Bulma
  • Using props and data
  • Calculation hallmark to a Vue.js app
  • Protecting routes with Vue navigation guards

Check out the side by side tutorial on this topic where y'all can take this existing Vue application, connect it to an Express API, and learn how to secure those API endpoints to protect the data. Cheers for reading!

abbottpiculd.blogspot.com

Source: https://auth0.com/blog/beginner-vuejs-tutorial-with-user-login/

0 Response to "Pearon Vue Please Ensure That Authentication Web Service Is Up Then Try Login Again"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel