Vee Validate - Sever side validation and front end validation - laravel

I have a laravel lumen endpoint which i return errors from. The issue i am facing is that vee validate is not setting the errors?
<template>
<v-container fluid class="ma-0 pa-0">
<v-card route>
<v-card-title class="primary white--text">Create New User</v-card-title>
<v-row align="center" :class="{'px-6': $vuetify.breakpoint.xs, 'pa-8': $vuetify.breakpoint.smAndUp}">
<v-row justify="space-between" v-if="!isCreating">
<v-col cols="12" md="4">
<v-row align="center" justify="center" class="mb-4">
<v-avatar color="primary" size="128">
<v-icon size="48" dark v-if="!userAvatarName()">mdi-account</v-icon>
<span
v-if="userAvatarName()"
class="white--text display-2"
>{{ userAvatarName() }}</span>
</v-avatar>
</v-row>
</v-col>
<v-col>
<ValidationObserver v-slot="{ invalid, validated, passes }" ref="provider">
<v-form #submit.prevent="passes(handleSubmit)">
<v-row >
<v-col class="py-0">
<VTextFieldWithValidation vid="username" rules="required" v-model="newUser['username']" label="Username" />
</v-col>
</v-row>
<v-row >
<v-col class="py-0">
<VTextFieldWithValidation vid="email" rules="required|email" v-model="newUser['email']" label="Email address" />
</v-col>
</v-row>
<v-row >
<v-col class="py-0">
<VTextFieldWithValidation rules="required" v-model="newUser['first_name']" label="First name" />
</v-col>
<v-col class="py-0">
<VTextFieldWithValidation rules="required" v-model="newUser['last_name']" label="Last name" />
</v-col>
</v-row>
<v-row >
<v-col class="py-0 mt-4">
<ValidationProvider name="role" rules="required" v-slot="{ errors, valid }">
<v-select
:error-messages="errors"
:success="valid"
:items="roles"
item-text="name" item-value="value"
label="Role"
outlined
v-model="newUser['role']"
></v-select>
</ValidationProvider>
</v-col>
</v-row>
<v-divider></v-divider>
<v-row >
<v-col class="py-0">
<v-switch
v-model="newUser['send_activation']"
label="Send Activation Email"
></v-switch>
</v-col>
</v-row>
<v-row v-if="!newUser['send_activation']">
<v-col class="py-0">
<ValidationProvider name="password" :rules="!newUser['send_activation'] ? 'required|min:8' : ''" v-slot="{ errors, valid }">
<v-text-field
outlined
counter
:error-messages="errors"
:success="valid"
label="Password"
:append-icon="userPasswordVisibility.password_current_show ? 'mdi-eye' : 'mdi-eye-off'"
:type="userPasswordVisibility.password_current_show ? 'text' : 'password'"
#click:append="userPasswordVisibility.password_current_show = !userPasswordVisibility.password_current_show"
v-model="newUser['password']"
></v-text-field>
</ValidationProvider>
</v-col>
<v-col class="py-0">
<ValidationProvider name="confirmation" :rules="!newUser['send_activation'] ? 'required|password:#password' : ''" v-slot="{ errors, valid }">
<v-text-field
outlined
counter
:error-messages="errors"
:success="valid"
label="Password Confirmation"
:append-icon="userPasswordVisibility.password_new_show ? 'mdi-eye' : 'mdi-eye-off'"
:type="userPasswordVisibility.password_new_show ? 'text' : 'password'"
#click:append="userPasswordVisibility.password_new_show = !userPasswordVisibility.password_new_show"
v-model="newUser['password_confirmation']"
></v-text-field>
</ValidationProvider>
</v-col>
</v-row>
<v-row class="mt-4">
<v-col class="py-0">
<v-btn text color="primary" :to="{ name: 'organisation/users', params: { org_id: organisation.id }}">Cancel</v-btn>
</v-col>
<v-col class="py-0 text-right">
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-btn outlined color="primary" v-on="on" #click="passes(handleSubmit)" :disabled="invalid || !validated">Add New User</v-btn>
</template>
<span>Create new user and add them to <strong>{{organisation.name}}</strong>.</span>
</v-tooltip>
</v-col>
</v-row>
</v-form>
</ValidationObserver>
</v-col>
</v-row>
<v-row align="center" justify="center" v-if="isCreating">
<v-progress-circular
class="ma-5"
indeterminate
color="teal accent-1 darken-2" >
</v-progress-circular>
</v-row>
</v-row>
</v-card>
</v-container>
</template>
<script>
import { mapState } from 'vuex'
import { CREATE_USER } from '#/_apollo/GraphQL/userGraphQL'
import BreadcrumbsManager from '#/_util/breadcrumbManager'
import VTextFieldWithValidation from '#/components/inputs/VTextFieldWithValidation'
import { ValidationProvider, ValidationObserver } from "vee-validate"
export default {
name: 'CreateUser',
mixins: [BreadcrumbsManager],
props: [
'organisation'
],
components: {
ValidationObserver,
ValidationProvider,
VTextFieldWithValidation
},
data() {
return {
userPasswordVisibility: {
password_current_show: false,
password_new_show: false,
password_new_confirm_show: false
},
newUser: {
send_activation: 1
},
roles: [
{ name: 'Admin', value: 'organisation_admin' },
{ name: 'User', value: 'user' }
],
isCreating: false
}
},
computed: {
...mapState({
authUser: state => state.AUTH_STORE.authUser
}),
},
methods: {
userAvatarName() {
let name = '';
if (this.newUser['first_name']) {
name += `${this.newUser['first_name'].charAt(0).toUpperCase()}`
}
if (this.newUser['last_name']) {
name += `${this.newUser['last_name'].charAt(0).toUpperCase()}`
}
if (name !== '') {
return name
}
return false
},
async handleSubmit() {
this.isCreating = true
this.newUser['reseller_id'] = this.authUser.reseller_id
this.newUser['organisation_id'] = parseInt(this.$route.params.org_id)
this.newUser['send_activation'] = this.newUser['send_activation'] ? 1 : 0
var refs = this.$refs.provider
this.$apollo
.mutate({
mutation: CREATE_USER,
variables: this.newUser
})
.then(response => {
this.isCreating = false
const user = response.data.createUser
this.$store.commit('USER_STORE/CREATE_USER', response.data.createUser)
this.$toast.success('Successfully created new user.')
this.$router.push({ name: "organisation/users", params: { org_id: this.$route.params.org_id }}, () => {})
}).catch((error) => {
this.isCreating = false
this.$toast.error(error.graphQLErrors[0].extensions.code.message)
let errors = error.graphQLErrors[0].extensions.code.errors
console.log(errors)
refs.setErrors(errors)
console.log(refs)
})
refs.validate();
}
},
created() {
this.setBreadcrumbs([
{ text: 'Dashboard' , path: '/' },
{ text: 'Organisations' , path: '/organisation/all/' },
{ text: ':organisation' },
{ text: 'Users', path: `/organisation/${this.organisation.org_id}/users/` },
{ text: 'Create' }
])
this.replaceBreadcrumb({
find: ':organisation',
replace: { text: this.organisation.name, path: `/organisation/${this.organisation.org_id}` }
})
}
}
</script>
In the catch error on handle submit i can see the sever errors but my view wont show an error on the field?
I have took inspiration from the following from the docs:
https://codesandbox.io/s/veevalidate-backend-driven-validation-ynrp9?from-embed=&file=/src/App.vue
But to no avail.
Can anyone help?

