update google chart with selected period parameter from v-select - javascript

I am looking for a way to update the google bar type chart display
by selecting the period value from v-select.
so far, Initially, my script loaded, call 'async created()' and
get Data from REST API to MongoDB call module and store returned data in posts[].
and then finally save it to chart data array.
The data format is like this
Key
Type
Date (YYYY-MM-DD)
String
success_count
int
failure_count
int
what I wanna do is, each v-select values consist with {name, id} pair.
Is there any way to pass selected 'selectedItem.id' to
script's created(parameter) method or PostService.get_dashboard_Posts(parameter)'s parameter position?
without refreshing the whole page?
below is my CSS code
<template>
<v-app
:style="{ background: $vuetify.theme.themes.light.background }"
style="max-height:100vw;"
>
<v-container style="min-height:100%;">
<v-layout row class="ma-4">
<v-app style="background-color:grey lighten-1:" class="rounded">
<v-row>
<v-col cols="12" md="12" class="ligne">
<v-container>
<v-row>
<v-col cols="12" md="12">
<h1 class="heading mb-2 grey--text">Dashboard </h1>
<v-card>
<template>
<p class="error" v-if="error">{{error}}</p>
<div class = "posts-container">
<div class="post"
v-for="(post,index) in posts"
v-bind:item="post"
v-bind:index="index"
v-bind:key="post._id"
>
</div>
</div>
<div id="app">
<v-col cols="12" md="12">
<v-row>
<v-col cols="12" md="9"><h3 class="subTitle">Number Of Transactions</h3></v-col>
<v-col cols="12" md="3">
<v-select
:items="comboItem"
v-model="selectedItem"
item-text="name"
item-value="id"
label="Period"
single-line
dense
v-on:change=""
return-object>
</v-select>
</v-col>
</v-row>
<GChart
type="ColumnChart"
:data="chartData"
:options="chartOptions"
/>
</v-col>
</div>
</template>
</v-card>
</v-col>
</v-row>
</v-container>
</v-col>
</v-row>
</v-app>
</v-layout>
</v-container>
below is my script code
<script>
// # is an alias to /src
import PostService from "../PostService";
export default {
name: "PostComponent",
data: () => ({
posts:[],
comboItem:[
{
name: 'today',
id: 1
},
{
name: 'last week',
id: 2
},
{
name: 'last month',
id: 3
},
{
name: 'last year',
id: 4
},
],
chartData: [
["date", "success", "failure"]
],
chartOptions: {
chart: {
title: "Company performance",
subtitle: "Sales,Expences,and Profit:2016-2019"
},
hAxis: {
title: "date",
textStyle: {
fontSize:9
}
},
vAxis: {
title: "frequency",
textStyle: {
fontSize:15
}
},
legend: {
position : 'top',
alignment:'center'
}
}
}),
async created() {
try {
this.posts = await PostService.get_dashboard_Posts();
for(var i=0;i<Object.keys(this.posts[0]).length;i++){
this.chartData.push([`${this.posts[0][i]._id.year}-${this.posts[0]
[i]._id.month}-${this.posts[0][i]._id.day}`, this.posts[0][i]._id.success_count, this.posts[0]
[i]._id.failure_count])
}
} catch (err) {
this.error = err.message;
}
},
components: {},
computed: {
theme() {
return this.$vuetify.theme.dark ? "dark" : "light";
}
},
methods:{
}
};
</script>

Yes that is possible,
Basically we need to be able to respond to changes to selectedItem. All the loading currently happens inside the created function, which we cannot run again, so step 1 would be to move or copy the body of the created function to a new method, which we may call load or something. Now we can respond to changed to selectedItem by calling load.
async created() {
await this.load();
},
components: {},
computed: {
theme() {
return this.$vuetify.theme.dark ? "dark" : "light";
}
},
methods: {
async load() {
try {
this.posts = await PostService.get_dashboard_Posts();
for (var i = 0; i < Object.keys(this.posts[0]).length; i++) {
this.chartData.push([`${this.posts[0][i]._id.year}-${this.posts[0]
[i]._id.month}-${this.posts[0][i]._id.day}`, this.posts[0][i]._id.success_count, this.posts[0]
[i]._id.failure_count
])
}
} catch (err) {
this.error = err.message;
}
}
}
There are a few ways we can respond to changes, i'll show you a few:
We can define this behavior on the input element directly in the template:
<v-select
v-model="selectedItem"
...
v-on:change="load(selectedItem.id)"
...
>
</v-select>
Another way to tell vue to respond to changes is by using watchers:
...
async created() {
await this.load();
},
watch: {
selectedItem(newValue) {
this.load(newValue.id);
// At the moment the watcher runs this.selectedItem is already
// the newValue (this.selectedItem === newValue) so we could also
// do this:
this.load(this.selectedItem.id);
}
},
components: {},
...
The last thing that I haven't covered and leave up to you is to supply the selectedItem.id to PostService.get_dashboard_Posts

