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

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?

Related

Vue router get value from vue router link

I have a list here from my table. It has a edit button and if I click edit button, It goes to another component which is called editTeacher. My question is how could I get the data from the table and transfer it to my editTeacher component. I get the data from route using axios . In laravel it is like this
<span class="glyphicon glyphicon-pencil"> .
How could I achieve it in vue?
Here is my code snippet
<table id="myTable" class="table table-hover">
<tbody>
<tr>
<th>ID</th>
<th>Image</th>
<th>First Name</th>
<th>Last Name</th>
<th>Gender</th>
<th>Birthday</th>
<th>Age</th>
<th>Type</th>
<th>Department</th>
<th>Status</th>
<th>Actions</th>
</tr>
<tr v-for="teacher in teachers" :key="teacher.id">
<td>{{teacher.id}}</td>
<td><img style=" border-radius: 50%;" :src="'img/'+teacher.image" height="42" width="42"/></td>
<td>{{teacher.firstname}}</td>
<td>{{teacher.lastname}}</td>
<td>{{teacher.gender}}</td>
<td>{{teacher.birthday}}</td>
<td>{{teacher.age}}</td>
<td>{{teacher.type}}</td>
<td>{{teacher.department_name}}</td>
<td v-if="teacher.status == 1"><span class="label label-success">Active</span></td>
<td v-else><span class="label label-danger">Inactive</span></td>
<td><router-link to="/viewTeacher"> <i class="fa fa-edit"></i></router-link></td>
</tr>
</tbody>
</table>
Route
//Teachers
Route::get('/getTeachers','TeacherController#index');
Route::post('/addTeacher','TeacherController#store');
Route::put('/editTeacher/{id}','TeacherController#update');
app.js route
{ path: '/viewTeacher', component: require('./components/editTeacher.vue').default },
Please follow the below code for the Vue js edit method.
As per your git repo.
app.js route
{ path: '/viewTeacher/:id', component: require('./components/editTeacher.vue').default, name: viewTeacher},
Edit Button in Teachers.vue
<router-link :to="{name: 'viewTeacher', params: {id: teacher.id}}" class="btn btn-xs btn-default">Edit</router-link>
EditTeacher.vue component
<template>
<div class="row">
<div class="col-xs-3">
<div class="box">
<div class="box-tools">
<img style="border-radius: 50%;" src="" height="100" width="50">
</div>
</div>
</div>
<div class="col-xs-9">
<div class="box">
<form v-on:submit.prevent="saveForm()">
<div class="row">
<div class="col-xs-12 form-group">
<label class="control-label">Teacher first name</label>
<input type="text" v-model="teacher.firstname" class="form-control">
</div>
</div>
<div class="row">
<div class="col-xs-12 form-group">
<label class="control-label">Teacher Last name</label>
<input type="text" v-model="teacher.lastname" class="form-control">
</div>
</div>
<div class="row">
<div class="col-xs-12 form-group">
<button class="btn btn-success">Update</button>
</div>
</div>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
let app = this;
let id = app.$route.params.id;
app.teacherId = id;
axios.get('/getTeacher/' + id)
.then(function (resp) {
app.teacher = resp.data;
})
.catch(function () {
alert("Could not load teacher")
});
},
data: function () {
return {
teacherId: null,
teacher: {
firstname: '',
lastname: '',
}
}
},
methods: {
saveForm() {
var app = this;
var newTeacher = app.teacher;
axios.patch('/editTeacher/' + app.teacherId, newTeacher )
.then(function (resp) {
app.$router.replace('/');
})
.catch(function (resp) {
console.log(resp);
alert("Could not Update");
});
}
}
}
Web.php
Route::get('/getTeachers','TeacherController#index');
Route::get('/getTeacher/{id}','TeacherController#show');
Route::post('/addTeacher','TeacherController#store');
Route::put('/editTeacher/{id}','TeacherController#update');
Controller
public function show($id)
{
return Teacher::findOrFail($id);
}

Property or method "key" is not defined on the instance - Vue/Laravel application

