I'm using Laravel 5.7 & VueJs 2.5.* ...
I have invoices table, i need to display specific invoice in a new component, so user can see whatever invoice he wants or print that invoice.
I don't know how to do that, i'm just playing around, if you could help me out, i'll be very grateful to you.
<router-link> to the component
<router-link to="/ct-invoice-view" #click="openInvoice(ctInvoice)">
<i class="fas fa-eye fa-lg text-blue"></i>
</router-link>
Displaying Customer information here like this:
<div class="col-sm-4 invoice-col">
<address v-for="ctInvoice in ctInvoices" :key="ctInvoice.id">
<strong>Customer Info</strong><br>
Name: <span>{{ ctInvoice.customer.customer_name }}</span>
Invoice view component data() & method{}
data() {
return {
ctInvoices: {},
customers: null
};
},
methods: {
openInvoice(ctInvoice) {
axios
.get("api/ct-invoice/show/" + this.viewInvoice)
.then(({
data
}) => (this.ctInvoices = data.data));
},
Image for Better Understanding
You need to look at Dynamic Route matching: https://router.vuejs.org/guide/essentials/dynamic-matching.html#reacting-to-params-changes
Then you need to use axios.get in invoice views beforeMount function where this.$route.params.id will hold the invoice ID you want to load if the link is applied like so:
<router-link :to="`/ct-invoice-view/${ctInvoice.id}`">
<i class="fas fa-eye fa-lg text-blue"></i>
</router-link>
Alternatively...
I suggest not navigating away from the list, it can be irritating for users having filtered the list then returning to it to look at more invoices and having to filter again unless the filter options and current results are sticky
There are a number of ways of doing this and they are lengthy to example, Typically I would make proper use of a modal and the invoice view load the data on display but to get you started a basic in page solution to experiment with, then try adapting in a reusable modal component later:
<button #click="showInvoice = ctInvoice.id">
<i class="fas fa-eye fa-lg text-blue"></i>
</button>
data() {
return {
loading: false,
invoice: {},
customers: null
};
},
computed: {
showInvoice: {
get: function() {
return this.invoice.hasOwnProperty('id');
},
set: function(value) {
if(value === false) {
this.invoice = {};
return;
}
// could check a cache first and push the cached item into this.invoice else load it:
this.loading = true;
axios.get("api/ct-invoice/show/" + value).then(response => {
// you could push the invoice into a cache
this.invoice = response.data;
}).cache(error => {
// handle error
}).finally(() => {
this.loading = false;
});
}
}
}
In view-invoice component have a close button with bind #click="$emit('close')"
Check this article for how $emit works: https://v2.vuejs.org/v2/guide/components-custom-events.html
<div v-if="loading" class="loading-overlay"></div>
<view-invoice v-if="showInvoice" :invoice="invoice" #close="showInvoice = false" />
<table v-else>....</table>
Hide the table when displaying the invoice, experiment with using v-show instead of v-if upon loosing table content state.
Inside your invoice view, property called invoice will contain the invoice data.
Check this article for how to use props: https://v2.vuejs.org/v2/guide/components-props.html
Hint: The #close listens to the $emit('close')
Could also make use of when switching between table and invoice view.
https://v2.vuejs.org/v2/guide/transitions.html
#MarcNewton
I did something like this, it's working for me, can u just review it for me:
<router-link> to the Invoice View component
<router-link v-bind:to="{name: 'ctInvoiceView', params: {id: ctInvoice.id}}">
<i class="fas fa-eye fa-lg text-blue"></i>
</router-link>
Getting Data of Specific Invoice ID Like This:
created: function() {
axios
.get("/api/ct-invoice/" + this.$route.params.id)
.then(({
data
}) => {
console.log(data);
this.form = new Form(data);
})
.catch(error => {
console.log(error.response);
});
},
Related
I'm trying to figure out how to get the current changes in a 'contenteditable' and update it in the row that it was changed.
<tbody>
<!-- Loop through the list get the each data -->
<tr v-for="item in filteredList" :key="item">
<td v-for="field in fields" :key="field">
<p contenteditable="true" >{{ item[field] }}</p>
</td>
<button class="btn btn-info btn-lg" #click="UpdateRow(item)">Update</button>
<button class="btn btn-danger btn-lg" #click="DelteRow(item.id)">Delete</button>
</tr>
</tbody>
Then in the script, I want to essentially update the changes in 'UpdateRow':
setup (props) {
const sort = ref(false)
const updatedList = ref([])
const searchQuery = ref('')
// a function to sort the table
const sortTable = (col) => {
sort.value = true
// Use of _.sortBy() method
updatedList.value = sortBy(props.tableData, col)
}
const sortedList = computed(() => {
if (sort.value) {
return updatedList.value
} else {
return props.tableData
}
})
// Filter Search
const filteredList = computed(() => {
return sortedList.value.filter((product) => {
return (
product.recipient.toLowerCase().indexOf(searchQuery.value.toLowerCase()) != -1
)
})
})
const DelteRow = (rowId) => {
console.log(rowId)
fetch(`${import.meta.env.VITE_APP_API_URL}/subscriptions/${rowId}`, {
method: 'DELETE'
})
.then((response) => {
// Error handeling
if (!response.ok) {
throw new Error('Something went wrong')
} else {
// Alert pop-up
alert('Delete successfull')
console.log(response)
}
})
.then((result) => {
// Do something with the response
if (result === 'fail') {
throw new Error(result.message)
}
})
.catch((err) => {
alert(err)
})
}
const UpdateRow = (rowid) => {
fetch(`${import.meta.env.VITE_APP_API_URL}/subscriptions/${rowid.id}`, {
method: 'PUT',
body: JSON.stringify({
id: rowid.id,
date: rowid.date,
recipient: rowid.recipient,
invoice: rowid.invoice,
total_ex: Number(rowid.total_ex),
total_incl: Number(rowid.total_incl),
duration: rowid.duration
// id: 331,
// date: rowid.date,
// recipient: 'new R',
// invoice: 'inv500',
// total_ex: Number(500),
// total_incl: Number(6000),
// duration: 'Monthly'
})
})
}
return { sortedList, sortTable, searchQuery, filteredList, DelteRow, UpdateRow }
}
The commented lines work when I enter them manually:
// id: 331,
// date: rowid.date,
// recipient: 'new R',
// invoice: 'inv500',
// total_ex: Number(500),
// total_incl: Number(6000),
// duration: 'Monthly'
Each cell has content editable, I'm not sure how to update the changed event
The way these run-time js frontend frameworks work could be summarized as "content is the function of data". What I mean is the html renders the data that you send it. If you want the data to be updated when the user changes it, you need to explicitly tell it to do so. Some frameworks (like react) require you to setup 1-way data binding, so you have to explicitly define the data that is displayed in the template, as well as defining the event. Vue has added some syntactic sugar to abstract this through v-model to achieve 2-way binding. v-model works differently based on whichever input type you chose, since they have slightly different behaviour that needs to be handled differently. If you were to use a text input or a textarea with a v-model="item[field]", then your internal model would get updated and it would work. However, there is no v-model for non-input tags like h1 or p, so you need to setup the interaction in a 1-way databinding setup, meaning you have to define the content/value as well as the event to update the model when the html tag content changes.
have a look at this example:
<script setup>
import { ref } from 'vue'
const msg = ref('Hello World!')
</script>
<template>
<h1 contenteditable #input="({target})=>msg=target.innerHTML">{{ msg }}</h1>
<h2 contenteditable>{{ msg }}</h2>
<input v-model="msg">
</template>
If you change the h2 content, the model is not updated because vue is not tracking the changes. If you change through input or h1, the changes are tracked, which will also re-render the h2 and update its content.
TL;DR;
use this:
<p
contenteditable="true"
#input="({target})=>item[field]=target.innerHTML"
>{{ item[field] }}</p>
layoutchange() {
this.layout = !this.layout;
if (this.layout === true) {
this.perPage = this.layout ? 8 : 12;
this.listProducts();
} else {
this.perPage = !this.layout ? 12 : 8;
this.gridProducts();
}
},
<a class="list-icon" v-bind:class="{ active: layout == true }" v-on:click="layoutchange"></a>
<a class="grid-icon" v-bind:class="{ active: layout == false }" v-on:click="layoutchange"></a>
<ul v-if="layout == true">
//code for product display
<b-pagination v-model="currentPage" :total-rows="rows" :per-page="perPage"></b-pagination>
</ul>
<ul v-if="layout == false">
//code for product display
<b-pagination v-model="currentPage" :total-rows="rows" :per-page="perPage"></b-pagination>
</ul
Basically i am trying to add the api call for the each page,(i have a api which need to call) for suppose if i click on pagination page no 1, i need to fire api, and same page 2 need to call api. Now i have a doubt, Now i am using the b-pagination (bootstrap-vue) are there any event to call for each page? like next previous or any event based. so with same name, i can call api using that.
I am using fr grid and list view, For both i have pagination
Reference document https://bootstrap-vue.org/docs/components/pagination
If there is no event provided by b-pagination that you can use, in that specific usecase, you can just watch the currentPage property.
https://v2.vuejs.org/v2/guide/computed.html#Watchers
export default {
data() {
return {
currentPage: null,
}
},
watch: {
currentPage(newVal) {
if(newVal) {
// Call the api
// Random api endpoint as example
const endpoint = 'https://jsonplaceholder.typicode.com/todos/'
fetch(endpoint + newVal).then((res) => {
console.log(res)
// update corresponding data
})
}
}
},
mounted() {
// Initialise currentPage to your route or 1 by default as example
this.currentPage = 1
}
}
I am trying to update view_count column on every #click. but couldn't figure out the right way of it.
First made the controller --resources and fetch datas via api.
controller:
public function index()
{
$articles = Article::all();
return response()->json([
"articles" => $articles
], 200);
}
public function show($id)
{
$article = Article::whereId($id)->first();
return response()->json([
"article" => $article
], 200);
}
also set the update function too.
public function update(Request $request, $id)
{
$view = Article::find($id);
$view->update($request->where("view_count"));
return response()->json(["message" => "view_count updated"]);
}
I set the api routes:
Route::get('/articles', 'ArticlesController#index');
Route::get('/articles/{id}', 'ArticlesController#show');
Route::get('/articles/{id}', 'ArticlesController#update');
And finally in Vue.js
<p class="button">
<i #click.prevent="count" class="fas fa-chevron-right"></i>
</p>
data(){
return {
view: 0,
};
},
methods: {
count: function(){
axios.post("/api/articles" + this.item.id).then(response => {
this.view++;
})
window.location.href = "pages/" + this.item.id
}
}
it's counting but not update the col. also, when I refresh the page of course it will start to count from 0... it's not really efficient way to it. what is the best and right way to do it?
Thank you.
Not: By the way I am fetching and iterating api in the parent component:
<div class="listWrap" :key="item.id" v-for="item in filterArticles">
<list :item="item" />
</div>
your workflow to update views is wrong.
first, we should change our uri method of the update method to GET like below :
Route::get('/articles/update/{id}', 'ArticlesController#update');
then, our update method within ArticlesController to increment view_count value:
public function update(int $id)
{
// i have changed the $view by $article
$article = Article::find($id);
$article->view_count++;
$article->save();
return response()->json(["message" => "view_count updated", 201]);
}
and within our Vue component, we should update the URI of the update method and the HTTP method name because we should use the same HTTP verb in both client and server sides.
<p class="button">
<i #click.prevent="count" class="fas fa-chevron-right"></i>
</p>
<script>
export default {
// as you are using parent/child relationship betwen components, you should use props.
props: { item: Object },
data(){
return {
view: 0,
};
},
methods: {
count: function(){
axios.get(`/api/articles/update/${this.item.id}`).then(response => {
this.view++;
})
window.location.href = "pages/" + this.item.id;
}
}
}
</script>
I have posts in my application which a user can either like or dislike: there are two different buttons and each of them shows how many likes/dislikes a post gets. Currently, I am struggling to resolve a bug of this feature. If I quickly click on these buttons, one after another, and then stop, these buttons continue to show this clicking behaviour and there are more requests appear for a short while in my console.
Here is what I have in my component:
<template>
...
<div>
<a style="cursor: default"
#click.pointer="dislikePost(getPost.id)">
<i class="icon-minus"/>{{ getPost.dislikes }} Dislike
</a>
<a style="cursor: default"
#click="likePost(getPost.id)">
<i class="icon-plus" />{{ getPost.likes }} Like
</a>
</div>
...
</template>
And here is what I have in my store:
...
actions: {
likePost({ commit }, postId) {
http
.post(`${config.api}/posts/${postId}/like`)
.then(({ data }) => {
commit('load_post', data);
});
},
dislikePost({ commit }, postId) {
http
.post(`${config.api}/posts/${postId}/dislike`)
.then(({ data }) => {
commit('load_post', data);
});
},
},
mutations: {
load_posts(state, posts) {
state.posts = posts;
},
},
...
I build following component:
var Modal = Vue.component('modal', {
template: `
<div id="modal" class="modal">
<div class="modal-content">
<p>{{ link }}</p>
</div>
</div>
`,
props: [
'link'
],
});
And I would like to change the link data dynamically after I sent successfully an axios post.
My vue instance
new Vue({
el: '#form',
components: {
'modal': Modal
},
data: {
userId: '',
title: '',
body: '',
snippetLink: '',
},
methods: {
publish (e) {
var self = this;
axios.post('/snippets', {
title: this.title,
body: this.content,
})
.then(function (response) {
console.log("success");
self.link = response.data.hash; // Here I tried to add the reponse content to the vue component's p
})
.catch(function (error) {
console.log(error);
})
},
My Html Markup:
<modal link=""></modal>
...
<button type="button"
v-bind:class="{ 'modal-trigger': !isActiveModal }"
#click="publish">Publish
<i class="material-icons right">send</i>
</button>
So I am sending an axios post to my server successfully and get the data, I would like to open a modal window and put the data in a p tag, so far the modal pops up after my post but I am not sure my it does not change the content of the p tag.
As per my understanding , Snippetlink property can be used to hold data from server.
self.Snippetlink = response.data.hash;
and Pass Snippetlink to link attribute of the snippet-model
<snippet-modal :link="Snippetlink"></snippet-modal>
rupesh_padhye's answer is correct. This is just a further explanation.
First of all, to store the response data to a Vue component, you need to define a key in data for that purpose first. So to make this line work: self.link = response.data.hash;, you need to add link as a key for the Vue component's data:
data: {
userId: '',
title: '',
body: '',
snippetLink: '',
link: ''
},
If you mean snippetLink, change the line to self.snippetLink = response.data.hash;
Secondly, to pass the data as a prop to a child component, you have to specify the prop name, as well as the data key being passed. For instance, to pass your component's link as a prop with the same name to the snippet-modal component, you need: <snippet-modal :link="link"></snippet-modal>