How to redirect to a page with an object in vue - vue.js

I have a couple of vue pages, one page is a list of users, the second is a list of permissions and the third is a vue component
that displays the list of users and another vue component that shows the details of a user.
What I'm trying to do is, if I'm on the permissions page and I would like to go to a specific user's details I would like to redirect
with that panel open.
I am able to redirect the the users page by doing window.location.href = '/admin/users'; but I'm not sure how I'm going to get
the user panel open.
I thought that if I could do $emit then that could pass the user object over to the users main page, but that won't work if I do
a window.location.href = '/admin/users';
Here is my code. This has 2 components displays a list of users and their details
<template>
<div class="row">
<div class="col-md-6">
<users :emit="emit" :users="users"></users>
</div>
<div class="col-md-6">
<detail v-if="userDetails" :emit="emit" :user="manageUser"></detail>
</div>
</div>
</template>
<script>
export default {
props: ['emit', 'users'],
data() {
return {
userDetails: false,
manageUser: null
}
},
mounted() {
this.emit.$on('user', payload => {
this.manageUser = payload.user;
this.userDetails = true;
});
}
}
</script>
This is my users lists
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title text-success" style="cursor: pointer;" #click="createUser"> New User</h3>
</div>
<div class="card-body">
<table class="table table-sm table-striped">
<thead>
<tr>
<th>Name</th>
<th style="width:120px;"></th>
</tr>
</thead>
<tbody>
<tr v-for="user in users">
<td>{{user.name}}</td>
<td style="text-align: right;">
<button class="btn btn-sm btn-outline-info" #click="detail(user)">Details</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
props: ['emit', 'users'],
data() {
return {
}
},
methods: {
detail(user) {
this.emit.$emit('user', { user: user });
}
}
}
</script>
and this is my permissions pages
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title">{{permission.name}}</strong></h3>
</div>
<div class="card-body p-0">
<table class="table table-sm table-striped">
<thead>
<tr>
<th>User Name</th>
<th style="width:120px;"></th>
</tr>
</thead>
<tbody>
<tr v-for="user in users">
<td>{{user.name}}</td>
<td style="text-align: right;">
<button class="btn btn-sm btn-outline-info" #click="detail(user)">Details</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
props: ['emit'],
data() {
return {
users: []
}
},
mounted() {
this.allUsers();
},
methods: {
allUsers() {
this.loading = true;
axios.get('/users').then(response => {
this.users = response.data.users;
});
},
detail(user) {
window.location.href = '/admin/users';
}
}
}
</script>

Related

Props passed to child component do not render well

I am attempting to pass user id's fetched from an API trough props from parent (App) to child (Modal). The problem is that when I pass the props down to the modal they don't render as they should in the div with the modal-body class, in fact all of them display an id of 1.
App.vue:
<template>
<div class="container mt-3">
<table class="table">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Username</th>
</tr>
</thead>
<tbody v-for="user in users" :key="user.id">
<tr>
<th scope="row">{{ user.id }}</th>
<td>{{ user.name }}</td>
<td>{{ user.username }}</td>
<td>
<Modal :id="user.id" />
</td>
</tr>
</tbody>
</table>
</div>
<pre>{{ user }}</pre>
</template>
<script>
import axios from "axios";
import Modal from "#/components/Modal.vue";
export default {
name: "App",
components: {
Modal,
},
data() {
return {
users: null,
};
},
methods: {
async load_users() {
try {
const { data } = await axios.get(
"https://jsonplaceholder.typicode.com/users"
);
this.users = data;
} catch (error) {
console.log("error");
}
},
},
mounted() {
this.load_users();
},
};
</script>
Modal.vue:
<template>
<!-- Button trigger modal -->
<button
type="button"
class="btn btn-danger"
data-bs-toggle="modal"
data-bs-target="#exampleModal"
>
Delete
</button>
<!-- Modal -->
<div
class="modal fade"
id="exampleModal"
tabindex="-1"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
Are you sure you want to delete user: {{ id }}
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
Close
</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Modal",
props: ["id"],
};
</script>
The page preview is the following:
It does not matter on which Delete button I click the id is always 1.
But when I inspect the props in the Vue Devtools they are different, for the second for example it appears 2 as it should:
Any help is appreciated.
Thank you.
You are rendering 10 modals each with the same id - exampleModal, so it's always the first one which is opened. This is why you are experiencing the behaviour you describe.
However - the real problem is with your structure.
Why are you rendering 10 modals? Why not render one and pass in the respective props?
Something like this:
<template>
<Modal v-if="modal.isActive" :id="modal.userId" />
<tbody v-for="user in users" :key="user.id">
<tr>
<th scope="row">{{ user.id }}</th>
<td>{{ user.name }}</td>
<td>{{ user.username }}</td>
<td>
<button
type="button"
class="btn btn-danger"
#click="onClick(user.id)"
>
Delete
</button>
</td>
</tr>
</tbody>
</template>
<script>
import Modal from '#/components/Modal.vue'
export default {
name: 'App',
components: {
Modal,
},
data() {
return {
users: null,
modal: {
isActive: false,
userId: null,
},
}
},
methods: {
onClick(userId) {
Object.assign(this.modal, { isActive: true, userId })
},
},
}
</script>