Related

I want to display an error message when an email address exists in a Vuetiify form

assumption
I am creating a user registration form using Vuetify. I want to display an error if an email address already exists at registration, how can I implement this functionality using Vuetify's textfield?
The configuration is loading the email componentized in the Form. It uses v-model between components with setter and getter to be updated reactively.
What we want to achieve
I want to use v-text-field of Vuetify to show an error if the email address already exists.
Code
Email
UserFormTextFieldEmail
<template>
<v-row justify="center">
<v-col cols="12" md="10" sm="10">
<v-text-field
v-model="setEmail"
type="text"
label="email"
prepend-icon="mdi-email"
:rules="rules"
/>
<p class="caption mb-0" />
</v-col>
</v-row>
</template>
<script>
export default {
props: ['email'],
data () {
return {
rules: [
v => !!v || '',
v => /.+#.+\..+/.test(v) || ''
]
}
},
computed: {
setEmail: {
get () { return this.email },
set (newVal) { return this.$emit('update:email', newVal) }
}
}
}
</script>
Form
<v-card class="pa-7 ma-10 mx-auto" max-width="600">
<div class="login-logo">
<img
:src="logoImg"
width="70px"
>
</div>
<v-form
ref="form"
v-model="isValid"
>
<v-container>
<UserFormTextFieldUserName :name.sync="userInfo.name" />
<UserFormTextFieldEmail :email.sync="userInfo.email" :error.sync="errorMessage" /> // email
<UserFormTextFieldPassword :password.sync="userInfo.password" />
<v-row justify="center">
<v-col cols="12" md="10" sm="10">
<v-btn
:disabled="!isValid || loading"
:loading="loading"
block
class="white--text"
color="deep-purple lighten-1"
#click="signup"
>
・・・
</v-btn>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card>
</div>
</template>
<script>
import '#/assets/css/user-form.scss'
import logoImg from '~/assets/images/login_logo.png'
export default {
auth: false,
data () {
return {
isValid: false,
loading: false,
logoImg,
show: false,
userInfo: {
name: '',
email: '',
password: ''
},
errorMessage:''
}
},
methods: {
signup () {
this.$axios.post('/api/v1/auth', this.userInfo)
.then((response) => {
this.$store.commit('alertSwitchSuccess', true)
setTimeout(() => {
this.$store.commit('alertSwitchSuccess', false)
this.$router.replace(`/user/login`)
}, 2000)
})
.catch((e) => {
})
}
}
}
</script>
I wanted to do the same thing before, and here's my solution:
methods: {
checkemail() {
axios
.get("/api/v1/auth")
.then((response) => {
this.emails = response.data.map((a) =>
a.email);
});
},
},
This function return an Array of all the emails
data() {
return {
//email input v-model
email: "",
//declare the emails array
emails: [],
rules: [
// your rules
(v) =>
(v && this.emails.indexOf(this.email)<0)||
"this email is already existing",
],
I hope that helps

How can I get only the field from a vuetify table to post in axios

How can I get only the field from a vuetify table to post by in axios, I have a table as I show in the following code
<div id="app">
<v-app id="inspire">
<v-card>
<v-card-title>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
</v-card-title>
<v-data-table
:headers="headers"
:items="students"
:search="search"
>
<template v-slot:item.status="{ item }">
<v-btn v-if="item.status" #click="add(item)">Add</v-btn>
<v-btn v-else disabled>Temporarily no access</v-btn>
</template>
</v-data-table>
</v-card>
</v-app>
</div>
and from script I have the following code
const app = new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
return {
search: '',
headers: [
{ text: 'Number list', align: 'start', value: 'id'},
{ text: 'Name', align: 'start', value: 'name'},
{ text: 'Options', value: 'status' }
],
students: [
{
id: 1,
name: pedro,
status: activo,
},
{
id: 2,
name: juan,
status: activo,
},
{
id: 3,
name: jose,
status: activo,
},
]
};
},
methods: {
add (item) {
axios.post('http://localhost/list?id=' + item.id)
.then(response => {
this.response = response
}).catch(e => {
console.log(e)
});
},
}
});
What I want to do is that when it is clicked I only sent the id per post with axios but when I click it I get an error in the console that says that item is undefined that is my main error, and as you can see I need it to be first verify that the student's status is active. Below I also add an image of the generated table for further reference.
table reference
Edit
i forgot put
#click="add(item)"
but still doesn´t work, no longer even an error appears in the console even if it has the catch
Try:
<v-btn v-if="item.estado" #click="add(item)">Add</v-btn>
Try 2:
Check first if item is coming through correctly by adding the following line. Maybe this will help in tracking down the problem
<template v-slot:item.status="{ item }">
<p>Debug item text: {{item}}</p>
<v-btn v-if="item.status" #click="add(item)">Add</v-btn>
<v-btn v-else disabled>Temporarily no access</v-btn>
</template>

