Pass JS object to component Vue.js - javascript

I am having trouble displaying product via product component.
First in my vue.js app, I load Products via ajax like so:
var app = new Vue({
el: '#app',
data: {
products: [] // will be loaded via Ajax
},
mounted: function () {
var self = this;
ajaxGetProducts(0, self); // ajax, to fetch products
},
methods: {
getProducts: function (event) {
let groupID = Number(document.getElementById("GroupSelect").value);
ajaxGetProducts(groupID, this);
}
}
});
//Ajax call to fetch Products
function ajaxGetProducts(groupID, self) {
$.ajax({
type: "POST",
url: "/Data/GetProducts",
data: { Id: groupID },
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
dataType: "json"
, success: function (response) {
self.products = response; // Loading products into the App instance
},
error: function (jqXHR, textStatus, errorThrown) {
self.products = [];
}
}).done(function () {
});
}
Then I display those produdcts, and it works just fine:
<!-- HTML -->
<div id="app">
<div v-for="prod in products" >{{prod.Id}}</div>
</div>
Question: if I want to use a component. How do I do that?
This is how my component looks so far:
Vue.component('product', {
props: [],
template: `<div>ProdID: {{product.Id}} {{product.Qty}}</div>`,
data() {
return {
Id: "test id"
}
}
})
Example Product object has following properties:
{
Id: 1,
Qty: 5,
Title: "Nike shoes",
Price: 200,
Color: "Green"
}
And eventually I would like to use it in HTML like so:
<!-- HTML -->
<div id="app">
<!-- need to pass prod object into product component -->
<div v-for="prod in products" >
<product></product>
</div>
</div>
I know that I have to pass the object via Component properties somehow?
Passing each property 1 by 1 is not a good idea, cause this product is subject to change, so property name can change, or be added more. I think there should be a way to pass a whole Product object to Product component somehow, right?

You can pass the information into your component via the props
something like this;
Vue.component('product', {
props: ['item'],
template: `<div>ProdID: {{item.Id}} {{item.Qty}}</div>`
})
and pass it on like this;
<div id="app">
<div v-for="prod in products" :key='prod.Id'>
<product :item='prod'></product>
</div>
</div>

What about passing it as
<product v-for="prod in products" :key="prod.Id" :product="prod"></product> and in the component: props: {product:{type: Object, required: true}}?
Then in the component template you can use things like {{product.Id}}

Related

Can't access sub-object in Vue JS 3

