How to edit and update form input values in vue.js? - javascript

I am getting up the details in a form for a hotel entry with basic details and viewing it in Room.vue. The created values were displayed here but here i need to give edit option for the filled details. When i click the edit, the page should redirect to RoomsEdit.vue and i should get the filled up contents in that page with form inputs. For that i have tried the following codes but nothing solves my issue.. Kindly have a go through and help me in solving the issue.
Room.vue:
<table class="table table-striped table-hover rooms-table">
<thead>
<tr>
<td>Title<i class="fa fa-sort"></i></td>
<td>Sub Title<i class="fa fa-sort"></i></td>
<td> Edit </td>
<td> Delete </td>
</tr>
</thead>
<tbody>
<tr v-for="room in items">
<td>{{ room.title }}</td>
<td>{{ room.subtitle }}</td>
<td>
<router-link class="btn btn-primary" v-bind:to="'rooms/edit/'+id">Edit</router-link>
</td>
<td>
<button class="btn btn-primary" v-on:click="deleteRoom(room)">Delete</button>
</td>
</tr>
</tbody>
</table>
Here i am giving edit option and making a redirect to edit page..
Script of Rooms.vue:
<script>
import config from '../../../config';
export default {
data(){
return{
items: [],
itemsData:{
title : '',
subtitle : '',
}
}
},
methods: {
deleteRoom: function (room) {
var index = this.items.indexOf(room);
this.items.splice(index, 1);
}
},
mounted() {
axios.get(config.apiDomain+'/Rooms').then((response)=>this.items = response.data);
}
}
</script>
RoomsEdit.vue:
<form #submit.prevent="updateItems" class="form-horizontal" enctype="multipart/form-data">
<div class="form-group">
<label for="title" class="col-sm-2 control-label">Title<span class="red">*</span></label>
<div class="col-sm-6">
<input type="text" name="title" class="form-control" id="title" v-model="itemsData.title">
<span class="text-danger">{{ errors.title?errors.title[0]:"" }}</span>
</div>
</div>
<div class="form-group">
<label for="subtitle" class="col-sm-2 control-label">Subtitle<span class="red">*</span></label>
<div class="col-sm-6">
<input type="text" name="subtitle" class="form-control" id="subtitle" v-model="itemsData.subtitle">
<span class="text-danger">{{ errors.subtitle?errors.subtitle[0]:"" }}</span>
</div>
</div>
</form>
Script of RoomsEdit:
<script>
import config from '../../../config';
export default {
data(){
return{
items: [],
itemsData:{
title : '',
subtitle : '',
},
errors: {
}
}
},
methods:{
fetchRoom(id){
axios.get(config.apiDomain+'/Rooms').then((response)=>this.items = response.data);
},
updateItems(e){
axios.put(config.apiDomain+'/Rooms/edit'+this.$route.params.id,this.itemsData).then(response=>{
this.this.itemsData = "";
this.$router.push('/admin/rooms');
}).catch(error=>{
this.errors = error.response.data;
});
}
},
created: function() {
this.fetchRoom(this.$route.params.id);
}
}
</script>
RoomsCreate.vue:
<form #submit.prevent="addItems" class="form-horizontal" enctype="multipart/form-data">
<div class="form-group">
<label for="title" class="col-sm-2 control-label">Title<span class="red">*</span></label>
<div class="col-sm-6">
<input type="text" name="title" class="form-control" id="title" v-model="itemsData.title">
<span class="text-danger">{{ errors.title?errors.title[0]:"" }}</span>
</div>
</div>
<div class="form-group">
<label for="subtitle" class="col-sm-2 control-label">Subtitle<span class="red">*</span></label>
<div class="col-sm-6">
<input type="text" name="subtitle" class="form-control" id="subtitle" v-model="itemsData.subtitle">
<span class="text-danger">{{ errors.subtitle?errors.subtitle[0]:"" }}</span>
</div>
</div>
Script of RoomsCreate.vue:
<script>
import config from '../../../config';
export default {
data(){
return{
items: [],
itemsData:{
title : '',
subtitle : '',
},
errors: {
}
}
},
methods:{
addItems(){
axios.post(config.apiDomain+'/Rooms',this.itemsData).then(response=>{
this.this.itemsData = "";
this.$router.push('/admin/rooms');
}).catch(error=>{
this.errors = error.response.data;
});
}
}
</script>
The issue i am facing is when i click the edit in Room.vue, it redirects to the RoomsEdit.vue where i am not getting the values that was already created, i need to get those value when i go to RoomsEdit.vue there i should edit and update the content.

Related

Passing data from Parent to Child modal - Vue.js

Im trying to pass some data from a parent component to a child component which is a modal. For some reason, the error thrown to me is "Uncaught TypeError: Cannot set property 'domain' of undefined". I am trying to pass through the parent component item with its key and then finding this element in the parent and then to a data variable in the child component. Can someone give me a clue on what im missing? Thank you!
This is my parent component Domain.vue
<template>
<div class="container">
<div class="form-group">
<input type="text" class="form-control" id="filter" placeholder="Filter the Domains">
</div>
<div class="row content-holder">
<table>
<thead>
<tr>
<th class="client-name">Domain</th>
<th class="client-pm">Client</th>
<th class="client-pm">Add Log on Domain</th>
</tr>
</thead>
<tbody>
<tr v-for="domain in domains" :key="domain.id" :domain="domain" class="tr-table">
<td class="client-name">{{ domain.url }}</td>
<td class="client-pm">{{ domain.client }}</td>
<td class="client-pm center"><i class="fas fa-plus jobs-page" data-toggle="modal" #click="openAdd(domain)"></i></td>
</tr>
</tbody>
</table>
</div>
<Add :openmodal="addActive" #closeRequest='close'></Add>
</div>
</template>
<script>
import axios from 'axios';
let Add = require('./Add.vue');
export default {
name:'Domain',
data(){
return {
addActive:'',
domains: [],
domain: {id:'', url:'', client: ''},
errors:{}
}
},
methods:{
getDomains(){
window.axios.get('/develogger-app/public/api/domains').then(({data})=>{
data.forEach(domain =>{
this.domains.push(domain)
});
});
},
openAdd(key){
this.$children[1].domain = this.domains[key];
this.addActive = 'is-active';
},
save(){
},
close(){
this.addActive = '';
},
},
created(){
this.getDomains();
},
components:{
Add
}
}
</script>
This is Child component Add.vue
<template>
<!-- Modal -->
<div :class="openmodal" class="modal fade" id="exampleModalCenter" tabindex="-1" >
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">Create new Log</h5>
<button type="button" class="close">
<i class="fas fa-times" #click="close"></i>
</button>
</div>
<div class="modal-body">
<form method="post">
<input type="hidden" name="_token" :value="csrf">
<input name="website" type="text" id="website" class="form-control" placeholder="Log Title"><br>
<select id="type" class="form-control" name="type"><br>
<option></option>
</select>
<br>
<select id="type" class="form-control" name="type"><br>
<option value="" disabled selected>Type</option>
<option>Client Update</option>
<option>Dev Update</option>
<option>Bug</option>
<option>Style Fix</option>
</select>
<br>
<label class="left" for="description">Log Description:</label>
<textarea class="form-control" rows="5" id="description" name="description"></textarea>
<br>
<div class="left">
<input type="checkbox" name="tell-everyone" id="tell-everyone">
<label for="description">Tell Everyone?</label>
<br>
<input type="checkbox" name="status" id="status" value="checked">
<label for="checked">Resolved and Tested?</label>
</div>
</form>
</div>
<div class="modal-footer">
<button id="log-it" type="button" class="btn btn-circle btn-xl" data-dismiss="modal">
<span id="button-content"><b>LOG IT</b></span>
<span id="button-content"><b>FIX IT</b></span>
</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
// checked:false,
name:'Add',
props:['openmodal'],
data(){
return{
domain:'',
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
}
},
methods:{
close(){
this.$emit('closeRequest');
}
},
// computed: {
// isComplete () {
// return this.log.title && this.log.domain_id && this.log.type && this.log.description;
// }
// },
}
</script>
You are specifying your data as a function in the main Vue instance. It should be
an object in the main Vue instance:
data: {
....
}
while a function in the component:
data() {
return {...}
}
See the official reference.
You have data as function for both.