TextArea Avoid mutating a prop directly since the value will be overwritten

I know that a similar question has already been dealt with on stackoverflow. But I could not put together a solution from the proposed one. I am very ashamed.
The essence is this: I have a component and another one inside it.
The child component-VClipboardTextField is a ready-made configured text-area. I couldn't get the input out of there and I don't get an error when I try to enter it.
Avoid mutating a prop directly since the value will be overwritten
whenever the parent component re-renders. Instead, use a data or
computed property based on the prop's value. Prop being mutated:
"message"
code tabs-item.vue
<template>
<v-container fluid>
<v-row align="center">
<v-col cols="9">
<v-card flat>
<v-card-text>
<h1>Request</h1>
<v-container>
<v-textarea v-model="message"
placeholder="Placeholder"
label="Request"
auto-grow
clear-icon="mdi-close-circle-outline"
clearable
rows="10"
row-height="5"
#click:clear="clearMessage"
></v-textarea>
<v-textarea v-model="response"
placeholder="Placeholder"
label="Request2"
auto-grow
counter
rows="10"
row-height="5"
color="success"
></v-textarea>
<VClipboardTextField ></VClipboardTextField>
<VClipboardTextField isReadOnly></VClipboardTextField>
</v-container>
<v-row>
<v-btn
dark
color="primary"
elevation="12"
justify="end"
float-right
#click="sendRequest"
>
Send Request
</v-btn>
</v-row>
</v-card-text>
</v-card>
</v-col>
<v-col cols="3">
<schema-selector #changeSchema="onChangeSchema"></schema-selector>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: 'tabs-item',
props: ['response'],
data() {
return {
schema: String,
message: '',
}
},
methods: {
sendRequest() {
const message = {
message: this.message,
method: this.schema.state
}
this.$emit('sendRequest', message)
},
clearMessage() {
this.message = ''
},
onChangeSchema(selectS) {
console.log("get schema: ", selectS.state)
this.schema = selectS
}
},
}
</script>
and child VClipboardTextField.vue
<template>
<v-container>
<v-tooltip bottom
v-model="show">
<template v-slot:activator="{ on, attrs }">
<v-textarea
v-model="message"
:append-outer-icon="'mdi-content-copy'"
:readonly="isReadOnly"
auto-grow
filled
counter
clear-icon="mdi-close-circle-outline"
clearable
label="Response message"
type="text"
#click:append-outer="copyToBuffer"
#click:clear="clearMessage"
></v-textarea>
</template>
<span>Tooltip</span>
</v-tooltip>
</v-container>
</template>
<script>
export default {
name: 'VClipboardTextField',
props: {
isReadOnly: Boolean,
message : { type :String, default: "msg"}
},
data() {
return {
show: false,
// messageLocal: 'Response!',
iconIndex: 0,
}
},
methods: {
copyToBuffer() {
console.log("this: ", this)
navigator.clipboard.writeText(this.message);
this.toolTipChange()
setTimeout(() => this.toolTipChange(), 1000)
},
clearMessage() {
this.message = ''
},
toolTipChange() {
if (this.show)
this.show = false
}
}
}
</script>
I will be glad to see an example of the code that will explain how to correctly solve this problem without crutches!
Thanks.
you cannot modify props in components, if the initial value of message is needed as placeholder (meaning the input might not be empty at the begining), you can store the data in message prop to another data variable and use that as v-model to the textarea.
if there is no initial value for message, just use another data variable for textarea and emit an update for message in a watcher.
in code tabs-item.vue
<VClipboardTextField :message.sync="message"></VClipboardTextField>
and child VClipboardTextField.vue
<template>
<v-container>
<v-tooltip bottom
v-model="show">
<template v-slot:activator="{ on, attrs }">
<v-textarea
v-model="message_slug"
:append-outer-icon="'mdi-content-copy'"
:readonly="isReadOnly"
auto-grow
filled
counter
clear-icon="mdi-close-circle-outline"
clearable
label="Response message"
type="text"
#click:append-outer="copyToBuffer"
#click:clear="clearMessage"
></v-textarea>
</template>
<span>Tooltip</span>
</v-tooltip>
</v-container>
</template>
<script>
export default {
name: 'VClipboardTextField',
props: {
isReadOnly: Boolean,
message : { type :String, default: "msg"}
},
data() {
return {
show: false,
// messageLocal: 'Response!',
iconIndex: 0,
message_slug: '',
}
},
watch: {
message_slug(x) {
this.$emit('update:message', x)
}
},
}
</script>
It will bind the value to message_slug and update the message on parent component when it's value changes.
Instead of watching for change every time, you can only emit when there are changes.
In your tabs-item.vue
<VClipboardTextField v-model="message"></VClipboardTextField>
In your VClipboardTextField.vue component, you can receive the v-model input prop as a "VALUE" prop and assign it to a local data property. That way u can manipulate the local property and emit only when in it is changed!
<template>
<v-container>
<v-tooltip bottom v-model="show">
<template v-slot:activator="{ on, attrs }">
<v-textarea
v-model="message"
:append-outer-icon="'mdi-content-copy'"
:readonly="isReadOnly"
v-on="on"
v-bind="attrs"
auto-grow
filled
counter
clear-icon="mdi-close-circle-outline"
clearable
label="Response message"
type="text"
#click:append-outer="copyToBuffer"
#click:clear="clearMessage"
#change="$emit('input', v)"
></v-textarea>
</template>
<span>Tooltip</span>
</v-tooltip>
</v-container>
</template>
<script>
export default {
name: "VClipboardTextField",
props: {
isReadOnly: { type: Boolean },
value: {
type: String,
},
},
data() {
return {
show: false,
message: this.value,
};
},
};
</script>

