Pass vuejs ID to a data-url - javascript

I have v-for="product in products"
How i can pass product.id to a data-url?
I try:
<li class="list-group-item" v-for="product in products">
#{{ product.label }}
<input
type="checkbox" :id="product.id" :value="product.id" v-model="product.checked"
class="toggle-product pull-right"
data-url="{{env('APP_URL')}}/accounts/{{ $account->id }} /albums/{{ $album->id }}/images/{{ $image }}/settings/#{{ product.id }}/toggle">
or
data-url="{{route('image.settings.product.toggle',[$account,$album,$image,'product.id'])}}
not work.
$(function () {
// enable/disable products on sidebar
$(".toggle-product").on('click', function () {
var checked = $(this).is(':checked');
var $status = $(this).parents(".list-group-item");
$status.addClass('list-group-item-warning');
$.post($(this).data('url'), {
'enabled': checked ? 1 : 0,
'_token': "{{csrf_token()}}"
}, function (response) {
$status.removeClass('list-group-item-warning');
}.bind(this))
});
});
new Vue({
el: '#products',
data: {
products: [
#foreach($validProducts as $p)
{ label: '{{$p->getTranslatedName()}}', id: '{{$p->id}}', #if(!isset($restrictedProductIds[$p->id])) checked: true #endif},
#endforeach
]
},
computed: {
checked: function (){
return this.products.filter(function (l) {
return l.checked
}).map(function (l){
return l})
}
},
});
Can you help me, thanks.
I know there are two side, server side and client side, but mybe have possible to pass that id ?

Is
:data-url="'{{env('APP_URL')}}/accounts/{{ $account->id }} /albums/{{ $album->id }}
/images/{{ $image }}/settings/' + product.id + '/toggle'">
working ?

Related

Vuetify Table - rendering column value based on a condition

I have a vuetify table and one of the columns is url. I need to add logic to either display URL or URL Group name. I can tell that based on the property of my rules array. If my rules[i].urlGroup != '' then I know
I have tried to add
<template v-slot:item.url="{ index, item }">
{{ displayURLInfo(index) }}
</template>
displayURLInfo(index) {
if (this.rules[index].urlGroup != '') {
// console.log('this.rules[index].urlGroup', this.rules[index].urlGroup) // here printed perfectly on console.
return this.rules[index].urlGroup
} else {
return this.url
}
}
I was inside the first if condition, it consoles log perfectly, but it didn't render to the UI.
My rules array structure look like this. It's only has 5 properties
What did I do wrong?
You have to do below correction in the displayURLInfo() method :
Use urlGroup instead of userGroup.
Instead of return this.url it should be return this.rules[index].url
Working Demo :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
headers: [
{ text: "Priority", value: 'priority' },
{ text: "Name", value: 'name' },
{ text: "URL", value: 'url' },
],
rules: [{
priority: 1,
name: "Alpha",
url: 'https://example.com',
urlGroup: ''
}, {
priority: 2,
name: "Beta",
url: 'https://example.com',
urlGroup: 'userGroup2'
}],
}
},
methods: {
displayURLInfo(index) {
if (this.rules[index].urlGroup != '') {
return this.rules[index].urlGroup
} else {
return this.rules[index].url
}
}
}
})
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.4/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.4/dist/vuetify.min.css"/>
<div id="app">
<v-app id="inspire">
<v-data-table
:headers="headers"
:items="rules"
>
<template v-slot:item.url="{ index, item }">
{{ displayURLInfo(index) }}
</template>
</v-data-table>
</v-app>
</div>

Vue.js : How to iterate with v-for into a dynamic array