vue js multiple dynamic selects population issue

Hello there vue js guru's
Currently i'm creating some wizard with a little bit more "advanced" setup and need some help getting the correct outcome. I tried a few different methods but not with any success yet.
What i'm trying to do is the following:
I'm building a workout create wizard where it's possible to add sections and inside each section you could define one or more movements where each movement itself should have some sort of measurement this last one should change based on the selected movement and could have one or more options as an select to.
The data is fetched by axios (remotely) and saved to the data array for available movements like so:
[
'' => [
1 => [
'name' => 'pull ups',
'measure' => [
0 => 'none',
1 => 'distance',
],
],
2 => [
'name' => 'push ups',
'measure' => [
0 => 'none',
1 => 'weight',
],
],
...
],
...
]
this will then be stored in the this.movements = data.movements; data array in my vue js instance.
Here is my vue js code:
<script>
new Vue({
el: '#workout-wrapper',
data() {
return {
dynamicOptions: [],
name: null,
sections: [{
name: null,
rounds: null,
minutes: null,
measure: null,
movements: [{
reps: null,
movement: null,
measure: null,
remarks: null
}]
}],
movements: [],
...
}
},
methods: {
...
onMovementChange(group, movement) {
// TODO: this one combined with the computed options still isn't working correctly
if (group == '') {
this.options = { section: 0, movement: 0, options: this.movements[""][movement].measure };
} else {
this.options = { section: 0, movement: 0, options: this.movements[group][movement].measure };
}
},
...
},
computed: {
options: {
get(event, data) {
// TODO: now we should only return the options for section and movement indexes
return this.dynamicOptions;
},
set(data) {
this.dynamicOptions[data.section] = [];
this.dynamicOptions[data.section][data.movement] = data.options;
// console.log(this.dynamicOptions);
}
}
},
created() {
axios.get('/workouts/create/data').then(response => {
let data = response.data;
this.movements = data.movements;
...
}).catch(error => {
console.error(error);
});
}
});
</script>
And here the template:
<div class="row m-t-20" v-for="(section, index) in sections">
<div class="col-md-12">
<div class="card card-default no-border">
<div class="card-header separator">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label>Section name</label>
<div class="input-group m-b-15">
<input type="text" name="sections[0][name]" placeholder="e.g. Warming-Up" class="form-control" v-model="section.name">
<div class="input-group-btn">
<button type="button" class="btn btn-danger" data-title="Remove" data-tooltip #click="removeSection(index)">
<i class="fa fa-times"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Rounds</label>
<input type="number" name="sections[0][rounds]" class="form-control" placeholder="Optional" min="0" v-model="section.rounds">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="">
Minutes
</label>
<input type="number" name="sections[0][minutes]" class="form-control" placeholder="Optional" min="0" v-model="section.minutes">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Measure</label>
<select name="sections[0][measure]" class="form-control" v-model="section.measure"> {{-- data-init-plugin="select2" --}}
<option :value="key" v-for="(measure, key) in measurements">#{{ measure }}</option>
</select>
</div>
</div>
</div>
</div>
<div class="card-block m-t-25">
<table class="table table-striped">
<thead>
<tr>
<th width="10%">
Reps
</th>
<th width="35%">
Movement
</th>
<th width="20%">
Measure
</th>
<th width="35%">
Remarks
</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="(movement, movementIndex) in section.movements">
<td>
<input type="number" name="sections[0][movements][0][reps]" class="form-control" min="0" v-model="movement.reps">
</td>
<td>
<select name="sections[0][movements][0][movement]" class="form-control" v-model="movement.movement" #change="onMovementChange('', movement.movement)">
<optgroup :label="group" v-for="(options, group) in movements">
<option :value="key" v-for="(option, key) in options">#{{ option.name }}</option>
</optgroup>
</select>
</td>
<td>
<select name="sections[0][movements][0][measure]" class="form-control" v-model="movement.measure" :disabled="!movement.movement">
<option :value="key" v-for="(measure, key) in options">#{{ measure }}</option>
</select>
</td>
<td>
<textarea name="sections[0][movements][0][remark]" rows="1" class="form-control" v-model="movement.remarks"></textarea>
</td>
<td>
<button type="button" class="btn btn-link text-danger" data-title="Remove" data-tooltip #click="removeMovement(index, movementIndex)">
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
<tr>
<td colspan="5" class="text-center">
<button type="button" class="btn btn-cons btn-complete" data-title="Add movement" data-tooltip #click="addMovement(index)">
<i class="fa fa-plus"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
With this current set up it's adding the data in the second select box but it's always changing, so when adding a second movement row then both select boxes are changing there options based on the selected movement in the second row etc... it should only change based on the current row instead and section off course.
Maybe some of you could help me with this problem... if more info is needed please let me know :-)

