Skip to main content
Glama
App.vue4.74 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