It turns out apollo client is a promise which isnt resolved so the fix is:
const userResponse = await this.$apollo
.mutate({
mutation: CREATE_USER,
variables: this.newUser
})
.then(response => {
this.isCreating = false
const user = response.data.createUser
this.$store.commit('USER_STORE/CREATE_USER', response.data.createUser)
this.$toast.success('Successfully created new user.')
this.$router.push({ name: "organisation/users", params: { org_id: this.$route.params.org_id }}, () => {})
}).catch((error) => {
this.isCreating = false
this.$toast.error(error.graphQLErrors[0].extensions.code.message)
return error.graphQLErrors[0].extensions.code.errors.detail
})
this.$refs.provider.setErrors(userResponse);

Related

vuetify.js how to add type="submit" to Loaders Button

i am new in Vuetify.js im trying to make submit Form using Loaders Button
i tryid but it not worked.
my tryid code
<v-btn
rounded
class="ma-2"
type="submit"
:loading="loading2"
:disabled="loading2"
color="primary"
#click="loader = 'loading2'"
>
Login
<template v-slot:loader>
<span>Loading...</span>
</template>
</v-btn>
my main code
<template>
<v-row justify="center">
<v-col cols="12" sm="6">
<form #submit.prevent="submit">
<v-card ref="form">
<v-card-text>
<h3 class="text-center">Login</h3>
<v-divider class="mt-3"></v-divider>
<v-col cols="12">
<v-img
:src="require('#/assets/1.png')"
class="my-3"
contain
height="80"
/>
</v-col>
<v-col cols="12" sm="12">
<v-text-field
v-model.trim="form.mobile_number"
type="number"
label="Mobile No"
solo
autocomplete="off"
></v-text-field>
<small class="form-text red--text" v-if="errors.mobile_number">{{
errors.mobile_number[0]
}}</small>
</v-col>
<v-col cols="12">
<v-text-field
v-model.trim="form.password"
type="password"
label="Password"
solo
autocomplete="off"
append-icon="mdi-eye"
></v-text-field>
<small class="form-text red--text" v-if="errors.password">{{
errors.password[0]
}}</small>
</v-col>
</v-card-text>
<v-divider class="mt-12"></v-divider>
<v-card-actions>
<div class="text-center">
<v-btn
rounded
color="primary"
dark
to="/AppMain/UserRegisterPage"
nuxt
>Register</v-btn
>
</div>
<v-spacer></v-spacer>
<div class="text-center">
<v-btn
rounded
class="ma-2"
type="submit"
:loading="loading2"
:disabled="loading2"
color="primary"
#click="loader = 'loading2'"
>
Login
<template v-slot:loader>
<span>Loading...</span>
</template>
</v-btn>
<!-- <v-btn rounded type="submit" color="primary" dark>Login</v-btn> -->
</div>
</v-card-actions>
</v-card>
</form>
</v-col>
</v-row>
</template>
<script>
export default {
middleware: ["guest"],
data() {
return {
loader: null,
loading: false,
loading2: false,
loading3: false,
loading4: false,
loading5: false,
form: {
mobile_number: "",
password: "",
},
};
},
watch: {
loader() {
const l = this.loader;
this[l] = !this[l];
setTimeout(() => (this[l] = false), 3000);
this.loader = null;
},
},
methods: {
async submit() {
await this.$auth.loginWith("local", {
data: this.form,
});
this.$router.push("/");
},
},
};
</script>
<style>
.custom-loader {
animation: loader 1s infinite;
display: flex;
}
#-moz-keyframes loader {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
#-webkit-keyframes loader {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
#-o-keyframes loader {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
#keyframes loader {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
</style>
You create and use the variable "loading" but never set it.
For resolve your problem, please, add in your #click method something like
loading = true
....
and then make loading same to false, for stop the loading style of your button.
Good luck!