I have an application in which on deleting one of the registers i receive the message following Vue-warn:
Property or method "key" is not defined on the instance but referenced
during render. Make sure that this property is reactive, either in the
data option or for class-based components, by initializing the
property.
Only initializing the variable key on the data/return didn't actually solve my problem.
Another thing i noticed is that the data is deleted from the DB, the field log.title and log.user are deleted from the the logs list straight away but the other half of the row still remain on the view until I leave and reenter the Log component. Not sure if the above Vue-warn has something to do with this delete/viewing bug, but it might be.
I would appreciate if someone could give me a clue on what is going on?
This is my Log.vue:
<template>
<div class="container">
<log-show-detail v-if="logModalOpen" :logUser="logUser" :logTitle="logTitle" :logType="logType" :logDescription="logDescription" :logDate="logDate" #closeRequest='close'> </log-show-detail>
<log-edit v-if="editModalOpen" :logId="logId" :logUser="logUser" :logTitle="logTitle" :logType="logType" :logDescription="logDescription" :logDate="logDate" #closeRequest='close'></log-edit>
<div class="form-group">
<!-- <input type="text" class="form-control" id="filter" placeholder="Filter the Logs"> -->
<br>
<i class="fas fa-plus jobs-page" data-toggle="modal" data-target="#exampleModalCenter"></i>
<div v-if="isLoading" class="loading-image">
<img src="/develogger-app/public/img/dev-tick.gif" alt="">
</div>
</div>
<div class="row content-holder">
<table class="table">
<thead>
<tr class="left-align">
<th class="">Log</th>
<th class="">User</th>
<th class="center-align" style="text-align:right">Date</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="log in logs" :key="log.id" :log="log" #deleteLog="deleteLog" class="tr-table">
<td class="client-name" #click="openLogs(log.id,log.user,log.title,log.type,log.description,log.created_at)">{{ log.title }}</td>
<td class="" >{{ log.user }}</td>
<td class="client-pm" style="text-align:right">{{moment(log.created_at).fromNow()}}</td>
<!-- <td #click="openEdit(log.id,log.user,log.title,log.type,log.description,log.created_at)"><i class="fas fa-edit"></i></td> -->
<td #click="deleteLog(key,log.id)"><i class="fas fa-trash-alt"></i></td>
</tr>
</tbody>
</table>
</div>
<!-- Modal -->
<div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<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" data-dismiss="modal" aria-label="Close">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body">
<input type="hidden" name="_token" :value="csrf">
<input v-model="log.title" name="website" type="text" id="website" class="form-control" placeholder="Log Title"><br>
<select v-model="log.domain_id" id="type" class="form-control" name="type"><br>
<option value="" disabled selected>Website</option>
<option value="1">aerogen.com</option>
<option value="4">ardsolus.ie</option>
<option value="8">blacklionrealstate.fund</option>
<option value="11">blanchadstowncentre.ie</option>
<option value="7">chathamandking.ie</option>
<option value="5">clayfarm.ie</option>
<option value="10">edenplaza.ie</option>
<option value="6">hardwicke.ie</option>
<option value="2">kennedywilsonresidential.ie</option>
<option value="3">loulerie.ie</option>
<option value="9">procert.ie</option>
</select>
<br>
<select v-model="log.type" 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 v-model="log.description" class="form-control" rows="5" id="description" name="description"></textarea>
<br>
<div class="left">
<input v-model="tell" type="checkbox" name="tell-everyone" id="tell-everyone">
<label for="description">Tell Everyone?</label>
<br>
<input v-model="checked" type="checkbox" name="status" id="status" value="checked">
<label for="checked">Resolved and Tested?</label>
</div>
</div>
<div class="modal-footer">
<button :disabled="!checked || !isComplete" id="log-it" type="button" class="btn btn-circle btn-xl" data-dismiss="modal" #click="save">
<span v-if="checked && isComplete" id="button-content"><b>LOG IT</b></span>
<span v-else id="button-content"><b>FIX IT</b></span>
</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
import moment from 'moment';
import ShowDetail from './ShowDetail.vue';
import EditLog from './EditLog.vue';
export default {
name:'Log',
data(){
return {
key:0,
logModalOpen: false,
editModalOpen: false,
isLoading:'',
checked:false,
tell:false,
logs: [],
log: {id:'', domain_id:'', title: '', type: '', description: ''},
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
errors:{},
//passing those variables to ShowDetails component
logId:'',
logUser:'',
logTitle:'',
logType:'',
logDescription:'',
logDate:'',
}
},
methods:{
getLogs(){
window.axios.get('/develogger-app/public/api/logs').then(({data})=>{
data.forEach(log =>{
this.logs.push(log)
});
});
},
moment,
save(){
this.isLoading = true;
if(this.log.title.length > 0 && this.log.domain_id.length > 0 && this.log.type.length > 0 && this.log.description.length > 0){
window.axios.post('/develogger-app/public/api/logs',this.log).then((response) => {
this.logs.push(response.data)
this.log.domain_id = '';
this.log.title = '';
this.log.type = '';
this.log.description = '';
this.checked = false;
setTimeout(() => {
this.isLoading = false;
}, 800);
}).catch((error) => this.errors = error.response.data.errors)
}
},
openLogs(id,user,title,type,description,date){
this.logId = id;
this.logUser = user;
this.logTitle = title;
this.logType = type;
this.logDescription = description;
this.logDate = date;
this.logModalOpen = true;
},
openEdit(id,user,title,type,description,date){
this.logId = id;
this.logUser = user;
this.logTitle = title;
this.logType = type;
this.logDescription = description;
this.logDate = date;
this.editModalOpen = true;
},
close(){
this.logModalOpen = false;
this.editModalOpen = false;
},
deleteLog(key,id){
if(confirm("Are you sure you want to delete?")){
this.isLoading = true;
window.axios.delete(`/develogger-app/public/api/logs/${id}`,this.log).then((response) => {
this.logs.push(response.data)
this.logs.splice(key,1);
setTimeout(() => {
this.isLoading = false;
}, 800);
this.logId = '';
}).catch((error) => this.errors = error.response.data.errors)
}
}
},
created(){
this.getLogs();
},
computed: {
isComplete () {
return this.log.title && this.log.domain_id && this.log.type && this.log.description;
}
},
components:{
'log-show-detail': ShowDetail,
'log-edit': EditLog,
}
}
</script>
Change:
v-for="log in logs" to "v-for="(log, index) in logs"
<td #click="deleteLog(key, log.id)"... to <td #click="deleteLog(index, log.id)"
<tbody>
<tr v-for="(log, index) in logs" :key="log.id" :log="log" #deleteLog="deleteLog" class="tr-table">
<td class="client-name" #click="openLogs(log.id,log.user,log.title,log.type,log.description,log.created_at)">{{ log.title }}</td>
<td class="" >{{ log.user }}</td>
<td class="client-pm" style="text-align:right">{{moment(log.created_at).fromNow()}}</td>
<!-- <td #click="openEdit(log.id,log.user,log.title,log.type,log.description,log.created_at)"><i class="fas fa-edit"></i></td> -->
<td #click="deleteLog(index,log.id)"><i class="fas fa-trash-alt"></i></td>
</tr>
</tbody>

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 to edit and update form input values in vue.js?

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.

Categories