I want to display multiples html tables of tools (1 table = 1 tool's categorie / 1 tr = 1 tool).
data() {
return {
cats: '',
tools: [],
};
},
methods: {
getToolCats() {
var rez = getToolCats();
rez.then(data => this.receiveCats(data) )
},
receiveCats(_cats){
this.cats = _cats;
_cats.forEach(cat => {
getToolsByCat(cat.href).then(data => this.tools[cat.href] = data);
});
console.log(this.tools);
},
},
mounted() {
this.getToolCats();
},
cats (ie categories) is an array populated with an API call. Then for each cat, an API Call give me a tool list of that cat, that I place into the tools array (this.tools[cat.href] = data).
Here is the display code :
<div v-for="cat in cats" :key="cat.href" class="tbox col-xs-12 col-sm-6">
....
<table class="table table-hover">
<tr v-for="tool in tools[cat.href]" :key="tool.href">
<td>...</td>
</tr>
</table>
....
</div>
If i'm using a single var to store lthe tool list, all is OK. But while I don't know how many cats I'm going to have, I can't create a car for each category.
I think the problem could be there :
Using an array in v-for with a key not defined at mounted state :
v-for="tool in tools[cat.href]
I'll appreciate any help !
Vue can't detect dynamic property addition in this.tools[cat.href] = data, but it would detect the change with this.$set or Vue.set in this.$set(this.tools, cat.href, data):
new Vue({
el: '#app',
data() {
return {
cats: [],
tools: {}, // <-- make this an object (not an array)
};
},
mounted() {
this.getToolCats();
},
methods: {
getToolCats() {
// setTimeout to simulate delayed API calls...
setTimeout(() => {
this.cats = [
{ href: 'https://google.com' },
{ href: 'https://microsoft.com' },
{ href: 'https://apple.com' },
{ href: 'https://amazon.com' },
];
this.cats.forEach((cat, i) => {
setTimeout(() => {
const data = { href: cat.href };
this.$set(this.tools, cat.href, data); // <-- use this.$set for dynamic property addition
}, i * 1000);
})
}, 1000);
}
}
})
<script src="https://unpkg.com/vue#2.5.17"></script>
<div id="app">
<div v-if="!cats || cats.length == 0">Loading...</div>
<div v-for="cat in cats" :key="cat.href" v-else>
<table>
<tr v-for="tool in tools[cat.href]" :key="tool.href">
<td>cat.href: {{cat.href}}</td>
</tr>
</table>
</div>
<pre>tools: {{tools}}</pre>
</div>

Vue.js doesn't display calendars

I am using vue-full-calendar to display calendars on my webpage.
Here i iterate through my projects in my database and get the dates so i can fill the calendar events later (using props in my-message component).
<template>
<div class="row" >
<div #click="clickedDiv" v-for="val in allProjects" :key="val.projectName" class="col-md-5 margin-bottom-30" >
<my-message id ="calendar" v-bind:title="val.projectName" v-bind:dates= val.answeredDates v-bind:dateTitle = val.projectId v-bind:link="'/#/editstartup' + '?id=' + val.projectId + '&title='+ val.projectName "></my-message>
</div>
</div>
</template>
The my-message component is inside main class :*
Vue.component('my-message', {
props: ['dates', 'title', 'dateTitle', 'link'],
data: function () {
var customEvents = [];
this.dates.forEach((event) => {
customEvents.push({
title: this.dateTitle,
start: event,
color: 'rgb(0, 95, 206)',
textColor: 'rgb(0, 95, 206)',
clickedDate: '',
projectId: '',
answeredDateConfirmURI: 'http://...'
})
})
return {
isVisible: true,
events: customEvents,
config: {
defaultView: 'month',
},
}
},
methods: {
getAnsweredDateConfirm: function (id) {
axios.get('http://localhost:8081/webapi/projects/answers/' + id)
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error);
});
},
eventRender: function (event, element) {
// console.log(event)
},
eventClick: (event) => {
this.selected = event;
console.log(event);
this.projectId = event.title;
this.clickedDate = event.start._i;
this.$router.push("mystartups?date=" + this.clickedDate + "&project_id=" + this.projectId + "&project_title=" + this.title);
},
dayClick(date, event, view) {
// console.log(date, event, view)
console.log(date.format())
console.log(event);
},
refreshEvents() {
this.$refs.calendar.$emit('refetch-events')
},
eventSelected(event) {
this.selected = event;
console.log(event);
}
},
template: `
<article class="tile is-child notification" v-show="isVisible">
<!--
<p class="title">{{ title }}</p>-->
<a class="title" v-bind:href=link style="text-decoration:none; font-family: 'Open Sans', sans-serif;"> {{title}}
<i class="far fa-edit"></i>
</a>
<button class="delete" aria-label="delete" #click="isVisible = false"></button>
<full-calendar ref="calendar" :config="config" :events="events" #event-selected="eventSelected"></full-calendar>
<p class="subtitle">{{ body }}</p>
</article>
`
});
This code works when i use it locally, but when i build it, the calendats don't show and i get this error :
> vue.esm.js:1741 ReferenceError: body is not defined
> at o.eval (eval at ko (vue.esm.js:10680), <anonymous>:3:642)
> at o.t._render (vue.esm.js:4544)
> at o.<anonymous> (vue.esm.js:2788)
> at je.get (vue.esm.js:3142)
> at new je (vue.esm.js:3131)
> at vue.esm.js:2795
> at o.hn.$mount (vue.esm.js:8540)
> at o.hn.$mount (vue.esm.js:10939)
> at init (vue.esm.js:4137)
> at vue.esm.js:5608