Submit Vuetify form with the enter button

Thought this would be straight forward but it isn't. With vuetify forms how do I bind a form to the enter button so that it's submit function is invoked with the enter button?
Figures I'd find a work around as soon as I posted the question. I found the answer here:
https://github.com/vuetifyjs/vuetify/issues/1545
Basically I had to add an event listener to the component to attach the enter key press to my authenticate method. Here is the component in questions:
<template>
<v-card>
<v-card-title>
<span class="headline">Login</span>
</v-card-title>
<v-form v-model="isValid">
<v-card-text>
<v-container>
<v-row>
<v-col cols="12">
<v-text-field
v-model="username"
label="User Name"
prepend-icon="mdi-account circle"
:rules="userNameRequired"
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="password"
label="Password"
:type="showPassword ? 'text' : 'password'"
prepend-icon="mdi-lock"
:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
#click:append="showPassword = !showPassword"
:rules="passwordRequired"
required
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="close"> Close </v-btn>
<v-btn color="blue darken-1" text #click="authenticate" :disabled="!isValid">
Login
</v-btn>
</v-card-actions>
</v-form>
</v-card>
</template>
<script>
import { authenticationService } from "../services/authenticationService/authentication.service";
import User from "../models/user";
export default {
name: "LoginForm",
props: ["userForAuthentication"],
data: () => ({
username: "",
password: "",
user: {},
isValid: true,
showPassword: false,
userNameRequired: [(v) => !!v || "User Name is required"],
passwordRequired: [(v) => !!v || "Password is required"],
}),
methods: {
async authenticate() {
try {
const response = await authenticationService.authenticateUser(
this.$data.username,
this.$data.password
);
if (response.status === 200) {
this.$data.user.shallowClone(response.data.user);
await this.resetData();
this.$emit(
"user-logging-in-event",
this.$data.user,
response.data.token
);
this.$toasted.success(`${this.$data.user.fullName} is logged in.`, {
duration: 3000,
});
} else if (response.status === 400) {
this.$toasted.error("Username or Password is incorrect.", {
duration: 3000,
});
} else {
this.$toasted.error(
"An error occurred while trying to authenticate the user",
{
duration: 3000,
}
);
}
} catch (error) {
this.$toasted.error(error, {
duration: 3000,
});
}
},
close() {
this.$emit("user-logging-in-event", null, null);
},
async resetData() {
this.$data.username = "";
this.$data.password = "";
},
},
mounted() {
let self = this;
window.addEventListener("keyup", function (event) {
if (event.keyCode === 13) {
self.authenticate();
}
});
this.$data.user = new User();
this.$data.user.shallowClone(this.$props.userForAuthentication);
},
};
</script>

