How can I assign a key index in array with Vuejs - laravel

Hi I have a form like this:
<tr v-for="(post, index) in posts" v-bind:index="index">
<td>{{ post.rut }}</td>
<td>{{ post.names }} {{ post.father_lastname }} {{ post.mother_lastname }}</td>
<td>
<input type="number" class="form-control" id="exampleInputEmail1" v-model="form.amount[index]" placeholder="Ingresa el monto">
</td>
</tr>
How you can see it has v-model="form.amount[index]" but what I want to do it's this:
<input type="number" class="form-control" id="exampleInputEmail1" v-model="form.amount[post.rut]" placeholder="Ingresa el monto">
I mean I ant to assign my own index my custom index I wonder how can I do that??
My vuejs code is this:
data: function() {
return {
form: {
amount: [],
},
I declared amount array in vuejs how you can see above but I need to assign my own index in array because if I send the data and I check the array in this moment, it is:
0 => value, 1 => value
but I need to do this
'2714155' => value, '4578745' => value...
how can I do that? Thanks

Declare your data as an object, then assign value to specific keys.
data() {
return {
form: {
amount: {},
},
}
}
You can use your desired layout as is.
<input
type="number"
class="form-control"
id="exampleInputEmail1"
v-model="form.amount[post.rut]" <-- here you assign a specific key
placeholder="Ingresa el monto">

You can achieve this by using an object instead of an array, as javascript doesn't have associative array's like php. You can achieve an associative array with the help of objects instead.
See the working example below:
Vue.config.productionTip = false
Vue.config.devtools = false
new Vue({
el: '#app',
data: {
posts: [{
rut: 2714155,
names: 'John',
father_lastname: 'Doe',
mother_lastname: 'Foo'
},
{
rut: 4578745,
names: 'Lion',
father_lastname: 'Doe',
mother_lastname: 'Bar'
}
],
form: {
amount: {
'2714155': 1,
'4578745': 2
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<tr v-for="post in posts" :key="post.rut">
<td>{{ post.rut }}</td>
<td>{{ post.names }} {{ post.father_lastname }} {{ post.mother_lastname }}</td>
<td>
<input type="number" class="form-control" id="exampleInputEmail1" v-model="form.amount[post.rut]" placeholder="Ingresa el monto">
<span>Model Value: {{ form.amount[post.rut] }}</span>
</td>
</tr>
</table>
</div>

Related

message: "Undefined index: SKU"

When i do dd($data); all data will shown but when i remove dd and simply submit my form then problem always come undefined index sku.
This is my controller for product attribute
public function addProductAttributes(Request $request){
if($request->isMethod('post')){
$data=$request->all();
foreach($data['sku'] as $key => $value){
if($value != null){
$attr = new ProductsAttribute();
$attr->product_id = $data['product_id'][$key];
$attr->sku = $data['sku'][$key];
$attr->stock=$data['stock'][$key];
$attr->price= $data['price'][$key];
$attr->size= $data['size'][$key];
$attr->status= $data['status'][$key];
$attr->save();
}
}
}
if($request->expectsJson()){
return response()->json([
'message'=>'Product has been Update Successfully',
]);
};
}
This is my Product Attribute Template. This is Dynamically Add / Remove input fields in Vue js so i can i add multiple data array
<form role="form" method="POST" #submit.prevent="addProductAttribute">
<table >
<tbody>
<tr v-for="(attr,index) in attribute" :key="index">
<td><input type="text" v-model="product_id" class="form-control"></td>
<td><input type="text" v-model="attr.sku" placeholder="SKU" class="form-control"></td>
<td><input type="text" v-model="attr.size" placeholder="Size" class="form-control"></td>
<td><input type="text" v-model="attr.stock" placeholder="Stock" class="form-control"></td>
<td><input type="text" v-model="attr.price" placeholder="Price" class="form-control"></td>
<td><input type="text" v-model="attr.status" placeholder="Status" class="form-control"></td>
<td><a #click="addAttribute()" v-show="index == attribute.length-1" title="Add More Product Attribute"><i class="fas fa-plus" style="color: #00b44e"></i></a> <a title="Remove" #click="removeAttribute(index)" v-show="index || ( !index && attribute.length > 1)"><i class="fas fa-minus" style="color: red"></i></a></td>
</tr>
</tbody>
<tfoot>
<tr><button type="submit" class=" btn-outline-primary btn-lg">Add</button></tr>
</tfoot>
</table>
</form>
and my Script file is
<script>
export default {
mounted() {
let app = this;
let id = app.$route.params.id;
app.product_id = id;
this.readProductAttribute();
},
data(){
return{
product_id:'',
product_name:'',
attributes:[],
product:'',
attribute:[
{
product_id:'',
sku:'',
price:'',
stock:'',
size:'',
status:'',
}
]
}
},
methods: {
removeAttribute(index){
this.attribute.splice(index, 1);
},
addAttribute(){
this.attribute.push({ size: '',sku:'',price:'',stock:'',status:'' });
},
addProductAttribute: function(e){
axios.post(`/admin/add-product-attribute`,{
myArray: this.attribute
})
.then(function(response){
console.log(response);
})
}
}
}
Your data lies within the key 'myArray' so changing the loop to the following should help on your error.
foreach($data['myArray'] as $key => $value){
The access logic for each attr is also flawed. You should only access it on their key, secondly it seems like product_id is not always there in your data, so you can use nullcoalescent to return empty string if key is not there.
$attr->product_id = $data['product_id'] ?? '';
$attr->sku = $data['sku'] ?? '';
$attr->stock=$data['stock'] ?? '';
$attr->price= $data['price'] ?? '';
$attr->size= $data['size'] ?? '';
$attr->status= $data['status'] ?? '';

Data are not loaded in Edit Form in Vue app

I'm experimenting with Vue.js and Axios with Laravel. I'm using this tutorial where a simple Posts app is build: https://pusher.com/tutorials/laravel-vue-axios. I'm trying to extend this with an Update function. When I'm clicking on the edit button near a Post, the right Id is fetched. But in my Edit form the data of the post aren't loading. What could be the issue?
This is my code in EditPost.vue:
<template>
<form action="" #submit="editPost(post)">
<h4 class="text-center font-weight-bold">Post edit form</h4>
<div class="form-group">
<input type="text" placeholder="title" class="form-control"> {{ post.title }}
</div>
<div class="form-group">
<textarea placeholder="content" class="form-control" v-model="post.content">
</textarea>
</div>
<div class="form-group">
<button :disabled="!isValid" class="btn btn-block btn-primary" #click.prevent="updatePost(post)">Update
</button>
</div>
</form>
</template>
<script>
import {mapGetters, mapActions} from 'vuex'
export default {
name: "EditPost",
data() {
return {
post:{}
}
},
created () {
this.fetchData();
},
mounted() {
this.$store.dispatch('fetchPost')
},
methods: {
...mapActions('post', [
'fetchPost',
'updatePost'
]),
updatePost(post) {
this.$store.dispatch('updatePost', post)
},
fetchData: function () {
var _this = this;
// ajax call - then
_this.$store.commit('setData', {
name: 'post',
data: res.data.post
});
}
},
computed: mapGetters([
'posts'
])
}
</script>
This is the code in recources/js/store/actions.js:
fetchPost({commit}, post) {
axios.get(`/api/posts/${post.id}`)
.then(res => {
commit('FETCH_POST', res.data)
}).catch(err => {
console.log(err)
})
},
UPDATE: I've put in extra code.
file Posts.vue:
<template>
<div>
<h4 class="text-center font-weight-bold">Posts</h4>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Title</th>
<th scope="col">Content</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="post in posts">
<td>{{post.title}}</td>
<td>{{post.content}}</td>
<td>
<button class="btn btn-info" #click="editPost(post)"><i style="color:white" class="fa fa-edit"></i></button>
<button class="btn btn-danger" #click="deletePost(post)"><i style="color:white" class="fa fa-trash"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
name: "Posts",
mounted() {
this.$store.dispatch('fetchPosts')
},
methods: {
editPost(post) {
this.$store.dispatch('fetchPost',post)
},
deletePost(post) {
this.$store.dispatch('deletePost',post)
}
},
computed: {
...mapGetters([
'posts'
])
}
}
</script>
file recources/js/store/getters.js:
let getters = {
posts: state => {
return state.posts
}
}
export default getters
file recources/js/store/state.js:
let state = {
posts: []
}
export default state
file recources/js/store/mutations.js:
let mutations = {
CREATE_POST(state, post) {
state.posts.unshift(post)
},
FETCH_POSTS(state, posts) {
return state.posts = posts
},
UPDATE_POST(state, post) {
let index = state.posts.findIndex(item => item.id === post.id)
},
DELETE_POST(state, post) {
let index = state.posts.findIndex(item => item.id === post.id)
state.posts.splice(index, 1)
}
}
export default mutations
You are using post.content, which is coming from your data prop post. I don't see you adding content to post anywhere in your code.
I'm assuming your getter posts is getting the post data from the store.
So maybe you just need to use that instead?
v-model="posts.content">
Without seeing more of your code I cannot tell you exactly what to do. But your main problem is after you update the store value you need to get that value somehow.

Is there a way to update my component dynamically without refreshing the page?

I have a vue component which builds the card up, and within that component is another component that I pass data to with a prop I currently use Swal to as a popup to add a new project to the database however when it finishes adding I have to refresh the page for the data to be visible. The entire reason I wanted to use vue was to not have to refresh the page to view updated data and I haven't been able to figure it out.
This is my Projects.vue
import ProjectItem from './Projects/ProjectItem.vue';
export default {
name: "Projects",
components: {
ProjectItem
},
data() {
return {
projects: []
}
},
methods: {
getProjects () {
axios.get('/api/projects').then((res) => {this.projects = res.data});
},
addProject() {
Swal.queue([{
title: 'Add a New Project?',
html:
'<label for="name" style="color: #000;font-weight: 700">Project Name<input id="name" class="swal2-input"></label>' +
'<label for="description" style="color: #000;font-weight: 700">Project Description<textarea id="description" rows="5" cols="15" class="swal2-input"></textarea></label>',
showCancelButton: true,
confirmButtonText: 'Create Project',
showLoaderOnConfirm: true,
preConfirm: (result) => {
return new Promise(function(resolve, reject) {
if (result) {
let name = $('#name').val();
let desc = $('#description').val();
axios.post('/api/projects', {title:name,description:desc})
.then(function(response){
Swal.insertQueueStep({
type: 'success',
title: 'Your project has been created!'
})
resolve();
})
.catch(function(error){
Swal.insertQueueStep({
type: 'error',
title: 'Something went wrong.'
})
console.log(error);
reject();
})
}
});
}
}])
}
},
mounted () {
this.getProjects();
}
I bind it to ProjectItem in my Project.vue template:
<div class="table-responsive border-top">
<table class="table card-table table-striped table-vcenter text-nowrap">
<thead>
<tr>
<th>Id</th>
<th>Project Name</th>
<th>Team</th>
<th>Date</th>
<th>Preview</th>
</tr>
</thead>
<project-item v-bind:projects="projects" />
</table>
and this is my ProjectItem.vue:
<template>
<tbody>
<tr v-for="project in projects" :key="project.id">
<td>{{ project.id }}</td>
<td>{{ project.title }}</td>
<td><div class="avatar-list avatar-list-stacked">
{{ project.description }}
</div>
</td>
<td class="text-nowrap">{{ project.updated_at }}</td>
<td class="w-1"><i class="fa fa-eye"></i></td>
</tr>
</tbody>
</template>
<script>
export default {
name: "ProjectItem",
props: ["projects"],
}
</script>
You must insert the recently added project to the products array.
If you are able to change the backend code, you could change the response to include the project.
this.projects.push(response.project);

Dynamic class concate in v-for loop

This is the my for loop:
<tr v-for="doc in documents">
<th></th>
<th><i class="fas fa-doc.type fa-lg"></i></th>
<td>{{ doc.name }}</td>
</tr>
I have a doc.type which is either folder or file. I want to dynamically change fa icon like concatenate 'fa-' with doc.type. Is it possible?
Use binding and a method to return the formatted class name.
Template:
<i :class="getClassName(doc.type)"></i>
Vue -
using a method:
...
methods: {
getClassName(type){
return 'fas fa-' + type + ' fa-lg';
}
}
Or using a computed property:
...
computed: {
getClassName() {
return type => `fas fa-${doc.type} fa-lg`;
}
}
Alternative would be to do something like this (if using ES6+):
<i :class="`fas fa-${doc.type} fa-lg`"></i>
Try something like this:
<div v-for="doc in documents" :key="doc.id">
<th></th>
<th>
<a href="javascript:void(0)" #click="getChildDocs(doc.id)" :title="doc.type">
<i :class="{'fas': true, 'fa-file': doc.type == 'file', 'fa-dir': doc.type == 'dir', 'fa-lg': true}"></i>
</a>
</th>
<td>{{ doc.name }}</td>
</div>
Read here https://v2.vuejs.org/v2/guide/class-and-style.html
There are many ways to achieve this, here's one:
data() {
return {
iconTypes: {
'folder': 'fa-folder',
'file': 'fa-file'
}
}
},
methods: {
executeCommand(doc) {
if (doc.type === 'file') {
this.$emit('file-event-handler', any_arguments_here);
// or simply this.doSomething(doc)
}
// or use a switch here
}
}
<a href="#" #click.prevent="executeCommand(doc)"
<i class="[ 'fas fa-lg', iconTypes[doc.type] ] "></i>
</a>

VueJS linked inputs

I would like to link multiple inputs via VueJS and compute the values.
The inputs are to track vehicle mileage, and therefore any given day's value, cannot be less than the previous day's value.
So far I have come up with the following (it's very convoluted and I know it can be tidied up but I'll improve on that later). It doesn't work, as you can't change any value apart from Monday Start.
https://jsfiddle.net/mstnorris/qbgtpm34/1/
HTML
<table id="app" class="table">
<thead>
<tr>
<th>Day</th>
<th>Start</th>
<th>End</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Mon</th>
<td>
<input type="number" class="form-control" v-model="mon_start" number id="mon_start" placeholder="Monday Start Mileage">
</td>
<td>
<input type="number" class="form-control" v-model="mon_end" number id="mon_end" placeholder="Monday End Mileage">
</td>
</tr>
<tr>
<th scope="row">Tue</th>
<td>
<input type="number" class="form-control" v-model="tue_start" number id="tue_start" placeholder="Tuesday Start Mileage">
</td>
<td>
<input type="number" class="form-control" v-model="tue_end" number id="tue_end" placeholder="Tuesday End Mileage">
</td>
</tr>
<tr>
<th scope="row">Wed</th>
<td>
<input type="number" class="form-control" v-model="wed_start" number id="wed_start" placeholder="Wednesday Start Mileage">
</td>
<td>
<input type="number" class="form-control" v-model="wed_end" number id="wed_end" placeholder="Wednesday End Mileage">
</td>
</tr>
<tr>
<th scope="row">Thu</th>
<td>
<input type="number" class="form-control" v-model="thu_start" number id="thu_start" placeholder="Thursday Start Mileage">
</td>
<td>
<input type="number" class="form-control" v-model="thu_end" number id="thu_end" placeholder="Thursday End Mileage">
</td>
</tr>
<tr>
<th scope="row">Fri</th>
<td>
<input type="number" class="form-control" v-model="fri_start" number id="fri_start" placeholder="Friday Start Mileage">
</td>
<td>
<input type="number" class="form-control" v-model="fri_end" number id="fri_end" placeholder="Friday End Mileage">
</td>
</tr>
<tr>
<th scope="row">Sat</th>
<td>
<input type="number" class="form-control" v-model="sat_start" number id="sat_start" placeholder="Saturday Start Mileage">
</td>
<td>
<input type="number" class="form-control" v-model="sat_end" number id="sat_end" placeholder="Saturday End Mileage">
</td>
</tr>
<tr>
<th scope="row">Sun</th>
<td>
<input type="number" class="form-control" v-model="sun_start" number id="sun_start" placeholder="Sunday Start Mileage">
</td>
<td>
<input type="number" class="form-control" v-model="sun_end" number id="sun_end" placeholder="Sunday End Mileage">
</td>
</tr>
</tbody>
</table>
VueJS
new Vue({
el: "#app",
data: {
mon_start: '',
mon_end: '',
tue_start: '',
tue_end: '',
wed_start: '',
wed_end: '',
thu_start: '',
thu_end: '',
fri_start: '',
fri_end: '',
sat_start: '',
sat_end: '',
sun_start: '',
sun_end: ''
},
computed: {
mon_end: function() {
return this.mon_start
},
tue_start: function () {
return this.mon_end
},
tue_end: function() {
return this.tue_start
},
wed_start: function () {
return this.tue_end
},
wed_end: function() {
return this.wed_start
},
thu_start: function () {
return this.wed_end
},
thu_end: function() {
return this.thu_start
},
fri_start: function () {
return this.thu_end
},
fri_end: function() {
return this.fri_start
},
sat_start: function () {
return this.fri_end
},
sat_end: function() {
return this.sat_start
},
sun_start: function () {
return this.sat_end
},
sun_end: function() {
return this.sun_start
}
}
})
Why does this not work so far?
You are mixing data property names with computed properties, so the value will be read from the computed property (get) and it will try to write it back to the same computed property, which wont work since you haven't assigned a setter.
Also, always showing the value of mon_start in mon_end etc. will not allow you to show the updated value of mon_end in mon_end.
Computed: Getters and Setters
What you actually want is a custom action to happen when a new value is set (e.g. mon_start is set to 2, thus all other fields should be set to 2).
Now we could just say: If mon_start is updated, update mon_end. If mon_end is updated, update tue_start. And so on.
This can be achieved using computed setters:
mon_start_model: {
get: function() {
return this.mon_start
},
set: function(val) {
this.mon_start = val
this.mon_end_model = this.mon_start
}
},
I've simplified the example a little bit (using only Monday and Tuesday).
Avoiding confusing the user
As an extra condition for user-friendliness, you probably only want the next value to update if the next value is smaller or equal to the previous value.
Eg. mon_start is 1 at the beginning, then you update mon_end to 5, then you update mon_start to 3. You probably want mon_end to keep the value 5, right?
new Vue({
el: "#app",
data: {
mon_start: 0,
mon_end: 0,
tue_start: 0,
tue_end: 0,
},
computed: {
mon_start_model: {
get: function() {
return this.mon_start
},
set: function(val) {
this.mon_start = val
this.mon_end_model = Math.max(this.mon_end, this.mon_start)
}
},
mon_end_model: {
get: function() {
return this.mon_end
},
set: function(val) {
this.mon_end = val
this.tue_start_model = Math.max(this.tue_start, this.mon_end)
}
},
tue_start_model: {
get: function () {
return this.tue_start
},
set: function(val) {
this.tue_start = val
this.tue_end_model = Math.max(this.tue_end, this.tue_start)
}
},
tue_end_model: {
get: function() {
return this.tue_end
},
set: function(val) {
this.tue_end = val
}
}
}
})
https://jsfiddle.net/qbgtpm34/5/

Resources