I'm pretty new in VueJS and am stuck on the following problem:
I have a template in which a click on a button loads a new template through router.push and also sends an ID as data along.
The template:
<template>
<div>
{{ content.description }} // Works fine
{{ content.subobject.logo }} // Logo not found
</div>
</template>
In the new template that is being loaded I have the following function for saving the received ID into data:
created: function() {
this.ID = this.$route.params.data.toString()
},
then I have a function in methods which uses this ID to get data via axios/get:
methods: {
getContent: function() {
axios.get("https://api-url.com/" + this.ID)
.then((response) => {
this.content = response.data.data
console.log(response);
}).catch(error => {
console.log("ERRR:: ", error.response.data)
});
}
},
this function is called in mounted:
mounted: function() {
this.getContent()
}
My data function:
data: function() {
return {
ID: '',
content: [] as any
}
The data that is being returned by the API looks something like this:
title: "this is the title"
description: "Lorem Ipsum"
subobject: {
logo: "Logo URL"
}
When i use {{ content.title }} or {{ content.description }} in my template it shows up fine. As soon as I use {{ content.subobject.logo }} I'll get an error "logo" not found.
The funny thing is, when I have the template opend up and add the {{ content.subobject.logo }} after the page has loaded, save it, and it hot-reloads, it shows up fine?!
It seems like the data is not "available" on first load - but how can that be, if {{ content.title }} works fine?
Thanks a lot for any ideas!
The data is initially not available which means that content is still without inner field values, in order to avoid that add a conditional rendering like :
<div v-if="content.subobject">{{ content.subobject.logo }}</div>
you could also do :
data: function() {
return {
ID: '',
content: null
}
}
<template>
<template v-if="content">
<div >{{ content.subobject.logo }}</div>
<div >{{ content.title }}</div>
</template>
</template>

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

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

Laravel vuejs get inital value

I want to autofill my input value with the value of my server, this works if I do not use vue, but since I use vue for other reasons it does not.
Vue overrides the value with an empty string how can my vue take that value and update it in my axios post.
<input type="text" id="title" name="title" value="{{ $snippet->title }}" v-model="title">
new Vue({
el: '#snippet-form',
data: {
title: '',
},
methods: {
publishSnippet (e) {
var self = this;
axios.post('/snippets', {
title: this.title,
})
.then(function (response) {
console.log("success");
})
.catch(function (error) {
console.log(error);
})
},
},
});
You have to use use props which allows you to pass a value to the component. The thing is, you can't use it on Vue root instance. You need to use components.
In your resources/assets/js/components/, create a folder/file SnipperForm/SnippetForm.vue.
SnippetForm.vue, will be like:
<template>
<form>
<input type="text"
id="title"
name="title"
v-model="title">
<button #click.prevent="publishSnippet()">Submit</button>
</form>
</template>
<script>
export default {
props: {
snippetTitle: {
type: String,
default: ''
},
},
data: {
title: '',
},
mounted() {
this.title = this.snippetTitle;
},
methods: {
publishSnippet () {
axios.post('/snippets', {
title: this.title,
})
.then(function (response) {
console.log("success");
})
.catch(function (error) {
console.log(error);
})
},
},
}
</script>
And finally, in your app.js:
Vue.component('snippet-form', require('SnippetForm/SnippetForm.vue'));
And your, again, in your app.js, edit your Vue instance:
const app = new Vue({
el: '#app',
});
And in your blade view, use the SnippetForm.vue component like:
<snippet-form snippet-title="{{ $snippet->title }}"><snippet-form>

Vuejs not updating list but updating obect

I am learning Vuejs and I am stuck. Why can I see the messages get added to the object (in Chrome Vue debugger) yet it is not added to the div that contains the list?
My Vue Component:
<template>
<div id="round-status-message" class="round-status-message">
<div class="row">
<div class="col-xs-12" v-for="sysmessage in sysmessages" v-html="sysmessage.message"></div>
</div>
</div>
</template>
<script>
export default {
props: ['sysmessages'],
methods: {
scrollToTop () {
this.$el.scrollTop = 0
}
}
};
</script>
My Vue instance:
$(document).ready(function()
{
Vue.component('chat-system', require('../components/chat-system.vue'));
var chatSystem = new Vue({
el: '#system-chat',
data: function () {
return {
sysmessages: []
};
},
created() {
this.fetchMessages();
Echo.private(sys_channel)
.listen('SystemMessageSent', (e) => {
this.sysmessages.unshift({
sysmessage: e.message.message,
});
this.processMessage(e);
});
},
methods: {
fetchMessages() {
axios.get(sys_get_route)
.then(response => {
this.sysmessages = response.data;
});
},
processMessage(message) {
this.$nextTick(() => {
this.$refs.sysmessages.scrollToTop();
});
// updateGame();
}
}
});
});
My template call in HTML:
<div id="system-chat">
<chat-system ref="sysmessages" v-on:systemmessagesent="processMessage" :sysmessages="sysmessages" :player="{{ Auth::user() }}"></chat-system>
</div>
There are no compile or run time errors and I can see records added to the props in the vue chrome tool. I can also see empty HTML elements added to the div.
What have I missed?
UPDATE: My record structures:
response.data is an array of objects, each like this:
{"data":[
{"id":100,
"progress_id":5,
"message":"start message",
"action":"welcome"
},
{"id"....
e.message.message contains the text message entry, so just a string.
I am trying to access the message variable in each object during the fetchMessages method.
You're adding objects with sysmessage as the property.
this.sysmessages.unshift({
sysmessage: e.message.message,
});
But you are trying to view
v-for="sysmessage in sysmessages" v-html="sysmessage.message"
Based on your update, the code should be:
this.sysmessages.unshift({
message: e.message.message,
});
And you can leave the template as
v-html="sysmessage.message"

How to Re-bind / Refresh Dom after ajax call in vuejs2 (like $scope.$apply in angularjs)?

I am using vue.js 2 with webpack template. i am new to vue.js.
i want to bind DOM after data is received in ajax call.
here in my code contains v-for in which the response html is need to bind with v-html="tHtml[index]" every time after data is received from api call.
What can be used to re-bind or refresh view/DOM as we use $scope.$apply() in angularjs.
home.vue
<template>
<div class="row" id="home">
<h3 v-if="msg"><span class="label label-warning">{{msg}}</span></h3>
</div>
<div v-for="obj in tdata">
<div v-html="tHtml[$index]"></div>
</div>
</div>
</template>
<script>
import appService from '../service'
import bus from '../service/bus'
export default {
created() {
appService.checkAuth();
console.log("service appService val: "+appService.user.authenticated);
//called after vue has been created
bus.$on('eventBusWithTopic', (data) => { //fetch event bus on ready to get data
//alert("event fetched! "+data.user_id);
console.log("event fetched! "+data.user_id);
this.searchedTopic = data.topic;
this.user_id = data.user_id;
bus.$off('eventBusWithTopic'); //un-bind event after use
//check details
})
},
data() {
return {
searchedTopic: '',
user_id: '',
tdata: {},
tHtml: []
}
},
methods: {
getdata(){
console.log("dfd "+this.user_id);
appService.getdata(this, this.user_id, this.searchedTopic).success((res) => {
//console.log(" res: "+JSON.stringify(res));
this.tdata = res.data;
if(this.tdata.length > 0){
//**GET HTML** // <<--==--==--==
for(var i=0;i<this.tdata.length;i++){
this.getTemplate(this.tdata[i].id_str);
}
}
if(res.success == 0)
this.msg = res.message;
}).error((e) => console.log("error while getting data: "+JSON.stringify(e)))
},
getTemplate(id){
this.$http.get("https://uqkcgyy8wb.execute-api..*****../user/"+id+"/getHtml", (resp) => {
//return resp.data.html;
this.tHtml[this.tHtml.length] = resp.data.html;
console.log(this.tHtml);
}).error((e) => console.log("error while getting html: "+JSON.stringify(e)))
}
}
}
</script>

Categories