Model and Computed Property interaction in Vue.js

Using vue.js I am trying to build a simple task manager.
When a user clicks the "complete" checkbox I want two things to happen:
If the "Show all tasks" is unchecked, hide the task.
Send an ajax request to the server to mark the task as complete/open.
The impotent parts are shown below:
<div id="tasks-app">
<input type="checkbox" id="checkbox" v-model="show_all">
<label for="checkbox">Show all tasks</label><br>
<table class="table">
<tr><th v-for="column in table_columns" v-text="column"></th><tr>
<tr v-for="row in visibleTasks" :class="{danger: !row.daily_task.complete && row.daily_task.delayed, success: row.daily_task.complete}">
<td v-text="row.task.name"></td>
<td v-text="row.task.deadline"></td>
<td v-text="row.daily_task.status"></td>
<td v-text="row.daily_task.task_user"></td>
<td>
<input type="checkbox" v-on:change="updateStatus(row)" v-model="row.daily_task.complete" >Complete</input>
</td>
<td><input v-model="row.daily_task.delay_reason"></input></td>
</table>
</div>
And the VUE.js code:
app = new Vue({
el: '#tasks-app',
data: {
table_columns: ['Task','Deadline','Status','User','Actions','Reason'],
tasks: [],
filter_string: '',
show_all: false
},
computed: {
visibleTasks() {
show_all = this.show_all
if(show_all){
search_filter = this.tasks
}else{
search_filter = _.filter(this.tasks,function(task){
return !task.daily_task.complete;
})
}
return search_filter
}
},
methods: {
updateStatus(row){
var id = row.daily_task.id
var complete = row.daily_task.complete
if(complete){
axios.get('set_task_complete/' + id)
}else{
axios.get('set_task_open/' + id)
}
}
}
})
If the show all checkbox is checked, this works as expected. The data changes and then the updateStatus function is called.
If however the show all checkbox is unchecked, the visibleTasks will trigger and the logic for the updateStatus will fail, as the row will be hidden and the ID that is send to the server will be off by one. If the row hides before updateStatusis called the wrong row is passed to the updateStatus function.
I could solve this by adding a filter update at the end of updateStatus function but that does not seems to utilize the Vue.js library. Could someone help me what components of Vue you would use to solve this problem?
You can simplify a lot if you separate the logic:
Loop over every tasks (completed or not) to display them (without considering show_all)
Update tasks with v-on:click="updateStatus(row)"
On each tr, add v-show="show_all || !row.status.complete"
Your problem is use both change event handle and model. Both actually, trigger at same time when you click on the checkbox.
<input type="checkbox" v-on:change="updateStatus(row)" v-model="row.daily_task.complete" >Complete</input>
You should edit your code use only v-on:change="updateStatus(row)". After updateStatus complete ajax calls toggle row.daily_task.complete to trigger visibleTasks to update your view.
updateStatus(row){
var id = row.daily_task.id
var complete = !row.daily_task.complete
var p;
if(complete){
p = axios.get('set_task_complete/' + id)
}else{
p = axios.get('set_task_open/' + id)
}
p.then(() => row.daily_task.complete = complete)
}
I've refactored your code a bit and it seems to work fine:
you shouldn't use v-on:change, instead, use <input type="checkbox" v-on:click="updateStatus(row)" v-bind:checked="row.daily_task.complete">
Don't update row.daily_task.complete straight away, update it only when the asynchronous axios is complete.
const fakeUpdateComplete = (id) => {
return new Promise((resolve, reject) => { resolve(true); });
};
const fakeUpdateIncomplete = (id) => {
return new Promise((resolve, reject) => { resolve(true); });
};
const app = new Vue({
el: '#tasks-app',
data: {
history: [],
table_columns: ['Task','Deadline','Status','User','Actions','Reason'],
tasks: [
{
task: {
name: 'A',
deadline: '2017-01-01',
},
daily_task: {
id: 1,
status: '',
task_user: '',
complete: true,
delayed: false,
delay_reason: ''
}
},
{
task: {
name: 'B',
deadline: '2017-01-02',
},
daily_task: {
id: 2,
status: '',
task_user: '',
complete: false,
delayed: false,
delay_reason: ''
}
},
{
task: {
name: 'C',
deadline: '2017-01-03',
},
daily_task: {
id: 3,
status: '',
task_user: '',
complete: false,
delayed: false,
delay_reason: ''
}
},
{
task: {
name: 'D',
deadline: '2017-01-03',
},
daily_task: {
id: 4,
status: '',
task_user: '',
complete: true,
delayed: false,
delay_reason: ''
}
},
{
task: {
name: 'E',
deadline: '2017-01-03',
},
daily_task: {
id: 5,
status: '',
task_user: '',
complete: false,
delayed: false,
delay_reason: ''
}
}
],
filter_string: '',
show_all: true
},
computed: {
visibleTasks() {
const show_all = this.show_all
let search_filter;
if(show_all){
search_filter = this.tasks
}else{
console.log(this.tasks);
search_filter = this.tasks.filter(task => {
return !task.daily_task.complete
});
}
return search_filter
}
},
methods: {
updateStatus(row){
const id = row.daily_task.id;
const complete = !row.daily_task.complete;
if (complete) {
fakeUpdateComplete(id).then(() => {
this.history.push(`Task ${row.task.name} with id ${row.daily_task.id} is marked as complete`);
row.daily_task.complete = !row.daily_task.complete;
});
} else {
fakeUpdateIncomplete(id).then(() => {
this.history.push(`Task ${row.task.name} with id ${row.daily_task.id} is marked as incomplete`);
row.daily_task.complete = !row.daily_task.complete;
});
}
/*
if(complete){
axios.get('set_task_complete/' + id)
}else{
axios.get('set_task_open/' + id)
}
*/
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="tasks-app">
<input type="checkbox" id="checkbox" v-model="show_all">
<label for="checkbox">Show all tasks</label><br>
<table class="table">
<tr><th v-for="column in table_columns" v-text="column"></th><tr>
<tr
v-for="row in visibleTasks"
:class="{danger: !row.daily_task.complete && row.daily_task.delayed, success: row.daily_task.complete}"
>
<td>{{row.task.name}}</td>
<td>{{row.task.deadline}}</td>
<td>{{row.daily_task.status}}</td>
<td>{{row.daily_task.task_user}}</td>
<td>
<input type="checkbox" v-on:click="updateStatus(row)" v-bind:checked="row.daily_task.complete">
<label for="complete">Complete</label>
</td>
<td><input v-model="row.daily_task.delay_reason" /></td>
</tr>
</table>
<div>
<div><b>Data:</b></div>
<div>{{this.tasks}}</div>
</div>
<div>
<div><b>History:</b></div>
<div v-for="item in history">
{{item}}
</div>
</div>
</div>

How to calculate grand total from a list

I'm making an POS application on mobile phone and I have a question. How do I calculate the grand total from the list of item from the database?
Here's my code
order-detail.dxview
POSApp.OrderDetail = function (params, viewInfo) {
"use strict";
var id = params.id,
order = new POSApp.OrderViewModel(),
isReady = $.Deferred(),
// Item List
shouldReload = false,
dataSourceObservable = ko.observable(),
dataSource;
function handleViewShown() {
POSApp.db.Orders.byKey(id).done(function (data) {
order.fromJS(data);
isReady.resolve();
});
// Item List
if (!dataSourceObservable()) {
dataSourceObservable(dataSource);
dataSource.load().always(function () {
isReady.resolve();
});
}
else if (shouldReload) {
refreshList();
}
// Item List
}
// Item List
function handleViewDisposing() {
POSApp.db.OrderDetails.off("modified", handleOrderDetailsModification);
}
function handleOrderDetailsModification() {
shouldReload = true;
}
// Item List
dataSource = new DevExpress.data.DataSource({
store: POSApp.db.OrderDetails,
map: function (item) {
return new POSApp.OrderDetailViewModel(item);
},
expand: ["Item"],
sort: { field: "OrderDetailId", desc: false },
filter: ["OrderId", parseInt(id)]
});
POSApp.db.OrderDetails.on("modified", handleOrderDetailsModification);
var viewModel = {
grandTotal: ko.observable(total),
handleDelete: function () {
DevExpress.ui.dialog.confirm("Are you sure you want to delete this item?", "Delete item").then(function (result) {
if (result)
handleConfirmDelete();
});
},
handleConfirmDelete: function () {
POSApp.db.Orders.remove(id).done(function () {
if (viewInfo.canBack) {
POSApp.app.navigate("Orders", { target: "back" });
}
else {
POSApp.app.navigate("Blank", { target: "current" });
}
});
},
//Item List
refreshList: function () {
shouldReload = false;
dataSource.pageIndex(0);
dataSource.load();
},
//Item List
// Return
id: id,
order: order,
viewShown: handleViewShown,
isReady: isReady.promise(),
// Item List
dataSource: dataSourceObservable,
viewDisposing: handleViewDisposing,
// Item List
// Return
};
return viewModel;
};
order-detail.js
<div data-options="dxView : { name: 'OrderDetail', title: 'Order' } " >
<div data-bind="dxCommand: { onExecute: '#OrderEdit/{id}', direction: 'none', id: 'edit', title: 'Edit', icon: 'edit' }"></div>
<div data-bind="dxCommand: { onExecute: handleDelete, id: 'delete', title: 'Delete', icon: 'remove' }"></div>
<div data-options="dxContent : { targetPlaceholder: 'content' } " class="dx-detail-view dx-content-background" data-bind="dxDeferRendering: { showLoadIndicator: true, staggerItemSelector: 'dx-fieldset-header,.dx-field', animation: 'detail-item-rendered', renderWhen: isReady }" >
<div data-bind="dxScrollView: { }">
<div class="dx-fieldset">
<div class="dx-fieldset-header" data-bind="text: order.PhoneNumber"></div>
<div class="dx-field">
<div class="dx-field-label">Order id</div>
<div class="dx-field-value-static" data-bind="text: order.OrderId"></div>
</div>
<div class="dx-field">
<div class="dx-field-label">Phone number</div>
<div class="dx-field-value-static" data-bind="text: order.PhoneNumber"></div>
</div>
<div class="dx-field">
<div class="button-info" data-bind="dxButton: { text: 'Add Item', onClick: '#AddItem/{id}', icon: 'add', type: 'success' }"></div>
<!-- Item List -->
<div data-bind="dxList: { dataSource: dataSource, pullRefreshEnabled: true }">
<div data-bind="dxAction: '#OrderDetailDetails/{OrderDetailId}'" data-options="dxTemplate : { name: 'item' } ">
<!--<div class="list-item" data-bind="text: Item().ItemName"></div>
<div class="list-item" style="float:right;" data-bind="text: Amount"></div>-->
<div class="item-name" data-bind="text: Item().ItemName"></div>
<div class="item-amount" data-bind="text: Amount"></div>
<div class="clear-both"></div>
</div>
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">Grand total</div>
<!--<div class="dx-field-value-static" data-bind="text: order.GrandTotal"></div>-->
<div class="dx-field-value-static" data-bind="text: grandTotal"></div>
</div>
</div>
<div data-options="dxContentPlaceholder : { name: 'view-footer', animation: 'none' } " ></div>
</div>
</div>
</div>
I've tried by using get element by class name and it still doesn't work.
I've tried by using get element by class name and it still doesn't work.
You shouldn't try to get the data from your view; it's already in your viewmodel!
This documentation page tells me you can get an array of items from your DataSource instance by calling the items method.
From your data source's map function and your text: Amount data-bind, I figured each item probably has an Amount property which holds an integer.
grandTotal can be a computed that adds these values together whenever dataSourceObservable changes:
grandTotal: ko.computed(function() {
var total = 0;
var currentDS = dataSourceObservable();
if (currentDS) {
var currentItems = currentDS.items();
total = currentItems.reduce(function(sum, item) {
return sum + item.Amount;
}, total);
}
return total;
});
Here, the source is a Knockout observable array and the dxList data source. A value of grand totals is stored in the 'total' variable which is a computed observable depending on 'source'. So, once 'source' is changed, 'total' is re-calculated as well.
var grandTotal = ko.observable(0);
dataSource = new DevExpress.data.DataSource({
// ...
onChanged: function () {
grandTotal(0);
var items = dataSource.items();
for (var i = 0; i < items.length; i++) {
grandTotal(grandTotal() + items[i].Amount());
}
}
});
return {
// ...
grandTotal: grandTotal
};

Categories