Usng Vuetify v-data-table how to set chip color based and value and item

I would like to set the chip color for the value of an item in a v-data-table using both the items value and the items id. So i want to set values under 0.5 to red only if the id is "Cl2". ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[enter image description here][1]
Here's my table and code:
[1]: https://i.stack.imgur.com/Sp8DQ.jpg
<template>
<v-data-table
:headers="headers"
:items="parameters"
class="elevation-1"
hide-default-footer
calculate-widths
dense
>
<template v-slot:top>
<v-toolbar flat color="white">
<v-toolbar-title>Parameter Input {{ scanSite }} </v-toolbar-title>
<v-divider class="mx-4" inset vertical></v-divider>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" persistent>
<v-card
max-width="315px"
max-height="240px"
style="position: absolute; top: 90px; left: 30px; right: 30px;"
>
<v-card-title>
<span class="">{{ editedItem.name }} - Enter/Edit Values </span>
</v-card-title>
<v-card-text class="card-text py-0 my-0">
<v-container>
<v-row class="row py-0">
<v-col cols="5">
<v-text-field
v-model="editedItem.value"
label="Value"
></v-text-field>
</v-col>
<v-col cols="5">
<v-text-field
v-model="editedItem.sid"
label="Save ID"
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="close">Cancel</v-btn>
<v-btn color="blue darken-1" text #click="save">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:[`item.actions`]="{ item }">
<v-icon small class="mr-2" #click="editItem(item)">
mdi-pencil
</v-icon>
<v-icon small class="mr-2" #click="addItem(item)">
mdi-plus
</v-icon>
</template>
<template v-slot:[`item.analyse`]="{ item }">
<v-simple-checkbox
color="green"
v-model="item.analyse"
></v-simple-checkbox>
</template>
<template v-slot:[`item.value`]="{ item }">
<v-chip :color="getColor(item.value)" small dark>{{ item.value }}</v-chip>
</template>
</v-data-table>
</template>
<script>
import { mapState } from "vuex";
export default {
data() {
return {
scanSite: "",
dialog: false,
selected: [],
editedItem: {
name: "",
value: 0,
sid: 0
}
};
},
computed: {
...mapState(["scanSite", "headers", "parameters", "sites"])
},
methods: {
editItem(item) {
this.editedIndex = this.parameters.indexOf(item);
this.editedItem = Object.assign({}, item);
this.dialog = true;
},
save() {
Object.assign(this.parameters[this.editedIndex], this.editedItem);
this.dialog = false;
},
close() {
this.dialog = false;
},
getColor(value, id) {
console.log(value, id);
if (value > 0.5 && id == "Cl2") return "red";
else if (value > 10) return "orange";
else return "green";
},
addItem(item) {
this.editedIndex = this.parameters.indexOf(item);
this.editedItem = Object.assign({}, item);
this.parameters.push(this.editedItem);
this.dialog = true;
}
}
};
</script>
I think you need to set both paramareters when you call your getColor() function, like so
<v-chip :color="getColor(item.value, item.id)" small dark>{{ item.value }}</v-chip>
or pass the whole item object and manage it in the getColor() function
<v-chip :color="getColor(item)" small dark>{{ item.value }}</v-chip>
...
getColor(item) {
if (item.value > 0.5 && item.id == "Cl2") return "red";
else if (item.value > 10) return "orange";
else return "green";
},