Two way data binding in Vue: Unable to update the Parent component from the child component

I got the following two components:
Parent:
<template>
<v-container>
<v-row class="text-center">
<v-col cols="12" class="parent">
<p>Ich bin der Parent component</p>
<button #click="changeDetail" :name.sync="name">Change Details</button>
<Child v-bind:name="name"></Child>
</v-col>
</v-row>
</v-container>
</template>
<script>
import Child from "./Child";
export default {
name: "Parent",
data: () => ({
name: "test"
}),
methods: {
changeDetail() {
this.name = "Updated from Parent";
}
},
components: {
Child
}
};
</script>
Child:
<template>
<v-container>
<v-row class="text-center">
<v-col cols="12">
<p>My name is: {{ name}}</p>
<button #click="resetname">Reset the name</button>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
// props: ["name"],
props: {
name: {
type: String,
required: true
}
},
data: () => ({
newname: "Updated from Child"
}),
methods: {
resetname() {
this.$emit("update:name", this.newname);
}
}
};
</script>
As far as I read here: https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier, I should use update and sync to pass props from the child back to the parent. However it does not work. I don´t understand what´s wrong here. What am I missing?
It is usually best to not bind your template to the prop but a computed property instead to ensure the data is accessed and modified externally. It will also simplify your code a bit so that you don't have to trigger updates.
Parent:
<template>
<v-container>
<v-row class="text-center">
<v-col cols="12" class="parent">
<p>Ich bin der Parent component</p>
<button #click="changeDetail">Change Details</button>
<Child v-bind:name.sync="name"></Child>
</v-col>
</v-row>
</v-container>
</template>
<script>
import Child from "./Child";
export default {
name: "Parent",
data: () => ({
name: "test"
}),
methods: {
changeDetail() {
this.name = "Updated from Parent";
}
},
components: {
Child
}
};
</script>
Child:
<template>
<v-container>
<v-row class="text-center">
<v-col cols="12">
<p>My name is: {{ currentName }}</p>
<button #click="resetname">Reset the name</button>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
// props: ["name"],
props: {
name: {
type: String,
required: true
}
},
data: () => ({
//Be careful with fat arrow functions for data
//Because the value of *this* isn't the component,
//but rather the parent scope.
}),
computed: {
currentName: {
get() { return this.name },
set(value) { this.$emit("update:name", value); }
}
},
methods: {
resetname() {
this.currentName = "updated from child";
}
}
};
</script>

