I'm new to Vue and I am trying to make a component were a user has to divide 10 points over 4 statements. It looks like this:
If a user clicks on a circle, it will fill and add +1 to a counter. The problem is the counters are all linked. My question is thus: How do I make a separate / independent counter for each statement?
The data in qa contains 4 statements in json with a view properties I later need to bind to the input that are passed to the component via a prop.
Here is my code:
<template>
<div class="counter">
<span>Je mag nog {{ total.counter }} punten verdelen</span>
<div class="row" v-for="n in qa[0]">
<div class="col s4">
<h5>{{ n['title'] }}</h5>
</div>
<div class="col s7">
<span v-for="n in fullCounter"><img v-on:click="subtractCounter" class="circle-image" src="/images/solid-circle.png"></span>
<span v-for="n in emptyCounter"><img v-on:click="addCounter" class="circle-image" src="/images/empty-circle.png"></span>
</div>
<div class="col s1">
<input v-model="fullCounter" type="number" min=0 max=10>
</div>
</div>
</div>
</template>
<script>
let total = {
counter: 10
}
export default {
props: ['qa'],
data() {
return {
total,
fullCounter: 0,
emptyCounter: 10,
}
},
mounted() {
},
methods: {
addCounter: function () {
if(this.fullCounter <= 10 && total.counter > 0) {
this.total.counter -= 1,
this.fullCounter += 1,
this.emptyCounter -= 1
}
},
subtractCounter: function () {
if(this.emptyCounter >= 0) {
this.total.counter += 1,
this.fullCounter -= 1,
this.emptyCounter += 1
}
}
}
}
Can anyone point me in the right direction?
Edit:
here is a GA example:
{
"id": 600226,
"customer_id": 1,
"survey_id": 600006,
"user_id": 1,
"section": 1,
"pos": 2,
"title": null
"Company 1",
"required": 0,
"subtitle": null
}
Related
So, I'm trying to fetch my laravel api and already run php artisan serve. After that the error came out and I'm looking to find solution here but I have no idea what's going on.
The error shown in the console:
Uncaught (in promise) TypeError: Cannot convert undefined or null to object
The API:
{
"message": "List trash and category order by time",
"data": [
{
"id": 1,
"trash_id": 1,
"category_id": 2,
"created_at": "2022-01-01T12:41:43.000000Z",
"updated_at": "2022-01-01T12:41:43.000000Z",
"garbage": {
"id": 1,
"name": "Buku",
"weight": 1,
"created_at": "2022-01-01T12:41:19.000000Z",
"updated_at": "2022-01-01T12:41:19.000000Z"
},
"categories": {
"id": 2,
"name": "Kertas",
"price": 1800
}
}]
}
Index.vue script:
<script>
import axios from "axios";
import { onMounted, ref } from "vue";
export default {
setup() {
// reactive state
let all_trash = ref([]);
onMounted(() => {
// get data from api endpoint
axios
.get("http://127.0.0.1:8000/api/trash")
.then((result) => {
all_trash.value = result.data;
})
.catch((err) => {
console.log(err.response);
});
});
return {
all_trash,
};
},
};
</script>
Index.vue HTML:
<div v-for="(trash, index) in all_trash.data" :key="index">
<div class="card rounded shadow mb-3">
<div class="card-body">
<div class="mx-3">
<h5 class="mb-4">{{ trash.garbage.name }}</h5>
<p>
{{ trash.categories.name }}
<span class="float-end">
<button class="btn" style="color: red">Hapus</button>
</span>
</p>
</div>
</div>
</div>
</div>
Just add a condition that not to render the loop until data is not set. The data from API end point is not available on the initial dom rendered and there is not data in all_trash.data hence the error. Just add a v-if on top of the div and hopefully it should work.
<div v-if=" all_trash.data">
<div v-for="(trash, index) in all_trash.data" :key="index">
<div class="card rounded shadow mb-3">
<div class="card-body">
<div class="mx-3">
<h5 class="mb-4">{{ trash.garbage.name }}</h5>
<p>
{{ trash.categories.name }}
<span class="float-end">
<button class="btn" style="color: red">Hapus</button>
</span>
</p>
</div>
</div>
</div>
</div>
i had implement the click event for the chart and it can filter the data in the table. But now i face the problem of the data does not return all the data in the table when click outside the chart are.How can make it return all the data when click outside the area chart? Thank you. Any help will be appreciated.
<script>
import VueApexCharts from "vue-apexcharts";
import faker from "faker";
export default {
components: {
VueApexCharts,
},
data() {
return {
Lists: [],
selectedData:{},
search1:'',
clicked:'',
clicked1:'',
isLoading1:true,
currentPage: 1,
series: [300, 200, 49, 100,290, 228, 119, 55],
chartOptions: {
colors: ['#7961F9', '#FF9F43', '#196EB0', '#2EAB56','#df87f2','#057FF2', '#14DA6E','#FF5500'],
legend: {
fontSize: "14px",
position: "top",
},
dataLabels: {
enabled: true,
minAngleToShowLabel: 0,
distributed: false,
style: {
colors: ['#111'],
},
background: {
enabled: true,
foreColor: '#fff',
borderWidth: 0,
}
},
chart: {
width: 500,
type: 'pie',
events: {
legendClick: (chartContext, seriesIndex,w, config) => {
this.clicked = w.config.labels[seriesIndex];
console.log(this.clicked);
console.log(seriesIndex);
},
dataPointSelection: (event,chartContext,config) => {
this.clicked1 = config.w.config.labels[config.dataPointIndex];
console.log(this.clicked1);
},
},
},
labels: ['Private', 'Local','Dental', 'Government','Cyber Security', 'Health', 'Foreign','Medical'],
responsive: [
{
breakpoint: 480,
options: {
legend: {
position: "bottom",
fontSize: "12px",
},
},
},
],
},
}
},
created() {
this.getData();
this.getData1();
},
computed:{
filterLists(){
let list = this.Lists;
if(this.search1 !=''){
list = list.filter((tr)=>{
return tr.agency.toUpperCase().includes(this.search1.toUpperCase())
});
}
if (this.clicked !=''&& this.clicked){
list = list.filter((tr)=>{
return tr.projectCategory.toUpperCase().includes(this.clicked.toUpperCase())
});
}
if (this.clicked1 !=''&& this.clicked1){
list = list.filter((tr)=>{
return tr.projectCategory.toUpperCase().includes(this.clicked1.toUpperCase())
});
}
return list;
},
},
methods: {
next(page) {},
getData1() {
this.isLoading1 = true;
for (let i = 0; i < this.randInt(8, 4); i++) {
let index = Math.floor(Math.random() * 2);
let projectCategory = this.rotate([
'Private', 'Local','Dental', 'Government','Cyber Security', 'Health', 'Foreign','Medical'
]);
this.Lists.push({
projectCategory:projectCategory,
project_name: faker.company.catchPhrase(),
agency: faker.company.companyName(),
logo: faker.image.abstract(),
});
}
this.maxPage = 2;
this.isLoading1 = false;
},
next1(page) {
if (page == -2) {
this.currentPage = 1;
} else if (page == -3) {
this.currentPage = this.maxPage;
} else {
if (
this.currentPage + page < 1 ||
this.currentPage + page > this.maxPage
) {
return;
}
this.currentPage += page;
}
this.showLoader("#card-list");
this.Lists = [];
this.isLoading1 = true;
setTimeout(() => {
this.closeLoader("#card-list");
this.getData1();
}, 1500);
},
},
};
</script>
<style>
#card > header{
padding: 1.5rem 2rem;
background-color: #2E3839;
}
#card{
--tw-bg-opacity: 1;
background-color: rgba(249, 250, 251, var(--tw-bg-opacity));
}
.con-img.vs-avatar--con-img img {
object-fit: cover !important;
}
.apexcharts-toolbar {
position:absolute;
margin-right:12px;
}
vs-button.btn:hover{
background-color: rgba(255,255,255,0);
cursor: pointer;
}
</style>
<template>
<div class="mb-base">
<div class="vx-row mb-base">
<div class="vx-col 2/3 w-full mb-base">
<vs-card
id="card"
class="vs-con-loading__container h-full"
>
<template slot="header">
<div class="flex">
<div>
<img
src=""
alt="Info"
class="h-12 inline-block mr-4 object-scale-down"
/>
</div>
<div class="flex flex-col justify-center w-full text-start">
<h3 class="text-white">Source of Fund</h3>
<span class="text-sm text-white">Based on Total Billed (Yearly)</span>
</div>
<div>
</div>
</div>
</template>
<div class="flex flex-wrap mt-2">
<div class="lg:w-1/3 w-full">
<vue-apex-charts
type="donut"
:options="chartOptions"
:series="series"
width="100%"
class="items-center justify-center flex mt-16 content-center"
/>
</div>
<div class="lg:w-2/3 w-full lg:pl-6 pl-0 mt-6">
<div class="flex justify-end items-end">
<vx-input-group class="mb-base lg:w-1/2 w-full">
<template slot="append">
<div class="append-text btn-addon">
<vs-button color="#A9A9A9"><i class="fas fa-search"></i></vs-button>
</div>
</template>
<vs-input
v-model="search1"
placeholder="Project Code or name"
/>
</vx-input-group>
</div>
<div id="card-list">
<vs-list v-if="!isLoading1">
<vs-list-item
v-for="(tr, index) in filtertLists"
:key="index"
class="hover:shadow cursor-pointer text-base mb-4"
>
<template slot="title">
<div
class="flex flex-col ml-2 cursor-pointer"
>
<div class="font-bold">{{ tr.project_name }}</div>
<div>{{ tr.agency }}</div>
</div>
</template>
<template slot="avatar">
<vs-avatar :src="tr.logo"></vs-avatar>
</template>
{{ tr.projectCategory }}
</vs-list-item>
<div v-if="!filterLists.length" class="flex">
<div class="items-center justify-center text-lg font-bold">No record...</div>
</div>
</vs-list>
<div v-else class="flex">
<div class="items-center justify-center">Fetching data...</div>
</div>
</div>
<div class="flex justify-end gap-4">
<div class="flex items-center justify-center text-sm">
Page {{ currentPage }} of
{{ maxPage }}
</div>
<div>
<vs-button
type="filled"
color=" rgba(243, 244, 246)"
class="w-10 mr-2 rounded-md bg-gray-400 text-black btn hover:text-black"
#click="next1(-1)"
>
<i class="fas fa-chevron-left"></i>
</vs-button>
<vs-button
type="filled"
color=" rgba(243, 244, 246)"
class="w-10 mr-2 rounded-md bg-gray-400 text-black btn"
#click="next1(1)"
>
<i class="fas fa-chevron-right"></i>
</vs-button>
</div>
</div>
</div>
</div>
</vs-card>
</div>
</div>
</div>
</template>
I am working with laravel vue pie chart. is there any way to filter the data in the table when click the element of pie chart. For example, when clicked the section pie chart, the table will be filter and display the the data in the table under that section..Any help will be appreciated
It is not possible to point directly to a solution as you have given so little detail. I will still try to explain the logic. For this process, you will get an input from the screen working with Vue.js and you will manipulate a data displayed on Vue.js.
So first; you need to know which part of your pie chart clicked on. I assume the pie chart you are using on your project have some events which triggered when you interact with charts. You will listen that event and catch the value of clicked item.
Now you have the value of clicked item and you need to filter your results by that.
To accomplish that you can use Vue.js Computed Properties and Watchers :https://v2.vuejs.org/v2/guide/computed.html
Lets say you have your data on your Vue.js application:
data () {
return {
clickedItem: null,
itemsOnTable: [ ... ]
}
}
You have all your table content in itemsOnTable and selected item's data in clickedItem
You can use computed to filter your data:
data () {
return {
clickedItem: null,
itemsOnTable: [ ... ]
}
},
computed: {
// filter itemsOnTable if clickedItem have any value
filteredItems: function () {
if(this.clickedItem==null) return this.itemsOnTable;
return this.itemsOnTable.filter(item => item.column = this.clickedItem);
}
}
Now in your Vue.js component you can directly use filteredItems for your table elements v-for
<table>
<tr v-for="items in filteredItems">
<td>{{ item.column }}</td>
<!-- other columns -->
</tr>
</table>
This examples explains basics of interactions and computed properties and aims to help you to understand basics.
Response Json from back-end
{
"INCOME": {
"TOURIST": [{
"vehicleNumber": "TN 47 RG 4567",
"details": [{
"id": 01,
"amount": 100
},{
"id": 02,
"amount": 200
}]
},
{
"vehicleNumber": "TN 47 RG 9876",
"details": [{
"id": 03,
"amount": 300
},{
"id": 04,
"amount": 400
}]
}
]
}
}
is assigned to this.tesTourist = this.objectValue.INCOME.TOURIST;
component.html
<form [formGroup]="accountsForm" autocomplete="off">
<div class="row">
<div *ngFor="let obj of tesTourist">
<div *ngFor="let item of obj.details">
<label class="text-left"> {{obj.vehicleNumber}} :</label>
<input type="text" formControlname="incomeTourist" value="{{item.amount}}">
</div>
</div>
</div>
</form>
component.ts
this.accountsForm = this.fb.group({
incomeTourist:[],
});
Let as take obj.details.length is 2. It displays 2 input box in page with the value amount binded(item.amount). While changing the amount in any one of the input field that could not bind the amount in incomeTourist. It constantly shows null as below.
Result of <pre><code>{{accountsForm?.value | json}}</code></pre>
{
"incomeTourist": null
}
You form structure should be like below.
this.accountsForm = this.fb.group({
testTourist: this.fb.array([]),
});
After that create 2 method to create testTourist and incomeTourist form array.
createTestTourist() {
return this.formBuilder.group({
details: this.fb.array([])
});
}
createDetails() {
return this.formBuilder.group({
amount: [],
vehicleNumber: []
});
}
And after that loop over the testTourist object in ngOnInt method.
this.testTourists.forEach(testTourist => {
const fb = this.createTestTourist();
testTourist.details.forEach((detail, i) => {
const cfb = this.createDetails();
cfb.get('amount').setValue(detail.amount);
cfb.get('vehicleNumber').setValue(detail.vehicleNumber);
(fb.controls.details as FormArray).push(cfb);
});
(this.accountsForm.get('testTourist') as FormArray).push(fb);
});
after this in html you should write below code.
<form [formGroup]="accountsForm" autocomplete="off">
<div class="row">
<div *ngFor="let obj of testTourists; let i= index" formArrayName="testTourist">
<ng-container [formGroupName]="i">
<div *ngFor="let item of obj.details; let j= index" formArrayName="details">
<ng-container [formGroupName]="j">
<label class="text-left"> {{item.vehicleNumber}} :</label>
<input type="text" formControlName="amount">
</ng-container>
</div>
</ng-container>
</div>
</div>
</form>
After this if you do any changes it will reflect in form object. You can console the form object.
console.log(this.accountsForm.value)
you will get expect same object structure as you are providing. And you will get updated values.
I'm currently working on a Vue.js component in my Laravel application, and I'm stuck on pagination. My pagination logic works like a charm, but the Bootstrap styles are not applied. This is my Vue.js component:
<template>
...
<div style="margin: auto; text-align: center;">
<pagination v-if="pagination.total > pagination.per_page"
:pagination="pagination" :callback="loadData"
:options="paginationOptions">
</pagination>
</div>
...
</template>
<script>
import pagination from 'vue-bootstrap-pagination';
export default {
components: {
pagination
},
pagination: {
total: 0,
per_page: 15, // required
current_page: 1, // required
last_page: 0, // required
from: 1,
to: 12
},
paginationOptions: {
offset: 4,
previousText: 'Previous',
nextText: 'Next',
alwaysShowPrevNext: true
},
...
</script>
As I said, my pagination works like a charm, but I have no styles to it. This is how it currently looks like:
Desired output would be something like this:
Based on our conversation above, it seems that the package you are using was written for Bootstrap 3, which has a slightly different pagination scheme.
I have updated the package Vue component with the proper class additions for Bootstrap 4. You can now use this component directly instead of loading the outdated package.
https://gist.github.com/matticustard/9e11277b2e4f32e8bfffa9a08e38f338
Pagination.vue
<template>
<nav>
<ul class="pagination" v-if="pagination.last_page > 0" :class="sizeClass">
<li class="page-item" v-if="showPrevious()" :class="{ 'disabled' : pagination.current_page <= 1 }">
<a href="#" class="page-link" v-if="pagination.current_page > 1 " :aria-label="config.ariaPrevioius" #click.prevent="changePage(pagination.current_page - 1)">
<span aria-hidden="true">{{ config.previousText }}</span>
</a>
</li>
<li class="page-item" v-for="num in array" :class="{ 'active' : num === pagination.current_page }">
{{ num }}
</li>
<li class="page-item" v-if="showNext()" :class="{ 'disabled' : pagination.current_page === pagination.last_page || pagination.last_page === 0 }">
<a href="#" class="page-link" v-if="pagination.current_page < pagination.last_page" :aria-label="config.ariaNext" #click.prevent="changePage(pagination.current_page + 1)">
<span aria-hidden="true">{{ config.nextText }}</span>
</a>
</li>
</ul>
</nav>
</template>
<script>
export default {
props: {
pagination: {
type: Object,
required: true,
},
callback: {
type: Function,
required: true,
},
options: {
type: Object,
},
size: {
type: String,
},
},
computed: {
array() {
if (this.pagination.last_page <= 0) {
return [];
}
let from = this.pagination.current_page - this.config.offset;
if (from < 1) {
from = 1;
}
let to = from + (this.config.offset * 2);
if (to >= this.pagination.last_page) {
to = this.pagination.last_page;
}
const arr = [];
while (from <= to) {
arr.push(from);
from += 1;
}
return arr;
},
config() {
return Object.assign({
offset: 3,
ariaPrevious: 'Previous',
ariaNext: 'Next',
previousText: '«',
nextText: '»',
alwaysShowPrevNext: false,
}, this.options);
},
sizeClass() {
if (this.size === 'large') {
return 'pagination-lg';
} else if (this.size === 'small') {
return 'pagination-sm';
}
return '';
},
},
watch: {
'pagination.per_page'(newVal, oldVal) { // eslint-disable-line
if (+newVal !== +oldVal) {
this.callback();
}
},
},
methods: {
showPrevious() {
return this.config.alwaysShowPrevNext || this.pagination.current_page > 1;
},
showNext() {
return this.config.alwaysShowPrevNext ||
this.pagination.current_page < this.pagination.last_page;
},
changePage(page) {
if (this.pagination.current_page === page) {
return;
}
this.$set(this.pagination, 'current_page', page);
this.callback();
},
},
};
</script>
I am trying to update the items in my dropdown as my state updates with players BUT I am failing. Please help.
In the child component I have dropdown in the Skylight dialog box.
I am using following code which prints the dropdown of the array of players from the state. State is updated from the json.php (remote server).
<select name="bowlerId" value={this.state.bowlerId} onChange={this.handleInputChange} className="form-control">
<option value="">Select an existing bowler</option>
{
this.currState.teamBowling.players.map(function (player, index) {
return <option value="{player}" key={index}>{player}</option>
})
}
</select>
I can clearly see in the console.log that the state is correctly updated and it prints 11 player names. But in the child component it does not update the dropdown.
Surprising enough, in the child component componentWillReceiveProps() does print new player names as they become availble but the dropdown is not updated still.
Please advise.
Console.log output as page loads
BowlingSummary render() Object {id: 0, name: "", runs: 0, overs: 0, balls: 0…}
PropStore Object {id: 0, name: "", runs: 0, overs: 0, balls: 0…}
BowlingAddBowler render() []
Console.log output after few seconds as data is fetched from php
BowlingSummary render() Object {id: 20, name: "Team Bowling", runs: 0, overs: 0, balls: 0…}
componentWillReceiveProps Object {id: 20, name: "Team Bowling", runs: 0, overs: 0, balls: 0…}
BowlingAddBowler render() []
Parent component named BowlingSummary
class BowlingSummary extends Component {
render() {
const { store } = this.context;
const currState = store.getState();
this.isBowlerBowling = false;
console.log('BowlingSummary render()', currState.teamBowling);
return (
<div>
<div className="score-summary-bar">
<div className="row">
<div className="col-xs-4">Bowling</div>
<div className="col-xs-1"></div>
<div className="col-xs-1">Ovr</div>
<div className="col-xs-1">Mdn</div>
<div className="col-xs-1">Run</div>
<div className="col-xs-1">Wkt</div>
<div className="col-xs-1">Econ</div>
<div className="col-xs-1">W</div>
<div className="col-xs-1">N</div>
</div>
</div>
<div className="score-summary-bowling">
<div className="">
{
currState.teamBowling.players.map(function (player, index) {
if ( player.isBowling === true ) {
this.isBowlerBowling = true;
return <BowlingBowler player={player} key={index}/>
}
})
}
</div>
<div className="">
{
this.isBowlerBowling != true &&
<BowlingAddBowler propStore={store} />
}
</div>
</div>
<div className="score-summary-bowling">
<ScoreThisOver />
</div>
</div>
);
}
}
BowlingSummary.contextTypes = {
store: PropTypes.object
}
export default BowlingSummary;
Child component named BowlingAddBowler
class BowlingAddBowler extends Component {
constructor(props, context) {
super(props, context);
// Setup current state
this.currState = this.props.propStore.getState();
console.log('PropStore', this.currState.teamBowling);
// This component state
this.state = {
bowlerId: 0,
bowlerName: '',
markPrevOverFinished: false
};
// Setup variables
this.players = this.props.players;
// Bind this (so that we write short code in the onChange like onChange=this.func)
this.handleInputChange = this.handleInputChange.bind(this);
}
componentWillReceiveProps(nextProps, nextState){
//this.props.something // old value
//nextProps.something // new value
console.log('componentWillReceiveProps', nextProps.propStore.getState().teamBowling);
}
render() {
//console.log('BowlingBowler render()', this.props);
var responsiveWidth = {
width: '90vw',
transform: 'translate(-23%, 0%)'
};
console.log('BowlingAddBowler render()', this.currState.teamBowling.players);
return (
<div className="row">
<div className="col-xs-12">
<button className="btn" onClick={() => this.refs.dialogAddBowler.show()}>No Bowler Selected. Click To Select</button>
</div>
<SkyLight dialogStyles={responsiveWidth} hideOnOverlayClicked ref="dialogAddBowler" title="Select a new bowler">
<div>
<form onSubmit={this.handleSubmit.bind(this)}>
<label className="bg-yellow">
Total <span className="text-red">0</span> for the loss of <span className="text-red">0</span> wickets in <span className="text-red">0</span> overs
</label>
<div className="spacer-horizontal"></div>
<label>Who will bowl the next over ?</label>
<select name="bowlerId" value={this.state.bowlerId} onChange={this.handleInputChange} className="form-control">
<option value="">Select an existing bowler</option>
{
this.currState.teamBowling.players.map(function (player, index) {
return <option value="{player}" key={index}>{player}</option>
})
}
</select>
<div className="spacer-horizontal"></div>
<b>- OR -</b>
<input type="text" name="bowlerName" value={this.state.bowlerName} onChange={this.handleInputChange} className="form-control" />
<div className="spacer-horizontal"></div>
<input type="checkbox" name="markPrevOverFinished" checked={this.state.markPrevOverFinished} onChange={this.handleInputChange}/>
<label> Mark previous over finished</label>
<div className="spacer-horizontal"></div>
<div className="HorizLine"></div>
<div className="text-right">
<button type="submit" className="btn btn-primary"> Save </button>
</div>
</form>
</div>
</SkyLight>
</div>
)
}
}
This code:
this.currState = this.props.propStore.getState();
is in BowlingAddBowler's constructor and only runs first time when the class is instantiated. You can put it in componentWillReceiveProps of BowlingAddBowler too so that it runs when new data comes from server and parent component updates BowlingAddBowler.