Call to undefined method Person::id()

Something really weird is happening, when I don't upload any image in my form it doesn't throw any error, but when I do it throws this: Call to undefined method App\Models\User_Management\Person::id()
Front-End Code
<template>
<div>
<div class="container-2 mx-8 mt-10">
<v-row>
<v-col xs="12" sm="12" md="8">
<v-card>
<v-form enctype="multipart/form-data">
<div class="container-2 mx-2">
<!-- Personal information -->
<v-divider></v-divider>
<p class="mt-2">Personal information</p>
<v-row>
<v-col cols="12" sm="12" md="4">
<v-text-field v-model="person.first_name" counter="150" :rules="id="first_name" name="first_name" label="First Name" color="black"></v-text-field>
</v-col>
<v-col cols="12" sm="12" md="4">
<v-text-field label="Last Name" v-model="person.last_name" counter="150" name="last_name" color="black"></v-text-field>
</v-col>
</v-row>
<v-divider></v-divider>
<p class="mt-2">Profile Picture</p>
<v-row>
<v-col cols="12" sm="12" md="6">
<input type="file" id="avatar" ref="avatar" v-on:change="fileUpload()"/>
</v-col>
</v-row>
<div class="right-align">
<v-btn color="yellow" class="black-text" #click="update()">Submit</v-btn>
<v-btn color="red accent-3" class="black-text">Back</v-btn>
</div>
<br>
</div>
</v-form>
</v-card>
</v-col>
</v-row>
</div>
</div>
</template>
<script>
export default {
data() {
return {
//Data
person: {
first_name: '',
last_name: '',
},
userAvatar: undefined,
person_id: 1,
}
}
},
methods: {
fileUpload(){
this.userAvatar = this.$refs.avatar.files[0];
},
update() {
let formData = new FormData();
formData.append('first_name', this.person.first_name);
formData.append('last_name', this.person.last_name);
formData.append('profile_picture', this.userAvatar);
formData.append('_method', 'PUT');
axios.post(`/profile/${this.person_id}`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(res => {
console.log(res.data);
}).catch(e => {
console.log(e);
})
},
}
}
}
</script>
Back-End Code
public function update(Request $request, $id)
{
//Person
$person = Person::find($id);
//Avatar
$name = null;
$image = null;
if ($request->hasFile('profile_picture')){
$file = $request->file('image');
$name = $person->id();
$file->move(public_path().'/img/uploads/avatars', $name);
//Image
$image = new Image();
$image->path = $name;
$image->save();
}
//Profile
$person->first_name = $request->first_name;
$person->last_name = $request->last_name;
if ($image)
$person->image_id = $image->id;
$person->save();
}
My import for the Model is written like this
use App\Models\User_Management\Person;
For some reason it seems like it doesn't recognize the ::find($id) method when I upload an image but I have no idea why, is there any fix for this? I tried to log the $request variable and it's throwing the following
[2020-04-13 19:41:23] local.INFO: array (
'first_name' => 'John',
'last_name' => 'Smith',
'_method' => 'PUT',
'profile_picture' =>
Illuminate\Http\UploadedFile::__set_state(array(
'test' => false,
'originalName' => 'kekas.jpg',
'mimeType' => 'image/jpeg',
'error' => 0,
'hashName' => NULL,
)),
)
It doesn't seem like there's something wrong with the front-end code but I left it here just in case it gives a hint about this weird error.
There's no method $person->id(); in Person Instanse. You need do get a property like this: $person->id. Hope it helps.
You're error in this code line: $name = $person->id();