Dynamically set v-model

I'm trying to make my v-model naming dynamic, but when I run npm run watch I get this error
You are binding v-model directly to a v-for iteration alias. This will not be able to modify the v-for source array because writing to the alias is like modifying a function local variable. Consider using an array of objects and use v-model on an object property instead
and it points to my v-model.
Here is my code
<template>
<div class="content-header">
<div class="container">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-12">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr v-for="product in products">
<td>{{ product }}</td>
<td>
<input type="text" class="form-control" v-model="product" :name="product"> month
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default{
props: [],
data(){
return {
products: []
}
},
computed:{
},
methods: {
getProducts(){
axios.get(`/api/product/all`).then(response => {
this.products = response.data.products;
});
}
},
mounted() {
}
}
</script>

The specified value cannot be parsed, or is out of range in vue

I'm trying to create a form that will update a product's pricing when adding the unit amount of the product and
a price of the product, but when I type into one of my textboxes I end up getting this error
The specified value "unit_product_1" cannot be parsed, or is out of range
I don't know what that means or how to solve it.
Here is my code
<template>
<div class="content-header">
<div class="container">
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-12">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Unit</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr v-for="product in products">
<td>{{ product['name'] }}</td>
<td>
<input type="text" class="form-control" v-model="product['unit']">
</td>
<td>
<input type="text" class="form-control" v-model="product['price']">
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default{
props: [],
data(){
return {
products: [],
unit_product_1: null,
price_product_1: null,
}
},
computed:{
},
methods: {
getProducts(){
axios.get(`/api/product/all`).then(response => {
this.products = response.data.products;
});
}
},
mounted() {
this. getProducts();
}
}
</script>
unit_product_1: null,
price_product_1: null
You don't use them anywhere.
If you want to mutate your incoming products, you should make a new POST request and send the data back to the server. In this scenario you must know how the server waits to receive the data and send them back as requested.

Hiding Rows After a Certain Limit of Rows Has Been Updated