I can't display properly v-data-table data: ''Invalid prop: type check failed for prop "items". Expected Array, got Object''

I'm starting a project in which I had to use Vue. I'm actually really new to this, so I'm learning on the go. I do apologize in advance since this question have answered before, however, I didn't really understand the solutions provided, which is why I'm here asking myself.
Well, I was trying to display some data on my Data Table (more specifically, v-data-table from Vuetify). I was able to get the data from the API, but, for some reason it doesn't show me anything. Thanks to Vuex I can see that the mutation worked because on the console on Google Chrome I can see the Array of objects. But as I said, it still does't show me a single thing on the table, it even says 'no data available'. Some errors that I get are things like '[Vue warn]: Invalid prop: type check failed for prop "items". Expected Array, got Object' and 'TypeError: this.items.slice is not a function'.
Here is the code from List.vue
<template>
<v-container id="data-tables" tag="section">
<div class="text-right">
<v-btn class="mx-2" fab dark color="primary" :to="{ name: 'UserCreate' }">
<v-icon dark>mdi-plus</v-icon>
</v-btn>
</div>
<base-material-card
color="indigo"
icon="mdi-vuetify"
inline
class="px-5 py-3"
>
<template v-slot:after-heading>
<div class="display-2 font-weight-light">
Lista de Empleados
</div>
</template>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
class="ml-auto"
label="Search"
hide-details
single-line
style="max-width: 250px;"
/>
<v-divider class="mt-3" />
<v-data-table
:headers="headers"
:items="users"
:search.sync="search"
:sort-by="['name', 'office']"
:sort-desc="[false, true]"
multi-sort
>
<template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" #click="editItem(item)">
mdi-eye
</v-icon>
<v-icon
small
class="mr-2"
#click="editItem(item)"
:to="{ name: 'UserUpdate' }"
>
mdi-pencil
</v-icon>
<v-icon small #click="deleteItem(item)">
mdi-delete
</v-icon>
</template>
</v-data-table>
</base-material-card>
</v-container>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'UsersTable',
data() {
return {
headers: [
{
text: 'Nombre',
value: 'empleado.nombre',
},
{
text: 'Apellido',
value: 'empleado.apellido',
},
{
text: 'Dirección',
value: 'empleado.direccion',
},
{
text: 'Correo Electrónico',
value: 'email',
},
{
text: 'Teléfono',
value: 'empleado.telefono',
},
{
sortable: false,
text: 'Actions',
value: 'actions',
},
],
loader: true,
search: undefined,
}
},
created() {
this.$store.dispatch('users/fetchUsers')
},
computed: {
...mapState(['users']),
},
methods: {},
mounted() {},
}
</script>
And the code from user.js, where the fetchUsers it's coming from.
import auth from '#/api/auth'
export const namespaced = true
export const state = {
users: [],
}
export const mutations = {
SET_USERS(state, users) {
state.users = users
},
}
export const actions = {
fetchUsers({ commit, dispatch }) {
auth
.getAllAccounts()
.then((response) => {
commit('SET_USERS', response.data)
})
.catch((error) => {
const notification = {
type: 'error',
message: 'There was a problem fetching users: ' + error.message,
}
dispatch('notification/add', notification, { root: true })
})
},
}
Thanks in advance.
You are not getting the correct user from vuex, because is namespaced, change to:
computed: {
...mapState('users',['users']),
},
MapState helper dosen't work the same way like the other helpers because the state module isn't registred in the global namespace. So namespacing your module will help or you do it in this way:
computed: {
...mapState({
users: state => state.FilenameOfYourModule.users
})
}

Categories

Resources