mirror of
https://github.com/LukeHagar/website.git
synced 2025-12-10 21:07:46 +00:00
Add nuxt tutorial
This commit is contained in:
10
src/routes/docs/tutorials/nuxt/+layout.svelte
Normal file
10
src/routes/docs/tutorials/nuxt/+layout.svelte
Normal file
@@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { globToTutorial } from '$lib/utils/tutorials.js';
|
||||
import { setContext } from 'svelte';
|
||||
|
||||
export let data;
|
||||
const tutorials = globToTutorial(data);
|
||||
setContext('tutorials', tutorials);
|
||||
</script>
|
||||
|
||||
<slot />
|
||||
11
src/routes/docs/tutorials/nuxt/+layout.ts
Normal file
11
src/routes/docs/tutorials/nuxt/+layout.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { LayoutLoad } from './$types';
|
||||
|
||||
export const load: LayoutLoad = ({ url }) => {
|
||||
const tutorials = import.meta.glob('./**/*.markdoc', {
|
||||
eager: true
|
||||
});
|
||||
return {
|
||||
tutorials,
|
||||
pathname: url.pathname
|
||||
};
|
||||
};
|
||||
6
src/routes/docs/tutorials/nuxt/+page.ts
Normal file
6
src/routes/docs/tutorials/nuxt/+page.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async () => {
|
||||
throw redirect(303, '/docs/tutorials/nuxt/step-1');
|
||||
};
|
||||
35
src/routes/docs/tutorials/nuxt/step-1/+page.markdoc
Normal file
35
src/routes/docs/tutorials/nuxt/step-1/+page.markdoc
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Build an ideas tracker with Nuxt
|
||||
description: Learn to build an idea tracker app with Appwrite and Nuxt with authentication, databases and collections, queries, pagination, and file storage.
|
||||
step: 1
|
||||
difficulty: beginner
|
||||
readtime: 12
|
||||
back: /docs
|
||||
---
|
||||
|
||||
**Idea tracker**: an app to track all the side project ideas that you'll start, but probably never finish.
|
||||
In this tutorial, you will build Idea tracker with Appwrite and Nuxt.
|
||||
|
||||
{% only_dark %}
|
||||

|
||||
{% /only_dark %}
|
||||
{% only_light %}
|
||||

