-
Notifications
You must be signed in to change notification settings - Fork 228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(docs): add pagination tutorial and related files #196
Changes from 2 commits
caf422e
82d6d14
648acd1
1eefb63
2c0b172
a0a4a68
f6999c6
55b3491
e1105cb
95391d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 /> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { redirect } from '@sveltejs/kit'; | ||
import type { PageLoad } from './$types'; | ||
|
||
export const load: PageLoad = async () => { | ||
throw redirect(303, '/docs/tutorials/pagination/step-1'); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
--- | ||
layout: tutorial | ||
title: Build a todos app with react | ||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
||
description: Learn about using pagination in a react app using appwrite backend. | ||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
||
step: 1 | ||
difficulty: beginner | ||
readtime: 10 | ||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
||
--- | ||
|
||
**Todos App**: An app to track all your todos that you'll need to get done. In this tutorial, you will build the todos app with appwrite and react. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Focus on the end goal, title and this part should focus on general pagination with Appwrite + react, not the app. |
||
|
||
# Overview {% #overview %} | ||
**What is pagination in web development and why is it important?** | ||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Pagination is a technique used in web development to divide a large dataset into smaller manageable chunks. This practice enhances user experience by facilitating easy navigation through the dataset, making the data consumption less overwhelming. It's a common feature in data-rich applications, such as search engines and task management systems, offering benefits like reduced load times, easier data management, and a cleaner, more organized user interface. | ||
|
||
In this tutorial, while building the Todos app using Appwrite and React, you will use some pagination methods that Appwrite offers to manage the display of tasks efficiently, ensuring a user-friendly interface that allows users to navigate through their tasks effortlessly. | ||
|
||
# Concepts {% #concepts %} | ||
|
||
This tutorial will introduce the following concepts: | ||
|
||
1. Setting up your first project with Appwrite and React. | ||
2. Understanding databases and collections in Appwrite. | ||
3. Crafting queries and implementing pagination to manage data efficiently. | ||
4. Utilizing storage solution for your Todo App. | ||
|
||
# Prerequisites {% #prerequisites %} | ||
|
||
1. Basic knowledge of JavaScript and React. | ||
2. Have the latest version of [Node.js](https://nodejs.org/en) and [NPM](https://www.npmjs.com/) installed on your computer |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
--- | ||
layout: tutorial | ||
title: Next steps | ||
description: Run your react app built with appwrite | ||
step: 10 | ||
--- | ||
|
||
# Test your project {% #test-project %} | ||
Run your project | ||
|
||
```sh | ||
npm run dev -- --open --port 3000 | ||
``` | ||
|
||
open [http://localhost:3000](http://localhost:3000) in your browser. | ||
|
||
# Further resources {% #further-resources %} | ||
Read more about it in the [pagination docs here](/docs/products/databases/pagination). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
--- | ||
layout: tutorial | ||
title: Create app | ||
description: Create a react app project and integrate it with appwrite. | ||
step: 2 | ||
--- | ||
|
||
# Create React project {% #create-react-project %} | ||
|
||
Create a react app with the `npm create` command from [vite](https://vitejs.dev/). | ||
|
||
```sh | ||
npm create vite@latest tasks-app -- --template react && cd tasks-app | ||
``` | ||
|
||
# Add dependencies {% #add-dependencies %} | ||
|
||
Install the JavaScript Appwrite Web SDK. | ||
|
||
```sh | ||
npm install appwrite | ||
gewenyu99 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
|
||
You can start the development server to watch your app update in the browser as you make changes. | ||
|
||
```sh | ||
npm run dev -- --open --port 3000 | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
--- | ||
layout: tutorial | ||
title: Set up appwrite | ||
description: Import and initialize appwrite for your react application. | ||
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 optional steps. | ||
|
||
# Initialize Appwrite SDK {% #init-sdk %} | ||
|
||
To use Appwrite in our react app, we'll need to find our project ID. Find your project's ID in the **Settings** page. | ||
|
||
{% only_dark %} | ||
 | ||
{% /only_dark %} | ||
{% only_light %} | ||
 | ||
{% /only_light %} | ||
|
||
Create a new file `src/lib/appwrite.js` to hold our Appwrite related code. | ||
|
||
{% info title="One Client() instance per an app" %} | ||
Only one instance of the `Client()` class should be created per an app. | ||
{% /info %} | ||
|
||
Add the following code to it, replacing `<YOUR_PROJECT_ID>` with your project ID. | ||
|
||
```js | ||
import { Client, Databases } from "appwrite"; | ||
|
||
const client = new Client(); | ||
|
||
client | ||
.setEndpoint("https://cloud.appwrite.io/v1") | ||
.setProject("[YOUR_PROJECT_ID]"); // Replace with your project ID | ||
|
||
export const databases = new Databases(client); | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
--- | ||
layout: tutorial | ||
title: Add database | ||
description: Add a database to your react application using appwrite web SDK. | ||
step: 4 | ||
--- | ||
|
||
# Create collection {% #create-collection %} | ||
In Appwrite, a collection of documents stores the data. You can think of it like a filing cabinet. Create a collection in the [Appwrite Console](https://cloud.appwrite.io/) to store our ideas. | ||
|
||
{% only_dark %} | ||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
||
 | ||
{% /only_dark %} | ||
{% only_light %} | ||
 | ||
{% /only_light %} | ||
|
||
Create a new collection with the following attributes: | ||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Field | Type | Required | | ||
|-------------|--------|----------| | ||
| taskId | Number | Yes | | ||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| title | String | Yes | | ||
| completed | boolean | Yes | |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,64 @@ | ||||||||||||||||||||||||||||||||||||
--- | ||||||||||||||||||||||||||||||||||||
layout: tutorial | ||||||||||||||||||||||||||||||||||||
title: Environment setup | ||||||||||||||||||||||||||||||||||||
description: Setup the environment for seeding the database | ||||||||||||||||||||||||||||||||||||
step: 5 | ||||||||||||||||||||||||||||||||||||
--- | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
# Seeding the collection {% #seeding-the-collection %} | ||||||||||||||||||||||||||||||||||||
Now we need to create a script that will create documents from a setup.json file.Create a new file `./db/setup.js` in the root of the project directory to hold our related code. | ||||||||||||||||||||||||||||||||||||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||
Add the following code to it, replacing `[YOUR_DATABASE_ID]` and `[YOUR_COLLECTION_ID]` with your project ID and collection ID. | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
```js | ||||||||||||||||||||||||||||||||||||
import { databases } from "../src/lib/appwrite.js"; | ||||||||||||||||||||||||||||||||||||
import { ID } from "appwrite"; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
gewenyu99 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||
export const TODOS_DATABASE_ID = "[YOUR_DATABASE_ID]"; | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's life all these into a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||
export const TODOS_COLLECTION_ID = "[YOUR_COLLECTION_ID]"; | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||
const dataURL = "https://jsonplaceholder.typicode.com/users/1/todos"; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
async function seedCollection() { | ||||||||||||||||||||||||||||||||||||
const response = await fetch(dataURL); | ||||||||||||||||||||||||||||||||||||
const data = await response.json(); | ||||||||||||||||||||||||||||||||||||
console.log("Starting to seed collection..."); | ||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||
for (let i = 0; i < data.length; i++) { | ||||||||||||||||||||||||||||||||||||
const item = data[i]; | ||||||||||||||||||||||||||||||||||||
let promise = await databases.createDocument( | ||||||||||||||||||||||||||||||||||||
TODOS_DATABASE_ID, | ||||||||||||||||||||||||||||||||||||
TODOS_COLLECTION_ID, | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since the app is created using Vite, these env variables should be like this:
Suggested change
we can tell them to write them like this so the env variables will not be exposed to the client. |
||||||||||||||||||||||||||||||||||||
ID.unique(), | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
taskId: item.id, | ||||||||||||||||||||||||||||||||||||
title: item.title, | ||||||||||||||||||||||||||||||||||||
completed: item.completed, | ||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||
console.log(`${promise.title} has been added to the collection`); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
console.log("Seeding collection done!"); | ||||||||||||||||||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||||||||||||||||||
console.error(error); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
seedCollection(); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
# Add seed script to package.json {% #add-seed-script %} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
Open the project's package.json file and go to the `scripts` and add the following script to the list. | ||||||||||||||||||||||||||||||||||||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
```sh | ||||||||||||||||||||||||||||||||||||
"db:seed": "node ./db/setup.js", | ||||||||||||||||||||||||||||||||||||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
You can now open a terminal and run the following command. | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
```sh | ||||||||||||||||||||||||||||||||||||
npm run db:seed | ||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
You should see the command running in the terminal.When it's done check the collection to see it populated with the data. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
--- | ||
layout: tutorial | ||
title: Adding tasks | ||
description: Add tasks to your react application using appwrite | ||
step: 6 | ||
--- | ||
# Add tasks context {% #add-tasks-context %} | ||
|
||
Now we need to display our Todos to the page. In React, we can use [context](https://reactjs.org/docs/context.html) to share data between components. We'll use context and create a simple custom hook to manage out tasks. | ||
|
||
Create a new file `src/lib/context/tasks.jsx` and add the following code to it. | ||
|
||
```js | ||
import { createContext, useContext, useEffect, useState } from "react"; | ||
import { databases } from "../appwrite"; | ||
import { Query } from "appwrite"; | ||
|
||
export const TODOS_DATABASE_ID = "[YOUR_DATABASE_ID]"; | ||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
||
export const TODOS_COLLECTION_ID = "[YOUR_COLLECTION_ID]"; | ||
|
||
const TasksContext = createContext(); | ||
|
||
export function useTasks() { | ||
return useContext(TasksContext); | ||
} | ||
|
||
export function TasksProvider(props) { | ||
const [tasks, setTasks] = useState([]); | ||
|
||
async function init() { | ||
try { | ||
const response = await databases.listDocuments( | ||
TODOS_DATABASE_ID, | ||
TODOS_COLLECTION_ID, | ||
[Query.orderDesc("$createdAt"), Query.limit(4)], | ||
); | ||
setTasks(response.documents); | ||
} catch (error) { | ||
console.log(error); | ||
} | ||
} | ||
|
||
useEffect(() => { | ||
init(); | ||
}, []); | ||
|
||
return ( | ||
<TasksContext.Provider value={{ current: tasks }}> | ||
{props.children} | ||
</TasksContext.Provider> | ||
); | ||
} | ||
``` | ||
we will add more functionality to the context later | ||
|
||
## Basic Routing {% #basic-routing %} | ||
|
||
First, wrap the `main` element with the `TasksProvider` component. | ||
|
||
Update `src/App.jsx` to the following code. | ||
|
||
```js | ||
import { TasksProvider } from "./lib/context/tasks"; | ||
import { Tasks } from "./pages/Tasks"; | ||
|
||
function App() { | ||
return ( | ||
<div> | ||
<TasksProvider> | ||
<Tasks /> | ||
</TasksProvider> | ||
</div> | ||
); | ||
} | ||
|
||
export default App; | ||
``` | ||
|
||
### Tasks page {% #tasks-page %} | ||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
We are now able to create the Tasks page that will show users a list of tasks that is a small chuck of the large dataset. | ||
|
||
Create a new file `src/pages/Tasks.jsx` and add the following stub code to it. | ||
|
||
```js | ||
import { useTasks } from "../lib/context/tasks"; | ||
export function Tasks() { | ||
const tasks = useTasks(); | ||
return ( | ||
<section> | ||
<h2>Todos</h2> | ||
<ul> | ||
{tasks.current.map((task) => ( | ||
<li key={`task-${task.taskId}`}> | ||
<span>{`Task-${task.taskId}: `}</span> | ||
<input type='checkbox' id={task.taskId} name={task.title} /> | ||
<label htmlFor={task.title}>{task.title}</label> | ||
</li> | ||
))} | ||
</ul> | ||
</section> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wild suggestion, is it possible to take out all this complexity, focus on pagination, and just have pagination and UI all in app.jsx? Proper practice? No There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, and would this be the way you would want the pagination methods be structured too. so the user can just overwrite the |
||
); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or another idea, if we have this scaffolding, why don't we have each pagination method be in it's own page so someone could look through all 3? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean having one page with three sections on it? |
||
``` | ||
|
||
In the next two steps, we will explore how to use offset or cursor pagination in our application. | ||
timDeHof marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's leave it out, we'lll update this page later with all of the new tutorials
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I removed it. So now you travel to it by the URL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@timDeHof We will add it somewhere else, but we need to figure out how we'll do it. Dw about it, we'll handle it on our side :D