Change autocomplete selected item programmatically in Vue - javascript

I am not advance in Vue, suppose we have :
<v-autocomplete v-model="defaultUser"
:hint="`User: ${defaultUser.username}`"
:items="users"
:item-text="item =>`${item.firstName} - ${item.lastName}`"
item-value="userId"
label="Related user"
persistent-hint
return-object
dense
single-line outlined
:filter="userFilter"></v-autocomplete>
and data :
data: () => ({
users: [],
defaultUser: { userId: 0, username: 'select user', firstName: '', lastName: '' },
.....
by below snipped code nothing happened in UI, in the other words I want to change the selected item by code but nothing happened:
axios.get('api/user/relatedusers')
.then(response => {
if (response.status === 200 && response.data.id !== 0) {
this.defaultUser.userId = response.data.relatedId;//Here is nothing happened
}
}).catch(err => {
});
why this does not work if every variables are under Vue control, I mean bind and observable.

It seems that you should find a user by id and then assign him to this.defaultUser:
this.defaultUser = this.users.find(x => x.userId === response.data.relatedId)

Related

Error in vue js, problems with v-show or v-if

Good morning people, I'm going to comment on my mistake. This is a users screen. Where I have a record and an edit. Register is with id 0 and edit i have id types. My idea was, when you enter the registrar, you will see the change password fields (so far everything is perfect.). The issue is on the edit screen, my idea is until I select the role, the password fields did not appear. Once you choose the role, you can change your password.
Then I have the select, of roles. That the idea would be to bring me the role. And it doesn't. It takes me the text-field, but in the v-select it doesn't bring me the value. Attached images below.
v-select error does not bring me the data.
The roles are shown to me, but I would like it to be put in the select, as in the text fields. is there any way?
And there once the role is selected. The idea would be that the password fields appear. If there is no role selected, it should not appear.
<v-form ref="form" lazy-validation>
<v-select
label="Rol"
v-model="form.rolId"
:items="roles"
item-value="value"
item-text="text"
:rules="[(v) => !!v || 'Este campo es requiredo']"
required
></v-select>
<v-text-field
v-model="form.password"
v-show="showPassword"
:rules="[(v) => !!v || 'Este campo es requiredo']"
:type="'password'"
label="Contraseña"
required
></v-text-field>
<v-text-field
v-model="form.confirmPassword"
v-show="showPassword"
:rules="[(v) => !!v || 'Este campo es requiredo']"
:type="'password'"
label="Confirmar contraseña"
required
></v-text-field>
<v-row class="text-center">
<v-col cols="12" lg="3">
<v-btn
block
class="mr-4 blue white--text rounded-lg"
#click="submit"
submit
>Guardar</v-btn
>
</v-col>
</v-row>
</v-form>
data() {
return {
form: {},
showPassword: true,
roles: [
{ value: "Agente", text: "Agente" },
{ value: "Administrador", text: "Administrador" },
],
};
},
getOne(id) {
if (id != "0") {
this.showPassword = false;
this.usuarioServices
.getOne(id)
.then((data) => {
this.form = data;
this.form.password = "Encripted";
this.form.confirmPassword = "Encripted";
})
.catch((error) => {
console.log(error.response.status);
});
}
},
submit() {
if (this.form.password != this.form.confirmPassword) {
this.showError("Las contraseñas no coinciden.");
return;
}
this.$refs.form.validate();
if (this.$refs.form.validate(true)) {
Swal.fire({
title: "Espere unos momentos ...",
showConfirmButton: false,
});
if (this.form.id == "0") {
this.usuarioServices
.registrar(this.form)
.then((data) => {
Swal.close();
if (data == "") {
this.$router.push({ path: "/usuarios/listar" });
this.showSuccess("Entro al sistema correctamente.");
}
})
.catch((error) => {
Swal.close();
if (error.response.status == 401) {
this.showError("El mail o password es invalido.");
} else {
this.showError(error.response.data);
}
console.log(error.response.status);
});
} else {
this.usuarioServices
.editar(this.form)
.then((data) => {
Swal.close();
if (data == "") {
this.$router.push({ path: "/usuarios/listar" });
this.showSuccess("Entro al sistema correctamente.");
}
})
.catch((error) => {
Swal.close();
if (error.response.status == 401) {
this.showError("El mail o password es invalido.");
} else {
this.showError(error.response.data);
}
console.log(error.response.status);
});
}
}
},
As mentioned, you are facing two challenges in your code :
1. How to hide/show password text fields based on the role select by the user ?
Instead of v-show="showPassword", You can simply do v-show="form.rolId" in both the password text fields. As form.rolld is containing the selected role value. Hence, if it will be falsy (empty/null/undefined) .. text fields will not be shown.
2. How to display/show existing role (which coming from database) in the select box when user is on edit page ?
I can see, API returning roles as an array ['Agente']. Hence, while binding the value to v-model in the <v-select>. It should be access via zero index apiResponse.roles[0].

How to load Bootstrap-vue "b-table" row data dynamically using "_showDetails"?

I have bootstrap-vue "b-table" used in my Vue page. Each row has an "view-details" button, that shows additional information about the selected row. I was looking for examples that can send request to backend when user clicks for view-details, that expands the row and shows details retrieved from backend. The "_showDetails" option from bootstrap-vue table seems limited as the examples all use the data that was already loaded along with the main tableland using this way would overload the page as my data for each row is too big.
Are there any examples or even other libs that support such functionality?
You can do this with bootstrap-vue without any problems.
Create a method that gets called when you click your "view details" button, this method will call your backend and insert the data onto your item. Once the data has been retrieved you set _showDetails to true on the item, which will open the details.
You could also open it immediately and show a loading message while the data is retrieved, that's up to you.
new Vue({
el: '#app',
created() {
// Get initial data
fetch('https://reqres.in/api/users')
.then(response => response.json())
.then(json =>
/* Map and use only some of the data for the example */
this.items = json.data
.map(user => {
return {
id: user.id,
first_name: user.first_name,
last_name: user.last_name
}
}))
},
data() {
return {
items: [],
fields: ['id', 'first_name', 'last_name', {
key: 'actions',
label: ''
}]
}
},
methods: {
toggleDetails(item) {
if (item._showDetails) { // if details are open, close them
item._showDetails = false
} else if (item.details) { // if details already exists, show the details
this.$set(item, '_showDetails', true)
} else {
fetch(`https://reqres.in/api/users/${item.id}`)
.then(response => response.json())
.then(json => {
const user = json.data;
item.details = {
email: user.email,
avatar: user.avatar
}
this.$set(item, '_showDetails', true)
})
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.5.0/dist/bootstrap-vue.min.js"></script>
<link href="https://unpkg.com/bootstrap-vue#2.5.0/dist/bootstrap-vue.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap#4.3.1/dist/css/bootstrap.min.css" rel="stylesheet" />
<div id="app">
<b-container>
<b-table :items="items" :fields="fields">
<template v-slot:cell(actions)="{ item }">
<b-btn #click="toggleDetails(item)">
Show details
</b-btn>
</template>
<template v-slot:row-details="{ item : { details: { email, avatar }}}">
<b-card>
<b-img :src="avatar" fluid></b-img>
{{ email }}
</b-card>
</template>
</b-table>
</b-container>
</div>

Pull refresh does not update the list

My card does not get the latest data from firestore even with pull refresh implemented in quasar
<q-pull-to-refresh #refresh="refresh">
<q-card
class="q-ma-md"
bordered
v-for="announcement in announcements"
:key="announcement.key"
>
<q-card-section>
<div class="text-h6">{{announcement.TITLE}}</div>
<div class="text-subtitle2">{{announcement.CONTENT}}</div>
</q-card-section>
</q-card>
</q-pull-to-refresh>
here is my script and methods
data() {
return {
announcements: [],
};
},
//methods
retrieveAnnouncements() {
firebase
.firestore()
.collection("announcement")
.get()
.then(snapShot => {
snapShot.forEach(element => {
const { TITLE, CONTENT, AUTHOR } = element.data();
//add retrieved data in announcement
this.announcements.push({
key: element.id,
TITLE,
CONTENT,
AUTHOR
});
});
});
},
here is my refresh method that tries to update the card of the current page
refresh(done) {
setTimeout(() => {
(this.announcements = null), done();
}, 1000);
},
created() {
this.retrieveAnnouncements();
}
Does your refresh function call retrieveAnnouncements?
It looks like it's only called on created

How to have parent data be updated by child component with multiple values

Code below.
I think I'm missing a crucial piece here. I've been through the docs and watched the entire vue2 step by step. Everything is making sense so far but I'm stuck on what seems to be a core piece. Any help would be appreciated. If this is totally wrong, please let me know, I'm not married to any of this stuff.
Desired functionality: There is an order Vue instance and it has line items.
On order.mounted() we hit an api endpoint for the order's data, including possible existing line items. If there are existing line items, we set that order data (this.lineitems = request.body.lineitems or similar). This part works fine and I can get the order total since the orders' line items are up to date at this point.
Each line item is an editable form with a quantity and a product . If I change the quantity or product of any line item, I want the child line-item component to notify the parent component that it changed, then the parent will update its own lineitems data array with the new value, and preform a POST request with all current line item data so the server side can calculate the new line item totals (many specials, discounts, etc). This will return a full replacement array for the order's line item data, which in turn would passed down to the line items to re-render.
Problems:
The line-items components "update..." methods are feeling obviously wrong, but my biggest issue is understanding how to get the parent to update its own line items data array with the new data. for instance
​
lineitems = [
{id: 1000, quantity: 3, product: 555, total: 30.00},
{id: 1001, quantity: 2, product: 777, total: 10.00}
]
If the second line item is changed to quantity 1, how do I get the parent's lineitems data to change to this? My main problem is that I don't know how the parent is suppose to know which of its own lineitems data array need to be modified, and how to grab the data from the changed child. I assume it came in via an event, via emit, but do I now need to pass around the primary key everywhere so I can do loops and compare? What if its a new line item and there is no primary key yet?
Mentioned above, I'm using the existing line item's DB primary key as the v-for key. What if I need a "new lineitem" that appends a blank lineitem below the existing ones, or if its a new order with no primary keys. How is this normally handled.
Is there a best practice to use for props instead of my "initial..." style? I assume just using $emit directly on the v-on, but I'm not sure how to get the relevant information to get passed that way.
This seems like the exact task that VueJS is suited for and I just feel like I keep chasing my tail in the wrong direction. Thanks for the help!
LineItem
Vue.component('line-item', {
props: ["initialQuantity", "initialProduct", "total"],
data () {
return {
// There are more but limiting for example
quantity: initialQuantity,
product: initialProduct,
productOptions = [
{ id: 333, text: "Product A"},
{ id: 555, text: "Product B"},
{ id: 777, text: "Product C"},
]
}
},
updateQuantity(event) {
item = {
quantity: event.target.value,
product: this.product
}
this.$emit('update-item', item)
},
updateProduct(event) {
item = {
quantity: this.quantity,
product: event.target.value
}
this.$emit('update-item', item)
}
template: `
<input :value="quantity" type="number" #input="updateQuantity">
<select :value="product" #input="updateProduct">
<option v-for="option in productOptions" v-bind:value="option.id"> {{ option.text }} </option>
</select>
Line Item Price: {{ total }}
<hr />
`
})
Order/App
var order = new Vue({
el: '#app',
data: {
orderPK: orderPK,
lineitems: []
},
mounted() {
this.fetchLineItems()
},
computed: {
total() {
// This should sum the line items, like (li.total for li in this.lineitems)
return 0.0
},
methods: {
updateOrder(item) {
// First, somehow update this.lineitems with the passed in item, then
fetch(`domain.com/orders/${this.orderPK}/calculate`, this.lineitems)
.then(resp => resp.json())
.then(data => {
this.lineitems = data.lineitems;
})
},
fetchLineItems() {
fetch(`domain.com/api/orders/${this.orderPK}`)
.then(resp => resp.json())
.then(data => {
this.lineitems = data.lineitems;
})
},
},
template: `
<div>
<h2 id="total">Order total: {{ total }}</h2>
<line-item v-for="item in lineitems"
#update-item="updateOrder"
:key="item.id"
:quantity="item.quantity"
:product="item.product"
:total="item.total"
></line-item>
</div>
`
})
Here's a list of problems in your attempt that would prevent it from displaying anything at all i.e.
quantity: initialQuantity, - surely you meant quantity: this.initialQuantity, ... etc for all the other such data
missing } for computed total
your line-item template is invalid - you have multiple "root" elements
And then there's some minor issues:
you want the #change handler for the select, not #input, if your code ran, you'd see the difference,
Similarly you want #change for input otherwise you'll be making fetch requests to change the items every keystroke, probably not what you'd want
So, despite all that, I've produced some working code that does all you need - mainly for my own "learning" though, to be fair :p
// ******** some dummy data and functions to emulate fetches
const products = [
{ id: 333, text: "Product A", unitPrice: 10},
{ id: 555, text: "Product B", unitPrice: 11},
{ id: 777, text: "Product C", unitPrice: 12},
];
let dummy = [
{id: 1, quantity:2, product: 333, total: 20},
{id: 2, quantity:3, product: 777, total: 36},
];
const getLineItems = () => new Promise(resolve => setTimeout(resolve, 1000, JSON.stringify({lineitems: dummy})));
const update = items => {
return new Promise(resolve => setTimeout(() => {
dummy = JSON.parse(items);
dummy.forEach(item =>
item.total = parseFloat(
(
item.quantity *
(products.find(p => p.id === item.product) || {unitPrice: 0}).unitPrice *
(item.quantity > 4 ? 0.9 : 1.0)
).toFixed(2)
)
);
let res = JSON.stringify({lineitems: dummy});
resolve(res);
}, 50));
}
//********* lineItem component
Vue.component('line-item', {
props: ["value"],
data () {
return {
productOptions: [
{ id: 333, text: "Product A"},
{ id: 555, text: "Product B"},
{ id: 777, text: "Product C"},
]
}
},
methods: {
doupdate() {
this.$emit('update-item', this.value.product);
}
},
template: `
<p>
<input v-model="value.quantity" type="number" #change="doupdate()"/>
<select v-model="value.product" #change="doupdate()">
<option v-for="option in productOptions" v-bind:value="option.id"> {{ option.text }} </option>
</select>
Line Item Price: {{ '$' + value.total.toFixed(2) }}
</p>
`
})
//********* Order/App
const orderPK = '';
var order = new Vue({
el: '#app',
data: {
orderPK: orderPK,
lineitems: []
},
mounted() {
// initial load
this.fetchLineItems();
},
computed: {
carttotal() {
return this.lineitems.reduce((a, {total}) => a + total, 0)
}
},
methods: {
updateOrder(productCode) {
// only call update if the updated item has a product code
if (productCode) {
// real code would be
// fetch(`domain.com/orders/${this.orderPK}/calculate`, this.lineitems).then(resp => resp.json())
// dummy code is
update(JSON.stringify(this.lineitems)).then(data => JSON.parse(data))
.then(data => this.lineitems = data.lineitems);
}
},
fetchLineItems() {
// real code would be
//fetch(`domain.com/api/orders/${this.orderPK}`).then(resp => resp.json())
// dummy code is
getLineItems().then(data => JSON.parse(data))
.then(data => this.lineitems = data.lineitems);
},
addLine() {
this.lineitems.push({
id: Math.max([this.lineitems.map(({id}) => id)]) + 1,
quantity:0,
product: 0,
total: 0
});
}
},
template: `
<div>
<h2 id="total">Order: {{lineitems.length}} items, total: {{'$'+carttotal.toFixed(2)}}</h2>
<line-item v-for="(item, index) in lineitems"
:key="item.id"
v-model="lineitems[index]"
#update-item="updateOrder"
/>
<button #click="addLine()">
Add item
</button>
</div>
`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
</div>
note: there may be some inefficient code in there, please don't judge too harshly, I've only been using vuejs for a week

Javascript load more images of the array onclick. Using Firebase

I'm trying to make my website a little bit faster, and for that, I'm trying to make a button that on each click presents more images. For example: a user can see 5 images, and if the user wants to see 5 more he can, by clicking on the button.
So for now only got this, and i really think it's not the right way.
HTML ->
<ion-card *ngFor="let pic of photoList">
<h1>{{pic?.username}}</h1>
<h2>{{pic?.name}}</h2>
<img src={{pic?.picture}}>
</ion-card>
<button ion-button (click)="load()">Load More Images</button>
Js ->
load() {
firebase.database().ref('HomeList').limitToLast(5).on('value', snapshot => {
this.photoList = [];
snapshot.forEach(snap => {
this.photoList.push({
id: snap.key,
name: snap.val().name,
username: snap.val().username,
picture: snap.val().picture,
email: snap.val().email,
uid: snap.val().uid,
rating: snap.val().rating
});
console.log(this.photoList);
return false
});
return this.photoList.reverse();
});
}
so you need a pagination try to use .startAfter(number) and .limit(number); assuming this.current = 0; sets in constructor();
load() {
firebase.database().ref('HomeList').startAfter(this.current).limit(5).on('value', snapshot => {
this.photoList = [];
snapshot.forEach(snap => {
this.photoList.push({
id: snap.key,
name: snap.val().name,
username: snap.val().username,
picture: snap.val().picture,
email: snap.val().email,
uid: snap.val().uid,
rating: snap.val().rating
});
console.log(this.photoList);
this.current = this.current + photoList.length;
return false
});
return this.photoList.reverse();
});
}

Categories