Skip to main content
Glama
App.vue4.79 kB
<script setup lang="ts"> import { ref, computed, onMounted, watch } from "vue"; // ============================================ // Types // ============================================ interface User { id: number; name: string; email: string; } interface Todo { id: number; text: string; completed: boolean; } // ============================================ // Composables (Reusable Logic) // ============================================ // useFetch - Data fetching with loading/error states function useFetch<T>(url: string) { const data = ref<T | null>(null); const loading = ref(true); const error = ref<Error | null>(null); const fetchData = async () => { loading.value = true; error.value = null; try { const res = await fetch(url); if (!res.ok) throw new Error(`HTTP ${res.status}`); data.value = await res.json(); } catch (e) { error.value = e as Error; } finally { loading.value = false; } }; onMounted(fetchData); return { data, loading, error, refetch: fetchData }; } // useLocalStorage - Persistent state function useLocalStorage<T>(key: string, defaultValue: T) { const storedValue = localStorage.getItem(key); const data = ref<T>(storedValue ? JSON.parse(storedValue) : defaultValue); watch( data, (newValue) => { localStorage.setItem(key, JSON.stringify(newValue)); }, { deep: true }, ); return data; } // ============================================ // Component State & Logic // ============================================ // Reactive state const count = ref(0); const newTodo = ref(""); const todos = useLocalStorage<Todo[]>("todos", []); // Computed properties const completedCount = computed( () => todos.value.filter((t) => t.completed).length, ); const remainingCount = computed( () => todos.value.length - completedCount.value, ); // Methods function increment() { count.value++; } function addTodo() { if (!newTodo.value.trim()) return; todos.value.push({ id: Date.now(), text: newTodo.value.trim(), completed: false, }); newTodo.value = ""; } function removeTodo(id: number) { const index = todos.value.findIndex((t) => t.id === id); if (index !== -1) { todos.value.splice(index, 1); } } function toggleTodo(id: number) { const todo = todos.value.find((t) => t.id === id); if (todo) { todo.completed = !todo.completed; } } // Data fetching example const { data: users, loading: usersLoading, error: usersError, } = useFetch<User[]>("https://jsonplaceholder.typicode.com/users"); </script> <template> <div class="app"> <!-- Counter Example --> <section class="counter"> <h2>Counter: {{ count }}</h2> <button @click="increment">Increment</button> <button @click="count--">Decrement</button> <button @click="count = 0">Reset</button> </section> <!-- Todo List Example --> <section class="todos"> <h2>Todo List</h2> <p>{{ remainingCount }} remaining, {{ completedCount }} completed</p> <form @submit.prevent="addTodo" class="todo-form"> <input v-model="newTodo" placeholder="Add a todo..." class="todo-input" /> <button type="submit">Add</button> </form> <ul class="todo-list"> <li v-for="todo in todos" :key="todo.id" :class="{ completed: todo.completed }" > <input type="checkbox" :checked="todo.completed" @change="toggleTodo(todo.id)" /> <span>{{ todo.text }}</span> <button @click="removeTodo(todo.id)">×</button> </li> </ul> </section> <!-- Data Fetching Example --> <section class="users"> <h2>Users (Fetched)</h2> <div v-if="usersLoading">Loading...</div> <div v-else-if="usersError" class="error"> Error: {{ usersError.message }} </div> <ul v-else> <li v-for="user in users" :key="user.id"> {{ user.name }} ({{ user.email }}) </li> </ul> </section> </div> </template> <style scoped> .app { max-width: 600px; margin: 0 auto; padding: 20px; } section { margin-bottom: 30px; padding: 20px; border: 1px solid #ddd; border-radius: 8px; } button { padding: 8px 16px; margin: 4px; cursor: pointer; } .todo-form { display: flex; gap: 8px; margin-bottom: 16px; } .todo-input { flex: 1; padding: 8px; } .todo-list { list-style: none; padding: 0; } .todo-list li { display: flex; align-items: center; gap: 8px; padding: 8px; border-bottom: 1px solid #eee; } .todo-list li.completed span { text-decoration: line-through; color: #888; } .error { color: red; } </style>

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/millsydotdev/Code-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server