|
||||
{% /only_light %}
|
||||
|
||||
# Concepts {% #concepts %}
|
||||
|
||||
This tutorial will introduce the following concepts:
|
||||
|
||||
1. Setting up your first project
|
||||
2. Authentication
|
||||
3. Navigation
|
||||
4. Databases and collections
|
||||
5. Queries
|
||||
|
||||
|
||||
# Prerequisites {% #prerequisites %}
|
||||
|
||||
1. Basic knowledge of JavaScript.
|
||||
2. Have [Node.js](https://nodejs.org/en) and [NPM](https://www.npmjs.com/) installed on your computer.
|
||||
98
src/routes/docs/tutorials/nuxt/step-2/+page.markdoc
Normal file
98
src/routes/docs/tutorials/nuxt/step-2/+page.markdoc
Normal file
@@ -0,0 +1,98 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Create app
|
||||
description: Create a Nuxt app project and integrate with Appwrite
|
||||
step: 2
|
||||
---
|
||||
|
||||
# Create Nuxt project {% #create-nuxt-project %}
|
||||
|
||||
Create a Nuxt app with the `npx init` command.
|
||||
|
||||
```sh
|
||||
npx nuxi@latest init ideas-tracker
|
||||
```
|
||||
|
||||
# Add dependencies {% #add-dependencies %}
|
||||
|
||||
Install the JavaScript Appwrite SDK.
|
||||
|
||||
```sh
|
||||
npm install appwrite
|
||||
```
|
||||
|
||||
You can start the development server to watch your app update in the browser as you make changes.
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
# Set up files {% #set up-files %}
|
||||
|
||||
In Nuxt, directories help organize the codebase and minimize boilerplate.
|
||||
The purpose is making it easy to find and manage different aspect of your application.
|
||||
|
||||
The files added to the `pages` directory will automatically become a route.
|
||||
Nuxt will look for a layout in the `layouts` directory to render a page layout.
|
||||
Without a layout file in this directory, the routing won't work
|
||||
When these files have been created the `app.vue` file needs to edited to render these pages instead of the standard welcome page.
|
||||
Then, we will have a working app where we can verify our changes in the browser throughout the tutorial.
|
||||
As an additional step, we will be adding [Appwrite's Pink Design system]('https://pink.appwrite.io/') to have a global design system to help with the layout.
|
||||
|
||||
First, add a page by creating the file `src/pages/index.vue` and add the following code.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<h1>Hello, idea tracker!</h1>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
Create the file `src/layouts/default.vue` to add the default layout.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
layout: "default",
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
Go to `app.vue`, remove `NuxtWelcome`and insert `<NuxtPage />` wrapped inside `<NuxtLayout>`.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
To import Appwrite's design system to all pages and components, edit the file `nuxt.config.ts`.
|
||||
The layouts is then ready to be used in the templates with auto-import.
|
||||
|
||||
```ts
|
||||
export default defineNuxtConfig({
|
||||
app: {
|
||||
head: {
|
||||
link: [
|
||||
{ rel: "stylesheet", href: "https://unpkg.com/@appwrite.io/pink" },
|
||||
{
|
||||
rel: "stylesheet",
|
||||
href: "https://unpkg.com/@appwrite.io/pink-icons",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
devtools: { enabled: true },
|
||||
});
|
||||
```
|
||||
59
src/routes/docs/tutorials/nuxt/step-3/+page.markdoc
Normal file
59
src/routes/docs/tutorials/nuxt/step-3/+page.markdoc
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Set up Appwrite
|
||||
description: Import and configure a project with Appwrite Cloud.
|
||||
step: 3
|
||||
---
|
||||
|
||||
# Create project {% #create-project %}
|
||||
|
||||
Head to the [Appwrite Console](https://cloud.appwrite.io/console).
|
||||
|
||||
{% only_dark %}
|
||||

|
||||
{% /only_dark %}
|
||||
{% only_light %}
|
||||

|
||||
{% /only_light %}
|
||||
|
||||
If this is your first time using Appwrite, create an account and create your first project.
|
||||
|
||||
Then, under **Add a platform**, add a **Web app**.
|
||||
The **Hostname** should be localhost.
|
||||
|
||||
{% only_dark %}
|
||||

|
||||
{% /only_dark %}
|
||||
{% only_light %}
|
||||

|
||||
{% /only_light %}
|
||||
|
||||
You can skip the optional steps.
|
||||
|
||||
# Initialize Appwrite SDK {% #init-sdk %}
|
||||
|
||||
To use Appwrite in our app, we'll need to find our project ID.
|
||||
It is located in the **Settings** page.
|
||||
|
||||
{% only_dark %}
|
||||

|
||||
{% /only_dark %}
|
||||
{% only_light %}
|
||||

|
||||
{% /only_light %}
|
||||
|
||||
Create a new file `src/appwrite.js` for the Appwrite related code.
|
||||
Only one instance of the `Client()` class should be created per app.
|
||||
Add the following code to it, replacing `<YOUR_PROJECT_ID>` with your project ID.
|
||||
|
||||
```js
|
||||
import { Client, Databases, Account } from "appwrite";
|
||||
|
||||
const client = new Client();
|
||||
client
|
||||
.setEndpoint("https://cloud.appwrite.io/v1")
|
||||
.setProject("<YOUR_PROJECT_ID>"); // Replace with your project ID
|
||||
|
||||
export const account = new Account(client);
|
||||
export const database = new Databases(client);
|
||||
```
|
||||
196
src/routes/docs/tutorials/nuxt/step-4/+page.markdoc
Normal file
196
src/routes/docs/tutorials/nuxt/step-4/+page.markdoc
Normal file
@@ -0,0 +1,196 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Add authentication
|
||||
description: Add authentication to your Nuxt application using Appwrite Web SDK
|
||||
step: 4
|
||||
---
|
||||
|
||||
# Composables {% #composables %}
|
||||
|
||||
Composables is a design pattern in Vue and Nuxt to manage logic related to data fetching and global state management.
|
||||
You can think of a composable as a way to put pieces of code that do specific tasks into neat little boxes that you can use over and over again in you app.
|
||||
|
||||
In the user session composable will put all the logic related to user authentication, such as login, logout and authentication status.
|
||||
It will be available for access in all pages and components.
|
||||
|
||||
Create your composable for the user session logic by adding the file `src/composables/useUserSession.js`.
|
||||
Add the following code.
|
||||
|
||||
```js
|
||||
import { ID } from 'appwrite'
|
||||
import { ref } from 'vue'
|
||||
import { account } from '/appwrite'
|
||||
|
||||
const current = ref(null) //Reference to the current user object
|
||||
const isLoggedIn = ref(null) //Reference to check user status
|
||||
|
||||
export const useUserSession = () => {
|
||||
const register = async (email, password) => {
|
||||
await account.create(ID.unique(), email, password) //Interaction with the Appwrite backend
|
||||
await this.login(email, password) //Login the newly created user
|
||||
}
|
||||
|
||||
const login = async (email, password) => {
|
||||
const authUser = await account.createEmailSession(email, password) //Interaction with the Appwrite backend
|
||||
current.value = authUser //Passing user data to the current user reference
|
||||
isLoggedIn.value = true //Setting user status to true
|
||||
}
|
||||
|
||||
const logout = async () => {
|
||||
await account.deleteSession('current') //Interaction with the Appwrite backend
|
||||
current.value = null //Clearing the current user data object
|
||||
isLoggedIn.value = false //Setting user status to false
|
||||
}
|
||||
return {
|
||||
current,
|
||||
isLoggedIn,
|
||||
login,
|
||||
logout,
|
||||
register,
|
||||
}
|
||||
```
|
||||
|
||||
The `useUserSession()` function is neatly packed and ready to be reused in our pages and components.
|
||||
|
||||
# Login page {% #login-page %}
|
||||
|
||||
With the authentication functions handled, we can go on to build a simple login page.
|
||||
The user interactions will be handled in a form with two input fields for email and password.
|
||||
Underneath will be a button to login and a button to register a new user.
|
||||
|
||||
The values from the inputs will be bound to a `v-model`.
|
||||
The `v-model`acts like a bridge between your input fields and your Javascript code.
|
||||
If you change the information with yout code, the input field also changes.
|
||||
Similarily, if you type something in the input field, the information in your code also updates.
|
||||
It is a two-way-cnnection that are magically links them together.
|
||||
|
||||
Create the form by adding a new file `src/pages/index.vue` and put in the following code.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="card u-margin-32">
|
||||
<h2 class="eyebrow-heading-2">Login/Register</h2>
|
||||
<form
|
||||
class="form u-width-full-line u-max-width-500 u-margin-block-start-16"
|
||||
@submit.prevent="handleLogin || handleRegistration"
|
||||
>
|
||||
<ul class="form-list">
|
||||
<li class="form-item">
|
||||
<label class="label">Email</label>
|
||||
<div class="input-text-wrapper">
|
||||
|
||||
<!-- Input with v-model to link email field to script -->
|
||||
<input
|
||||
v-model="userData.email"
|
||||
type="email"
|
||||
class="input-text"
|
||||
placeholder="Email"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li class="form-item">
|
||||
<label class="label">Password</label>
|
||||
<div class="input-text-wrapper">
|
||||
|
||||
<!-- Input with v-model to link password field to script -->
|
||||
<input
|
||||
v-model="userData.password"
|
||||
type="password"
|
||||
class="input-text"
|
||||
placeholder="Password"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="buttons-list u-margin-block-start-16">
|
||||
<li class="buttons-list-item">
|
||||
|
||||
<!-- Login button -->
|
||||
<button
|
||||
class="button is-small u-margin-inline-start-4"
|
||||
aria-label="Login"
|
||||
@click="handleLogin"
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
</li>
|
||||
<li class="buttons-list-item">
|
||||
|
||||
<!-- Register button -->
|
||||
<button
|
||||
class="button is-small is-secondary u-margin-inline-start-4"
|
||||
aria-label="Register account"
|
||||
@click="handleRegistration"
|
||||
>
|
||||
Register
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
setup() {
|
||||
//Accessing the user session composable
|
||||
const user = useUserSession();
|
||||
|
||||
//V-model object
|
||||
const userData = {
|
||||
email: "",
|
||||
password: "",
|
||||
};
|
||||
|
||||
//Event handler for logging in
|
||||
const handleLogin = async () => {
|
||||
await user.login(userData.email, userData.password);
|
||||
};
|
||||
|
||||
//Event handler for registrering
|
||||
const handleRegistration = async () => {
|
||||
await user.register(userData.email, userData.password);
|
||||
};
|
||||
|
||||
return {
|
||||
handleLogin,
|
||||
handleRegistration,
|
||||
userData,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
# Home page {% #home-page %}
|
||||
|
||||
Now that users can register and login to the app, let's modify the home page in `src/pages/index.vue` to give the logged in users a secret message.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<!-- Visible if user is logged in -->
|
||||
<section v-if="user.isLoggedIn.value === true">
|
||||
<h2>Welcome!</h2>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Hello, idea tracker!</h2>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
setup() {
|
||||
//Accessing the user session composable
|
||||
const user = useUserSession();
|
||||
|
||||
return {
|
||||
user,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
||||
58
src/routes/docs/tutorials/nuxt/step-5/+page.markdoc
Normal file
58
src/routes/docs/tutorials/nuxt/step-5/+page.markdoc
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Add navigation
|
||||
description: Add navigation to your app.
|
||||
step: 5
|
||||
---
|
||||
|
||||
To help our users navigate the app we want it to have a navigation bar that's visible on all pages.
|
||||
We will use the `useUserSession` composable to show a login button when no user is logged in and a logout button when logged in.
|
||||
Open the `app.vue` file and add the navbar in the `template`, just above the `<NuxtPage />` tag.
|
||||
|
||||
```vue
|
||||
...
|
||||
<NuxtLayout>
|
||||
|
||||
<!--- Navbar -->
|
||||
<nav class="main-header u-padding-inline-end-0">
|
||||
<h3 class="u-stretch eyebrow-heading-1">Idea Tracker</h3>
|
||||
|
||||
<!-- Email and logout button if logged in user -->
|
||||
<div
|
||||
class="main-header-end u-margin-inline-end-16"
|
||||
v-if="user.isLoggedIn.value === true"
|
||||
>
|
||||
<p>
|
||||
{{ user.current.value.providerUid }}
|
||||
</p>
|
||||
<button class="button" type="button" @click="user.logout()">
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Login button if no user logged in -->
|
||||
<NuxtLink v-else href="/login" class="button u-margin-inline-end-16">
|
||||
Login
|
||||
</NuxtLink>
|
||||
</nav>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
...
|
||||
```
|
||||
|
||||
Add a new script below the template to get information about the user session from the `useUserSession`composable.
|
||||
|
||||
```vue
|
||||
<script>
|
||||
export default {
|
||||
setup() {
|
||||
const user = useUserSession(); //Access the user session composable
|
||||
return {
|
||||
user,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
You might wonder about the v-
|
||||
79
src/routes/docs/tutorials/nuxt/step-6/+page.markdoc
Normal file
79
src/routes/docs/tutorials/nuxt/step-6/+page.markdoc
Normal file
@@ -0,0 +1,79 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Add database
|
||||
description: Add databases and queries for ideas in your Nuxt project.
|
||||
step: 6
|
||||
---
|
||||
|
||||
In Appwrite, data is stored as a collection of documents. Create a collection in the [Appwrite Console](https://cloud.appwrite.io/) to store our ideas.
|
||||
|
||||
{% only_dark %}
|
||||

|
||||
{% /only_dark %}
|
||||
{% only_light %}
|
||||

|
||||
{% /only_light %}
|
||||
|
||||
Create a new collection with the following attributes:
|
||||
| Field | Type | Required |
|
||||
|-------------|--------|----------|
|
||||
| userId | String | Yes |
|
||||
| title | String | Yes |
|
||||
| description | String | No |
|
||||
|
||||
Change the collection's permissions in the settings to give access.
|
||||
First, add an "Any" role and check the `READ` box.
|
||||
Next, add a "Users" role and give them access to create, update and delete.
|
||||
|
||||
# Query methods {% #query-methods %}
|
||||
Now that you have a collection to hold ideas, we can use it to get, add and remove data from our app.
|
||||
We will create a new composable to manage the data fetching and their global state.
|
||||
Create a new file in the composables directory, `src/composables/useIdeas.js` and add the following code.
|
||||
|
||||
```js
|
||||
import { ID, Query } from "appwrite";
|
||||
import { database } from "~/appwrite";
|
||||
import { ref } from "vue";
|
||||
|
||||
const IDEAS_DATABASE_ID = "YOUR_DATABASE_ID";
|
||||
const IDEAS_COLLECTION_ID = "YOUR_COLLECTION_ID";
|
||||
|
||||
const current = ref(null); //Reference for the fetched data
|
||||
|
||||
export const useIdeas = () => {
|
||||
|
||||
/* Fetch the 10 most recent ideas from the database
|
||||
and passes the list to the current reference object */
|
||||
const init = async () => {
|
||||
const response = await database.listDocuments(
|
||||
IDEAS_DATABASE_ID,
|
||||
IDEAS_COLLECTION_ID,
|
||||
[Query.orderDesc("$createdAt"), Query.limit(10)]
|
||||
);
|
||||
current.value = response.documents;
|
||||
};
|
||||
|
||||
/* Add new idea to the database, add it to the current
|
||||
object and remove the last to still have 10 ideas */
|
||||
const add = async (idea) => {
|
||||
const response = await database.createDocument(
|
||||
IDEAS_DATABASE_ID,
|
||||
IDEAS_COLLECTION_ID,
|
||||
ID.unique(),
|
||||
idea
|
||||
);
|
||||
current.value = [response, ...current.value].slice(0, 10);
|
||||
|
||||
const remove = async (id) => {
|
||||
await database.deleteDocument(IDEAS_DATABASE_ID, IDEAS_COLLECTION_ID, id);
|
||||
await init(); //Refetch ideas to ensure we have 10 items
|
||||
};
|
||||
|
||||
return {
|
||||
add,
|
||||
current,
|
||||
init,
|
||||
remove,
|
||||
};
|
||||
};
|
||||
```
|
||||
203
src/routes/docs/tutorials/nuxt/step-7/+page.markdoc
Normal file
203
src/routes/docs/tutorials/nuxt/step-7/+page.markdoc
Normal file
@@ -0,0 +1,203 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Create ideas page
|
||||
description: Add database queries using Appwrite in your Nuxt app.
|
||||
step: 7
|
||||
---
|
||||
|
||||
# Create ideas page {% #create-ideas-page %}
|
||||
|
||||
Using the `useIdeas` composable, we can now get some ideas on the page to read and interact with.
|
||||
We don't have any to show just yet, so we are going to start with adding a form to create new ones.
|
||||
The form will only be visible for logged in users.
|
||||
|
||||
We begin with adding the `v-model` object in the `<script>` tag on `src/pages/index.vue`.
|
||||
Unlike the `v-model`on the login form, the properties for the input fields will have `ref` to make them even more reactive to changes.
|
||||
The difference is that we want to instantly clear the inputs when the user press the submit button.
|
||||
On the login page, we redirected the user to the home page, so this was not necessary.
|
||||
|
||||
Defining our object, we also need to think more closely about what the database is expecting to get.
|
||||
Not only does it want the title and the description, first it expects a userId.
|
||||
We can't expect our user to enter that, so we will add the one we have in the user session composable.
|
||||
This value does not require a `ref` since it is not a part of any other interactions.
|
||||
|
||||
```vue
|
||||
<script>
|
||||
export default {
|
||||
setup() {
|
||||
const ideas = useIdeas(); // New variable for the ideas composable
|
||||
const user = useUserSession();
|
||||
|
||||
// V-model object
|
||||
const addIdeaData = {
|
||||
title: ref(""),
|
||||
description: ref(""),
|
||||
};
|
||||
|
||||
// Event handler to submit form
|
||||
const handleAddIdea = async () => {
|
||||
// Extract the values from the refs and add userId
|
||||
const postIdeaData = {
|
||||
userId: user.current.value.userId,
|
||||
title: addIdeaData.title.value,
|
||||
description: addIdeaData.title.value
|
||||
}
|
||||
await ideas.add(postIdeaData);
|
||||
addIdeaData.title.value = ""; // Clears the title field
|
||||
addIdeaData.description.value = ""; // Clears the desciption field
|
||||
};
|
||||
|
||||
return {
|
||||
addIdeaData,
|
||||
handleAddIdea,
|
||||
user,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
Let's remove the header with the secret message we added in step 4 and replace it with a form.
|
||||
Since the properties for the `v-model` variables are references, we use the `.value` property to get the correct information to show.
|
||||
|
||||
```vue
|
||||
...
|
||||
<!-- Section to display for logged in users -->
|
||||
<section v-if="user.isLoggedIn.value === true" class="u-margin-32">
|
||||
<article class="box">
|
||||
<h4 class="heading-level-4">Submit Idea</h4>
|
||||
|
||||
<!-- Form -->
|
||||
<form @submit.prevent="handleAddIdea" class="u-margin-block-start-16">
|
||||
<ul class="form-list">
|
||||
<li class="form-item">
|
||||
|
||||
<!-- Input for title -->
|
||||
<label class="label">Title</label>
|
||||
<input
|
||||
v-model="addIdeaData.title.value"
|
||||
type="text"
|
||||
placeholder="Title"
|
||||
/>
|
||||
</li>
|
||||
<li class="form-item">
|
||||
|
||||
<!-- Input for description -->
|
||||
<label class="label">Description</label>
|
||||
<textarea
|
||||
v-model="addIdeaData.description.value"
|
||||
placeholder="Description"
|
||||
/>
|
||||
</li>
|
||||
<button class="button" aria-label="Submit idea" type="submit">
|
||||
Submit
|
||||
</button>
|
||||
</ul>
|
||||
</form>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- Section to display if the user is not logged in -->
|
||||
<section v-else><p>Please login to submit an idea.</p></section>
|
||||
...
|
||||
```
|
||||
|
||||
The next step is to get the newly added ideas from the database and show them below the previous sections.
|
||||
This section is visible to all users.
|
||||
Since we need the data before any interaction has taken place on the page, like pressing a button, we have to use a function to catch the moment when the page loads.
|
||||
The built-in `onMounted` function does just that.
|
||||
Inside it we can call the `init()` function from the ideas composable to get the data.
|
||||
Add the following to the `setup()`in the `<script>`, just underneath the `useIdeas` variable.
|
||||
|
||||
```vue
|
||||
setup() {
|
||||
<!-- After composable variables -->
|
||||
onMounted(() => {
|
||||
ideas.init();
|
||||
});
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Then continue with adding a new section in the template that displays the list of ideas.
|
||||
|
||||
```vue
|
||||
<!-- Form section -->
|
||||
...
|
||||
<!-- New ideas section -->
|
||||
<section class="u-margin-32">
|
||||
<article class="card">
|
||||
<h4 class="heading-level-4">Latest Ideas</h4>
|
||||
<ul>
|
||||
<li v-for="idea in ideas.current.value">
|
||||
<div class="box">
|
||||
<h5 class="heading-level-6">{{ idea.title }}</h5>
|
||||
<p class="body-text-2">{{ idea.description }}</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
...
|
||||
```
|
||||
Great, we can enter new ideas and have look at them!
|
||||
It probably won't be long before a user will want to delete an idea.
|
||||
We help the by adding a delete button in the top, right corner of their ideas.
|
||||
First, we'll add a function to handle the event in the `setup()`.
|
||||
|
||||
```vue
|
||||
setup() {
|
||||
...
|
||||
// New eventhandler
|
||||
const handleRemoveIdea = async (id) => {
|
||||
await ideas.remove(id);
|
||||
};
|
||||
|
||||
return {
|
||||
...
|
||||
handleRemoveIdea, // Return the added event handler
|
||||
...
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
The button is added to the `div` that renders the details of an idea.
|
||||
|
||||
```vue
|
||||
...
|
||||
<div class="box">
|
||||
<h5 class="heading-level-6">{{ idea.title }}</h5>
|
||||
<p class="body-text-2">{{ idea.description }}</p>
|
||||
|
||||
<!-- New div with icon button -->
|
||||
<div
|
||||
class="u-position-absolute u-inset-inline-end-8 u-inset-block-start-8"
|
||||
>
|
||||
<button
|
||||
class="button is-small is-text is-only-icon"
|
||||
aria-label="Remove item"
|
||||
<!-- Ensuring button is only displayed if a user is logged in and the id's are a match -->
|
||||
v-if="
|
||||
user.current.value &&
|
||||
idea.userId === user.current.value.userId
|
||||
"
|
||||
type="button"
|
||||
@click="handleRemoveIdea(idea.$id)"
|
||||
>
|
||||
<span class="icon-document-remove" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
...
|
||||
```
|
||||
|
||||
Just one last thing before it's all wrapped up -- the layout needs a finishing touch.
|
||||
At the bottom of the file, under the closing `</script>` tag, add a style with a class to change the background color of the list of cards with a color variable.
|
||||
|
||||
```vue
|
||||
<style>
|
||||
article.box {
|
||||
background-color: hsl(var(--color-neutral-0));
|
||||
}
|
||||
</style>
|
||||
```
|
||||
10
src/routes/docs/tutorials/nuxt/step-8/+page.markdoc
Normal file
10
src/routes/docs/tutorials/nuxt/step-8/+page.markdoc
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Next steps
|
||||
description: View your Nuxt app built on Appwrite Cloud.
|
||||
step: 8
|
||||
---
|
||||
|
||||
# Test your project {% #test-project %}
|
||||
Run your project with `npm run dev -- --open --port 3000` and open [http://localhost:3000](http://localhost:3000) in your browser.
|
||||
Head to the [Appwrite Console](https://cloud.appwrite.io/console) to see the new users and follow their interactions.
|
||||
Reference in New Issue
Block a user