-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathApp.vue
More file actions
111 lines (102 loc) · 2.93 KB
/
App.vue
File metadata and controls
111 lines (102 loc) · 2.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<script setup lang="ts">
import type {
Post,
User,
} from "https://esm.sh/*@untypeable/jsonplaceholder@1.0.2";
import { onWatcherCleanup, reactive, ref, watchEffect } from "vue";
const urlBase = "https://jsonplaceholder.typicode.com";
const selectedUserId = ref<number>();
const posts = reactive<{
value?: Post[];
loading: boolean;
}>({ loading: false });
const users = reactive<{
value?: User[];
loading: boolean;
}>({ loading: false });
const readmeHTML = ref<string | undefined>(undefined);
async function fetchUsers(signal: AbortSignal) {
users.loading = true;
try {
const response = await fetch(`${urlBase}/users`, { signal });
const data = await response.json();
signal.throwIfAborted();
users.value = data;
users.loading = false;
} catch (error) {
if (signal.aborted) return;
users.loading = false;
throw error;
}
}
async function fetchPosts(userId: number, signal: AbortSignal) {
posts.loading = true;
try {
const response = await fetch(`${urlBase}/posts?userId=${userId}`, {
signal,
});
const data = await response.json();
signal.throwIfAborted();
posts.value = data;
posts.loading = false;
} catch (error) {
if (signal.aborted) return;
posts.loading = false;
throw error;
}
}
async function fetchReadme(signal: AbortSignal) {
try {
const [{ marked }, readmeMarkdown] = await Promise.all([
import("https://esm.sh/*marked@17.0.0"),
fetch("./README.md", { signal }).then((res) => res.text()),
]);
const html = await marked.parse(readmeMarkdown);
signal.throwIfAborted();
readmeHTML.value = html;
} catch (error) {
if (signal.aborted) return;
throw error;
}
}
watchEffect(function updateUsers() {
const ctrl = new AbortController();
onWatcherCleanup(() => ctrl.abort());
fetchUsers(ctrl.signal);
});
watchEffect(function updatePosts() {
if (selectedUserId.value === undefined) return;
const ctrl = new AbortController();
onWatcherCleanup(() => ctrl.abort());
fetchPosts(selectedUserId.value, ctrl.signal);
});
watchEffect(function updateReadme() {
const ctrl = new AbortController();
onWatcherCleanup(() => ctrl.abort());
fetchReadme(ctrl.signal);
});
</script>
<template>
<section v-html="readmeHTML"></section>
<label v-if="users.value">
Select User:
<select v-model.number="selectedUserId">
<option hidden selected></option>
<option v-for="user in users.value" :key="user.id" :value="user.id">
@{{ user.username }}: {{ user.name }}
</option>
</select>
</label>
<p v-else-if="users.loading">Loading Users...</p>
<ul v-if="posts.value">
<li v-for="post in posts.value" :key="post.id">{{ post.title }}</li>
</ul>
<p v-else-if="posts.loading">Loading Posts...</p>
<p v-else-if="users.value">Select User to view posts</p>
<p>
Data Source:
<a href="https://jsonplaceholder.typicode.com/" target="_blank">
JSONPlaceholder
</a>
</p>
</template>