I'm new to coding with a very basic understanding to Javascript. I created a table that will push an update to my tables upon clicking a button. However, i do want to limit the number of rows within my table. say for example upon pushing the sixth data to the table, i do want the first data to be removed from the row.
I tried searching everywhere with no luck. Maybe it's because my basic understand to javascript is pretty basic. Im a newbie haha. I am using vue.js for this code.
HTML
<form class="form-inline">
<div class="form-group mx-sm-3 mb-2">
<input
type="number"
v-model="newElement.buy"
class="form-control">
</div>
<button
v-on:click="buyStock"
type="button"
class="btn btn-success mb-2">BUY
</button>
</form>
<section class="stock_tables">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">{{codeBuy}}</th>
<th><a v-html="negative"></a> Buy</th>
</tr>
</thead>
<tbody>
<tr v-for="u in stocks">
<th></th>
<td>{{u.buy}}</td>
</tr>
</tbody>
</table>
</section>
Script
<script>
new Vue({
el: '#app',
data: {
codeBuy: "12345",
stocks: [],
newElement: {buy:"",sell:""}
},
methods: {
buyStock: function(){
this.stocks.push({buy:this.newElement.buy});
this.newElement = {buy:""};
}
}
});
</script>
So basically everytime i enter the amount of the stocks and press buy it will update.
You have some options to modify the buyStock method:
this.stocks.shift()
this.$delete(this.stocks, 0)
this.stocks = this.stocks.slice(1);
this.stocks = this.stocks.slice(-5);
this.stocks = this.stocks.splice(0, 1);
or, without the if use:
this.stocks = [...this.stocks, {
buy: this.newElement.buy
}].slice(-5);
Demo below:
new Vue({
el: '#app',
data: {
codeBuy: "12345",
stocks: [],
newElement: {
buy: "",
sell: ""
},
negative: ''
},
methods: {
buyStock: function() {
this.stocks.push({
buy: this.newElement.buy
});
if (this.stocks.length > 5) {
// this.$delete(this.stocks, 0);
// this.stocks = this.stocks.slice(1);
// this.stocks = this.stocks.slice(-5);
// this.stocks = this.stocks.splice(0, 1);
this.stocks.shift();
}
//this.stocks = [...this.stocks, {
// buy: this.newElement.buy
//}].slice(-5);
this.newElement = {
buy: ""
};
}
}
});
<script src="https://unpkg.com/vue"></script>
<div id="app">
<form class="form-inline">
<div class="form-group mx-sm-3 mb-2">
<input type="number" v-model="newElement.buy" class="form-control">
</div>
<button v-on:click="buyStock" type="button" class="btn btn-success mb-2">BUY</button>
</form>
<section class="stock_tables">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">{{codeBuy}}</th>
<th><a v-html="negative"></a> Buy</th>
</tr>
</thead>
<tbody>
<tr v-for="u in stocks">
<th></th>
<td>{{u.buy}}</td>
</tr>
</tbody>
</table>
</section>
</div>

How to call method from another component VUE

I have one component
<template>
<div class="section">
<a v-if='type == "events"' class="button is-primary" #click="showForm()">
<i class="fa fa-calendar"></i> &nbsp<span class="card-stats-key"> Add Event</span>
</a>
<a v-if='type == "places"' class="button is-primary" #click="showForm()">
<i class="fa fa-location-arrow"></i> &nbsp<span class="card-stats-key"> Add Place</span>
</a>
<table class="table" v-if="!add">
<thead>
<tr>
<th>
<abbr title="Position"># Client Number</abbr>
</th>
<th>Title</th>
<th>
<abbr title="Played">Status</abbr>
</th>
<th>
<abbr title="Played">View</abbr>
</th>
</tr>
</thead>
<tbody>
<tr v-for="event in events">
<th>{{event.client_number}}</th>
<td v-if='type == "events" '>{{event.title}}</td>
<td v-if='type == "places" '>{{event.name}}</td>
<td>
<p class="is-danger">Waiting</p>
</td>
<td> View </td>
</tr>
</tbody>
</table>
<add v-if="add" v-on:hideAdd="hideFrom()"></add>
</div>
</template>
<script>
import Add from '../forms/AddPlace.vue'
export default {
name: 'Tabbox',
data() {
return {
events: [],
add: ''
}
},
props: ['type'],
created() {
let jwt = localStorage.getItem('id_token')
var ref = wilddog.sync().ref('users').child(jwt).child(this.type);
ref.once("value")
.then((snapshot) => {
this.events = snapshot.val();
}).catch((err) => {
console.error(err);
})
},
methods: {
showForm(add) {
if (this.add == true)
this.add = false;
else {
this.add = true;
}
},
hideFrom() {
this.add = false
console.log('This add is false');
}
},
components: {
Add
}
}
</script>
and another component
<template>
<div class="field is-horizontal">
<div class="field-label">
<!-- Left empty for spacing -->
</div>
<div class="field-body">
<div class="field">
<div class="control">
<button class="button is-primary" v-bind:class="{ 'is-loading': loading } " #click="addPlace()">
Add place
</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
add: false
}
},
methods: {
addPlace() {
this.$emit('hideAdd', this.add)
},
},
}
</script>
How i can calling method from showForm() from first first component in second one! I'm trying to that with $emit function, it doesn't work. And trying with $broadcast, same. How i can use event there?
Add a ref attribute to the child component in the parent component's template like this:
<add v-if="add" v-on:hideAdd="hideFrom()" ref="add-component"></add>
Now your parent component will have access to the child component's VueComponent instance and methods, which you can access like this:
methods: {
showForm() {
this.$refs['add-component'].addPlace();
}
}
Here's documentation on refs.