How to wire up external data in Vuetify datatables

I have just started with Vue and found Vuetify ( and very impressed ) . I'm a bit of a newbie with node.js as well but some experience.
I am trying to find some examples on loading data from external API's into the vuetify datagrid - CRUD type stuff, reasonably large amounts of data paginated. The documentation in Vuetify is a little lacking in this regard. Should I be using Vuex?
If you want to call external API using REST, you'll need to use axios, which is a NPM package allowing you to make GET, POST and all that kind.
Let's use this online working API for our example. First, you need to get your data by calling this API. A good tutorial on Internet will show you more details, but let's use this code.
this.todos = axios.get('https://jsonplaceholder.typicode.com/todos/')
.then(response => { this.todos = response.data })
.catch(error => { console.log(error)});
Then you just have to use the datatable like in the documentation. Here is a CodePen to help you see, briefly, how I made the API call and then displayed it. It all comes from the official documentation, just modified to call a REST API. I'll put the code also here, in order to save it for future readers too.
<div id="app">
<v-app id="inspire">
<div>
<v-toolbar flat color="white">
<v-toolbar-title>Todos CRUD</v-toolbar-title>
<v-divider
class="mx-2"
inset
vertical
></v-divider>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="500px">
<v-btn slot="activator" color="primary" dark class="mb-2">New Item</v-btn>
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container grid-list-md>
<v-layout wrap>
<v-flex xs12 sm6 md4>
<v-text-field v-model="editedItem.userId" label="User ID"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-text-field v-model="editedItem.id" label="ID"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-text-field v-model="editedItem.title" label="Title"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-checkbox v-model="editedItem.completed" label="Completed?"></v-checkbox>
</v-flex>
</v-layout>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" flat #click="close">Cancel</v-btn>
<v-btn color="blue darken-1" flat #click="save">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
<v-data-table
:headers="headers"
:items="todos"
class="elevation-1"
>
<template slot="items" slot-scope="props">
<td class="text-xs-right">{{ props.item.userId }}</td>
<td class="text-xs-right">{{ props.item.id }}</td>
<td class="text-xs-right">{{ props.item.title }}</td>
<td class="text-xs-right">{{ props.item.completed }}</td>
<td class="justify-center layout px-0">
<v-icon
small
class="mr-2"
#click="editItem(props.item)"
>
edit
</v-icon>
<v-icon
small
#click="deleteItem(props.item)"
>
delete
</v-icon>
</td>
</template>
<template slot="no-data">
<v-btn color="primary" #click="initialize">Reset</v-btn>
</template>
</v-data-table>
</div>
</v-app>
</div>
And then the associated JS.
new Vue({
el: '#app',
data: () => ({
dialog: false,
headers: [
{
text: 'User ID',
align: 'left',
sortable: false,
value: 'userId',
width: '10'
},
{ text: 'ID', value: 'id', width: '10' },
{ text: 'Title', value: 'title' },
{ text: 'Completed', value: 'completed' }
],
todos: [],
editedIndex: -1,
editedItem: {
userId: 0,
id: 0,
title: '',
completed: false
},
defaultItem: {
userId: 0,
id: 0,
title: '',
completed: false
}
}),
computed: {
formTitle () {
return this.editedIndex === -1 ? 'New Item' : 'Edit Item'
}
},
watch: {
dialog (val) {
val || this.close()
}
},
created () {
this.initialize()
},
methods: {
initialize () {
this.todos = axios.get('https://jsonplaceholder.typicode.com/todos/')
.then(response => { this.todos = response.data })
.catch(error => { console.log(error)});
},
editItem (item) {
this.editedIndex = this.todos.indexOf(item)
this.editedItem = Object.assign({}, item)
this.dialog = true
},
deleteItem (item) {
const index = this.todos.indexOf(item)
confirm('Are you sure you want to delete this item?') && this.todos.splice(index, 1)
},
close () {
this.dialog = false
setTimeout(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
}, 300)
},
save () {
if (this.editedIndex > -1) {
Object.assign(this.todos[this.editedIndex], this.editedItem)
} else {
this.todos.push(this.editedItem)
}
this.close()
}
}
})

Resources