How do I pass data in $router.push in Vue.js?

I am creating an alert component for a CRUD app using Vue.js. I want a message to be passed to another component once data has been saved. Currently I am trying to pass this data in $router.push like this this.$router.push({path: '/', query: {alert: 'Customer Added'}}) Then access this data in another component. However this is not working as expected, instead the data is passed into the url.
This is the component which saves the data, Add.vue
<template>
<div class="add container">
<Alert v-if="alert" v-bind:message="alert" />
<h1 class="page-header">Add Customer</h1>
<form v-on:submit="addCustomer">
<div class="well">
<h4>Customer Info</h4>
<div class="form-group">
<label>First Name</label>
<input type="text" class="form-control" placeholder="First Name"
v-model="customer.first_name">
</div>
<div class="form-group">
<label>Last Name</label>
<input type="text" class="form-control" placeholder="Last Name"
v-model="customer.last_name">
</div>
</div>
<div class="well">
<h4>Customer Contact</h4>
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" placeholder="Email" v-model="customer.email">
</div>
<div class="form-group">
<label>Phone</label>
<input type="text" class="form-control" placeholder="Phone" v-model="customer.phone">
</div>
</div>
<div class="well">
<h4>Customer Location</h4>
<div class="form-group">
<label>Address</label>
<input type="text" class="form-control" placeholder="Address" v-model="customer.address">
</div>
<div class="form-group">
<label>City</label>
<input type="text" class="form-control" placeholder="City" v-model="customer.city">
</div>
<div class="form-group">
<label>State</label>
<input type="text" class="form-control" placeholder="State" v-model="customer.state">
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</template>
<script>
import Alert from './Alert'
export default {
name: 'add',
data () {
return {
customer: {},
alert:''
}
},
methods: {
addCustomer(e){
if(!this.customer.first_name || !this.customer.last_name ||
!this.customer.email){
this.alert = 'Please fill in all required fields';
} else {
let newCustomer = {
first_name: this.customer.first_name,
last_name: this.customer.last_name,
phone: this.customer.phone,
email: this.customer.email,
address: this.customer.address,
city: this.customer.city,
state: this.customer.state
}
this.$http.post('http://slimapp.dev/api/customer/add',
newCustomer)
.then(function(response){
this.$router.push({path: '/', query: {alert: 'Customer
Added'}})
});
e.preventDefault();
}
e.preventDefault();
}
},
components: {
Alert
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only
-->
<style scoped>
</style>
This the alert component, Alert.vue
<template>
<div class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span></button>
{{message}}
</div>
</template>
<script>
export default {
name: 'alert',
props: ['message'],
data () {
return {
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
And this is the component where the alert is to be viewed, Customers.vue
<template>
<div class="customers container">
<Alert v-if="alert" v-bind:message="alert" />
<h1 class="page-header">Manage Customers</h1>
<table class="table table-striped">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="customer in customers">
<td>{{customer.first_name}}</td>
<td>{{customer.last_name}}</td>
<td>{{customer.email}}</td>
<td></td></tr>
</tbody>
</table>
</div>
</template>
<script>
import Alert from './Alert';
export default {
name: 'customers',
data () {
return {
customers: [],
alert: ''
}
},
methods: {
fetchCustomers(){
this.$http.get('http://slimapp.dev/api/customers')
.then(function(response){
this.customers = (response.body);
});
}
},
created: function(){
if (this.$route.params.alert) {
this.alert = $route.params.alert
}
this.fetchCustomers();
},
updated: function(){
this.fetchCustomers();
},
components: {
Alert
}
}
How do I solve this?
It is not possible to pass data through vue-router the way you want to. You only can pass parameters like this:
Route definition:
{ path: '/products/:id/edit', name: 'products.edit', component: ProductForm },
And then you can get the parameter with this.$route.params.id
Or you can do:
this.$router.push({name: 'products.index', params: { id: 1 }})
I suggest you to add a GET parameter like ?success=true or show an alert with sweetalert for example before pushing the new route.
A weird solution is to set the value to store/local_storage and retrieve and destroy it from the store/local_storage when the destination page loads.

What causes VueJS to stop updating the DOM in this instance?

I've looked on every similar post that I can find, but no answer seems to fix my issue. Specifically, it does not update the table with id "table".
HTML:
<section id="body">
<div class="container-fluid">
<div class="row">
<div class="col-xs-12">
<div class="panel panel-primary">
<div class="panel-heading" id="panel">
<div class="row">
<div class="col-sm-12">
<h3 class="panel-title">Filters</h3>
</div>
</div>
</div>
<div class="panel-body" id="panel-body">
<div class="row">
<div class="col-sm-12">
<form id="filterForm" class="form-horizontal">
<div class="form-group">
<div class="col-sm-12">
<label class="control-label" for="focusedInput">Category:</label>
<select id="category" class="js-example-basic-single form-control">
<option value="">Any</option>
<option v-for="category in categories" value="category.categoryTitle">
{{category.categoryTitle}}</option>
</select>
</div>
</div>
<div class="form-inline row">
<div class="col-sm-12">
<label class="control-label" style="margin-right:20px;">Air Date:</label>
<div style="width:35%" class="form-group">
<div class='input-group date' id='datetimepicker1'>
<input type='text' class="form-control" v-model="airDate"/>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
<label class="control-label">Show Number:</label>
<input style="width:35%" class="form-control" type="number" id="showNumber" v-model="showNumber">
</div>
</div>
<div class="form-inline row">
<div class="col-sm-12">
<label class="control-label">Question contains:</label>
<input style="width:35%" class="form-control" type="text" v-model="questionText">
<label class="control-label">Dollar Value:</label>
<input style="width:35%" class="form-control" type="number" id="showNumber" v-model="dollarValue">
</div>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-sm-offset-9 col-sm-3" style="margin-top:5px;">
<button type="button" class="btn btn-warning" v-on:click="reset">Reset Filters</button>
<button type="button" class="btn btn-primary" v-on:click="filter">Filter</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="panel panel-primary" id="tableCard" style="margin-bottom:20px; margin-top:40px;">
<div class="panel-heading">
<div class="row">
<div class="col-sm-10">
<h3 class="panel-title">Jeopardy Questions</h3>
</div>
<div class="col-sm-2">
<span id="totalQuestionsSpan">Total Entries: {{entries.length}} entries</span>
</div>
</div>
</div>
<div class="panel-body" style="padding-top:45px;">
<div class="wrapper">
<table id="tableScroll" class="table table-striped table-fixed">
<thead style="background-color:white;">
<tr>
<th style="cursor:pointer; min-width: 110px;">
Question
<span v-if="questionSort == 1" id="questionUp">▲</span>
<span v-else-if="questionDown == -1" id="questionDown">▼</span>
</th>
<th style="cursor:pointer; min-width: 120px; ">
Answer
<span v-if="answerSort == 1" id="answerUp">▲</span>
<span v-else-if="answerDown == -1" id="answerDown">▼</span>
</th>
<th style="cursor:pointer; min-width: 80px;">
Value
<span v-if="valueSort == 1" id="valueUp">▲</span>
<span v-else-if="valueDown == -1" id="valueDown">▼</span>
</th>
<th style="cursor:pointer; min-width: 80px;">
Show Number
<span v-if="showNumberSort == 1" id="showNumberUp">▲</span>
<span v-else-if="showNumberDown == -1" id="showNumberDown">▼</span>
</th>
<th style="cursor:pointer; min-width: 80px;">
Category
<span v-if="categorySort == 1" id="categoryUp">▲</span>
<span v-else-if="categoryDown == -1" id="categoryDown">▼</span>
</th>
<th style="cursor:pointer; min-width: 80px;">
Air Date
<span v-if="airDateSort == 1" id="airDateUp">▲</span>
<span v-else-if="airDateDown == -1" id="airDateDown">▼</span>
</th>
</tr>
</thead>
<tbody id="table">
<tr v-for="entry in entries">
<td>{{entry.questionText}}</td>
<td>{{entry.answerText}}</td>
<td>{{entry.dollarValue}}</td>
<td>{{entry.showNumber}}</td>
<td>{{entry.categoryTitle}}</td>
<td>{{entry.airDate}}</td>
</tr>
<tr v-if="entries.length == 0">
<td colspan="6" style="text-align: center;"> No entries to display </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
JS
var app = new Vue({
el: '#body',
data: {
loggedIn: false,
questionSort: 0,
answerSort: 0,
valueSort: 0,
showNumberSort: 0,
categorySort: 0,
airDateSort: 0,
entries: [],
url: "/questions",
categories: [],
// form model data
categoryTitle: '',
airDate: '',
questionText: '',
dollarValue: '',
showNumber: '',
},
mounted: function () {
$.get("/api/categories", function(result) {
Vue.set(app, "categories", result.data);
$('.js-example-basic-single').select2();
}, "json").fail(function(err) {
showErrorMessage(err.responseJSON.message_prettified);
});
},
methods: {
filter : function() {
var queryParams = "?";
var params = 0;
app.categoryTitle = $('#category :selected').text().trim();
if (typeof app.categoryTitle !== "undefined" && app.categoryTitle != null) {
params++;
queryParams += "categoryTitle=" + app.categoryTitle
}
if (app.airDate.length > 0) {
params++;
if (params > 0) {
queryParams += "&";
}
queryParams += "airDate=" + app.airDate
}
if (app.questionText.length > 0) {
params++;
if (params > 0) {
queryParams += "&";
}
queryParams += "questionText=" + app.questionText
}
if (app.dollarValue.length > 0) {
params++;
if (params > 0) {
queryParams += "&";
}
queryParams += "dollarValue=" + app.dollarValue
}
if (app.showNumber.length > 0) {
params++;
if (params > 0) {
queryParams += "&";
}
queryParams += "showNumber=" + app.showNumber
}
if (queryParams.length == 1) {
queryParams = "";
}
var url = "/questions"
var URL = url + queryParams;
$.get(URL, result => {
Vue.set(app, "entries", result.data);
app.$forceUpdate();
}, "json").fail(function(err) {
showErrorMessage(err.responseJSON.message_prettified);
}).always(function() {
$("#loader").addClass("toggled");
});
}
}
});
Current behavior:
The AJAX call to /api/categories correctly updates the dropdown on the DOM, allowing me to select a category. When the app is mounted, it does update the table, showing the colspan 6 "No entries to display" cell. However, after the filter request is sent and returns, the table does not update to reflect the updated data (despite the data correctly showing as having been changed when checked in the console).
Expected behavior:
When the AJAX call to /questions with the query params resolves and updates the entries data field in app, the table updates to reflect the changes.
Attempted fixes:
Explored $forceUpdate, $set, Vue.set, and manually overwriting the array using a for loop.
Edit:
After doing a lot of snooping and also integrating VueX (as suggested below by #WaldemarIce) which may have helped but regardless did improve the overall code structure of my mini program, I've arrived to a solution.
This post on Laracast made me wonder whether perhaps there was a data issue: https://laracasts.com/discuss/channels/vue/v-for-loop-rendering-keeps-throwing-undefined-error
Which then caused me to realize the problem was in this line of code here:
<option v-for="category in categories" value="category.categoryTitle">
{{category.categoryTitle}}</option>
Which was causing an issue because category in value="category.categoryTitle" wasn't defined until later in the life cycle. I changed that to v-bind:value="category.categoryTitle" and updated my JS to make it work now. The TypeError that I posted in the follow-up discussions on #Kaicui 's post was causing Vue to lose the reactivity of the data. Once I solved that problem, Vue began to react properly again.
Updated HTML:
<section id="body">
<div class="container-fluid">
<div class="row">
<div class="col-xs-12">
<div class="panel panel-primary">
<div class="panel-heading" id="panel">
<div class="row">
<div class="col-sm-11">
<h3 class="panel-title">Filters</h3>
</div>
<div class="col-sm-1">
<i id="toggleFilter" class="fa fa-chevron-down filter-collapsed" style="cursor:pointer; display:none;" aria-hidden="true"></i>
<i id="toggleFilter" class="fa fa-chevron-up filter-collapsed" aria-hidden="true" style="cursor:pointer;"></i>
</div>
</div>
</div>
<div class="panel-body" id="panel-body">
<div class="row">
<div class="col-sm-12">
<form id="filterForm" method="GET" action="/questions" class="form-horizontal">
<div class="form-inline">
<div class="col-sm-12" style="margin-bottom:15px;">
<input type="hidden" name="categoryTitle" id="categoryTitleHidden">
<label class="control-label" for="focusedInput">Category:</label>
<select style="width:90%; height:120% !important;" v-model="categorySelect" id="category" class="js-example-basic-single form-control">
<option value="">Any</option>
<option v-for="category in categories" v-bind:value="category.categoryTitle">
{{category.categoryTitle}}</option>
</select>
</div>
</div>
<div class="form-inline">
<div class="col-sm-12" style="margin-bottom:15px;">
<label class="control-label" style="margin-right:20px;">Air Date:</label>
<div style="width:35%; margin-right:10px" class="form-group">
<div style="width:100%" class='input-group date' id='datetimepicker1'>
<input type='text' class="form-control" name="airDate"/>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
<label class="control-label">Show Number:</label>
<input style="width:35%" class="form-control" type="number" id="showNumber" name="showNumber">
</div>
</div>
<div class="form-inline">
<div class="col-sm-12">
<label class="control-label">Question contains:</label>
<input style="width:35%" class="form-control" type="text" id="questionText" name="questionText">
<label class="control-label">Dollar Value:</label>
<input style="width:35%" class="form-control" type="number" id="dollarValue" name="dollarValue">
</div>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-sm-offset-9 col-sm-3" style="margin-top:5px;">
<button type="button" class="btn btn-warning" v-on:click="reset">Reset Filters</button>
<button type="button" class="btn btn-primary" v-on:click="filter">Filter</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="panel panel-primary" id="tableCard" style="margin-bottom:20px; margin-top:40px;">
<div class="panel-heading">
<div class="row">
<div class="col-sm-10">
<h3 class="panel-title">Jeopardy Questions</h3>
</div>
<div class="col-sm-2">
<span id="totalQuestionsSpan">Total Entries: {{entryCount}} entries</span>
</div>
</div>
</div>
<div class="panel-body" style="padding-top:45px;">
<div class="wrapper">
<table id="tableScroll" class="table table-striped table-fixed">
<thead style="background-color:white;">
<tr>
<th style="cursor:pointer; min-width: 110px;">
Question
</th>
<th style="cursor:pointer; min-width: 120px; ">
Answer
</th>
<th style="cursor:pointer; min-width: 80px;">
Value
</th>
<th style="cursor:pointer; min-width: 80px;">
Show Number
</th>
<th style="cursor:pointer; min-width: 80px;">
Category
</th>
<th style="cursor:pointer; min-width: 80px;">
Air Date
</th>
</tr>
</thead>
<tbody id="table">
<tr v-if="entriesValid" v-for="entry in entries">
<td>{{entry.questionText}}</td>
<td>{{entry.answerText}}</td>
<td>{{entry.dollarValue}}</td>
<td>{{entry.showNumber}}</td>
<td>{{entry.categoryTitle}}</td>
<td>{{entry.airDate}}</td>
</tr>
<tr v-if="!entriesValid">
<td colspan="6" style="text-align: center;"> No entries to display </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="position: absolute; left: 45%; top:25%; z-index:3;">
<i id="loader" class="fa fa-circle-o-notch fa-spin fa-5x fa-fw toggled" style="z-index:3"></i>
</div>
</section>
Updated JS
Vue.use(Vuex)
Vue.config.debug = false;
Vue.config.silent = true;
var URL;
const store = new Vuex.Store({
state: {
loggedIn: false,
// ordering data
questionSort: 0,
answerSort: 0,
valueSort: 0,
showNumberSort: 0,
categorySort: 0,
airDateSort: 0,
// server related ata
entries: [],
url: "/questions",
categories: [{
categoryTitle: "Test",
}],
},
mutations: {
categories (state, data) {
state.categories = data;
},
entries (state, data) {
console.log(data);
state.entries = data;
console.log(state.entries)
}
},
actions: {
fetchCategories ({ commit }) {
$("#loader").removeClass("toggled");
$.get("/api/categories", function(result) {
commit('categories', result.data);
}, "json")
.fail(function(err) {
if (err.status == 0) {
showErrorMessage("Network Problem");
}
else {
showErrorMessage(err.responseJSON.message_prettified);
}
}).always(function() {
$("#loader").addClass("toggled");
});
},
},
});
var app = new Vue({
el: '#body',
store: store,
data: {
categorySelect: "",
},
mounted: function() {
store.dispatch("fetchCategories").then(() => {
$('.js-example-basic-single').select2();
});
},
computed: {
categories: function() {
return store.state.categories;
},
entryCount: function() {
if (store.entries) {
if (typeof store.entries.length !== "undefined") {
return store.entries.length;
}
else {
return 0;
}
}
else {
return 0;
}
},
entriesValid: function() {
if (store.state.entries) {
if (typeof store.state.entries.length !== "undefined" && store.state.entries.length > 0) {
return true;
}
else {
return false;
}
}
else {
return false;
}
},
entries: function() {
return store.state.entries;
},
loggedIn: function() {
return store.state.loggedIn;
},
},
methods: {
reset: function() {
$('.js-example-basic-single').val('').trigger('change');
$("#datetimepicker1").datetimepicker("clear");
$("#categoryTitleHidden").val("");
$("#showNumber").val("");
$("#questionText").val("");
$("#showNumber").val("");
$("#dollarValue").val("");
},
filter : function() {
var value = $('#category :selected').text().trim();
if (value !== "Any") {
$("#categoryTitleHidden").val(value);
}
else {
$("#categoryTitleHidden").val("");
}
var options = {
success: function(responseText, statusText, xhr, $form) {
store.commit("entries", JSON.parse(xhr.responseText).data)
}
};
$("#filterForm").ajaxSubmit(options);
}
}
});
IMO problem is causing Vue.set(app, ...). AFAIK you can't set properties on Vue instance itself.
EDIT: Live example with Vuex and async data with jQuery
var store = new Vuex.Store({
state: {
// Car manufacturers for datalist will be held here.
// Cars are globally accessible, in every component,
// as this.$store.state.cars
cars: null
},
mutations: {
// Mutations changes state, but must be sync,
// so you can't call $.get() or another
// async function in any mutation.
updateCars: function (state, payload) {
state.cars = payload
}
},
actions: {
// For async ops there are actions,
// but they can't change state - for state
// change fire particular mutation.
loadCars: function (context, payload) {
$.get(payload.src).then(function (data) {
context.commit('updateCars', data)
})
}
}
})
Vue.component('my-list', {
template: '#my-list',
props: ['src'],
// All components see this.$store.state.cars, but
// still can have own local data.
data: function () {
return {
manufacturer: ''
}
},
// Fire async store action
created: function () {
this.$store.dispatch({
type: 'loadCars',
src: this.src
})
}
// Alternatively, you can use this
// version - w/o action. It's OK to use
// mutation here, in callback of async function.
/* created: function () {
$.get(this.src).then(function (data) {
this.$store.commit('updateCars', data)
})
} */
})
new Vue({
el: '#app',
// Inject store state to all components
store: store
})
<div id="app">
<my-list src="https://api.mockaroo.com/api/32318a80?count=20&key=cdbbbcd0">
</my-list>
</div>
<template id="my-list">
<div>
<label>
Choose a car manufacturer:<br>
<input list="cars" name="myCars" v-model="manufacturer">
</label>
<datalist id="cars">
<option
v-for="car in $store.state.cars"
:value="car.car"
>
{{ car.id }}
</option>
</datalist>
<p>
Selected manufacturer:<br>
{{ manufacturer }}
</p>
</div>
</template>
<script src="https://unpkg.com/vue#2.5.2/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex#3.0.0/dist/vuex.min.js"></script>
<script src="https://unpkg.com/jquery#3.2.1/dist/jquery.min.js"></script>
your code can not run because it relies on your server response.
but i think your code which setting resonse data to entries is fine.
This kind of problems may occur when other js code have caused an exception ,which interrupt the rendering of vue.
so could you check console and see is there any errors?

Build table from angular form data

So I am trying to build a table that takes the form data entered and is stored on the client and pushes each input property into a group to create an object. From there, the table is build using ng-repeat. Code started is below, but nothing is happening. Can anyone assist?
<form class="addClaim" ng-submit="submitClaim(claimInfo)">
<div class="form-group">
<label for="beneSelect">Select your benefit</label>
<select class="form-control" id="beneSelect" >
<option ng-repeat="descr in claim.claimBenes" data-ng-model="claimInfo.providerName">{{ descr.descr }}</option>
</select>
</div>
<div class="form-group">
<label for="start">Date of Service</label>
<div>
<input type="text" class="form-control" id="start" placeholder="--/--/----" data-ng-model="claimInfo.fromDate" style="width: 240px;">
<span>To</span>
<input type="text" class="form-control" id="end" placeholder="--/--/---- (optional)" data-ng-model="claimInfo.toDate" style="width: 240px;">
</div>
</div>
<div class="form-group">
<label for="providerName">Provider Name</label>
<input type="text" class="form-control" id="providerName" data-ng-model="claimInfo.provider">
</div>
<div class="form-group">
<label for="forWhom">For Whom</label>
<input type="text" class="form-control" id="forWhom" data-ng-model="claimInfo.who">
</div>
<div class="form-group" ng-show="claimInfo.benefCode == 'HCFSA'">
<label for="age">Age</label>
<input type="text" class="form-control" id="age" data-ng-model="claimInfo.who">
</div>
<div class="form-group">
<label for="claimAmount">Amount</label>
<input type="text" class="form-control" id="claimAmount" data-ng-model="claimInfo.amount">
</div>
<div class="form-group">
<label for="claimComment">Comments</label>
<textarea class="form-control" rows="5" id="claimComment" data-ng-model="claimInfo.comments"></textarea>
</div>
<div class="form-group">
<label class="control-label"></label>
<div>
<input type="button" class="btn btn-primary" ng-click="saveClaim()" value="add claim" style="float: right;">
</div>
</div>
</form>
The table:
<div class="claimedTable" style="background-color: #ffffff; color: black;">
<table class="table">
<thead>
<tr>
<th>service date</th>
<th>provider</th>
<th>amount</th>
<th>edit</th>
<th>delete</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in claimSubmit">
<td>{{ claimInfo.fromDate }}</td>
<td>{{ claimInfo.provider }}</td>
<td>{{ claimInfo.amount }}</td>
<td><i class="fa fa-pencil-square-o"></i></td>
<td><i class="fa fa-trash-o"></i></td>
</tr>
</tbody>
</table>
</div>
And controller:
$scope.claim = [];
claimsService.getClaim().then(function (results) {
$scope.claim = results.data;
});
// submit all claim objects entered
$scope.claimsSubmit = [];
$scope.claimInfo = {
id: "",
benefitId: "",
isSecIns: "",
isNoResId: "",
expenseTypeId: "",
fromDate: "",
toDate: "",
provider: "",
who: "",
depId: "",
age: "",
amount: "",
comments: "",
isOffset: ""
};
$scope.saveClaim = function() {
$scope.claimsSubmit.push($scope.claimInfo);
//clears scope so form is empty
$scope.claimInfo = {};
}
When I enter data into the fields and click submit, nothing ever leaves nor does it post to the table. The reason I want as an object versus an array is because the table may have multiple line items depending on how many times the form is field out and submitted.
Seems somewhere simple I am going wrong, but cannot figure where. Any help please?
Thanks much.
You have a few issues in your code. Here it is cleaned up a bit...
HTML
<form class="addClaim"> // ng-submit not needed in form tag
ng-repeat bindings should be item. not claimsSubmit.
<tr ng-repeat="item in claimsSubmit">
<td>{{ item.fromDate }}</td>
<td>{{ item.provider }}</td>
<td>{{ item.amount }}</td>
<td><i class="fa fa-pencil-square-o"></i></td>
<td><i class="fa fa-trash-o"></i></td>
</tr>
Controller
$scope.claim = []; // This can be removed.
claimsService.getClaim().then(function (results) {
$scope.claim = results.data;
});
// submit all claim objects entered
$scope.claimsSubmit = [];
$scope.claimInfo = {}; // This just needs to create an object.
$scope.saveClaim = function() {
$scope.claimsSubmit.push($scope.claimInfo);
//clears scope so form is empty
$scope.claimInfo = {};
}
This should be enough to get the form populating the table for you. There is obviously form data missing from the table but it will get you going in the right direction.

Categories