Skip to content

Instantly share code, notes, and snippets.

@gabrielheinrich
Created December 19, 2022 14:53
Show Gist options
  • Save gabrielheinrich/ca6df751a68aec6e41ce12606f9adac7 to your computer and use it in GitHub Desktop.
Save gabrielheinrich/ca6df751a68aec6e41ce12606f9adac7 to your computer and use it in GitHub Desktop.
BookEdit vue in Options and Composition API
<template>
<form v-if="book" @submit.prevent="onSubmit">
<label for="title">Title: </label>
<input type="text" name="title" id="title" v-model="book.title" />
<div v-if="errors.title">
{{ errors.title }}
</div>
<button type="submit">Save</button>
</form>
</template>
<script lang="ts">
import {
defineComponent,
ref,
reactive,
computed,
watch,
watchEffect,
} from "vue";
import { useRouter } from "vue-router";
import type { Book } from "@/Book";
export default defineComponent({
props: {
isbn: {
type: String,
required: true,
},
},
setup(props) {
console.log("setup", props.isbn);
// const isbn = props.isbn; // !!!!
// const book = ref(null as null | Book);
const book = ref<null | Book>(null);
const errors = reactive({
title: "",
});
const router = useRouter();
const onSubmit = async () => {
if (!isValid.value) return;
await fetch("http://localhost:4730/books/" + props.isbn, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(book.value),
});
router.push({ path: "/books", params: { isbn: props.isbn } });
};
const validateTitle = () => {
if (!book.value) return;
if (book.value.title.length < 5) {
errors.title = "Title has to be at least 5 chars long";
} else {
errors.title = "";
}
};
watch(() => book.value?.title, validateTitle, { immediate: true });
watchEffect(async () => {
const response = await fetch("http://localhost:4730/books/" + props.isbn);
book.value = await response.json();
});
const isValid = computed(() => {
return errors.title == "";
});
return {
book,
errors,
msg: "Hello World",
validateTitle,
isValid,
onSubmit,
};
},
});
</script>
<template>
<form v-if="book" @submit.prevent="onSubmit">
<label for="title">Title: </label>
<input type="text" name="title" id="title" v-model="book.title" />
<div v-if="errors.title">
{{ errors.title }}
</div>
<button type="submit">Save</button>
</form>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import type { Book } from "@/Book";
interface ComponentData {
book: null | Book;
errors: {
[key in keyof Book]?: string;
};
}
export default defineComponent({
props: {
isbn: {
type: String,
required: true,
},
},
data(): ComponentData {
return {
book: null as null | Book, // check
errors: {
title: "",
}, // check
};
},
computed: {
isValid() {
// check
return this.errors.title == "";
},
},
methods: {
async onSubmit() {
if (!this.isValid) return;
await fetch("http://localhost:4730/books/" + this.isbn, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(this.book),
});
this.$router.push({ path: "/books", params: { isbn: this.isbn } });
},
validateTitle() {
if (!this.book) return;
if (this.book.title.length < 5) {
this.errors.title = "Title has to be at least 5 chars long";
} else {
this.errors.title = "";
}
},
},
watch: {
isbn: {
async handler(newValue, oldValue) {
const response = await fetch(
"http://localhost:4730/books/" + this.isbn
);
this.book = await response.json();
},
immediate: true,
},
"book.title"() {
this.validateTitle();
},
},
});
</script>
<style scoped></style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment