I want to access json data from external file using vue component but I am not able to get any output in web page.The below is my code which I have tried.Can anyone help me out?
The below is Json data that included the models which I want to display on web page
{
"models": [
{
"title": "IRIS",
"project": "ABC",
"category": "SINGLES",
"bedrooms": 3
},
{
"title": "LILAC",
"project": "ABC",
"category": "DOUBLE",
"bedrooms": 4
},
{
"title": "ASTER",
"project": "ABC",
"category": "SINGLES",
"bedrooms": 4
}
]
}
Vue.component('single-model', {
data: function() {
return {
myData: []
}
},
template: `<div v-for="model in myData">
<p>{{model.title}}</p>
<hr>
</div>`,
created: function() {
this.fetchData();
},
methods: {
fetchData: function() {
var url = 'j.json';
axios.get(url)
.then(function(res) {
this.myData = res.data.models;
});
}
}
});
var vm = new Vue({
el: '#app',
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<div id="app">
<single-model></single-model>
</div>
As you might have noticed white running the provided snippet, template can have only one child element, using a v-for on the outermost element will create multiple children.
this in your case is not referring to the vue-component in fetchData function.
methods:{
fetchData() {
var url = '';
axios.get(url)
.then((res) => {
this.myData = res.data;
});
}
},
Try replacing with the above snippet in your code.
this on your code is not referring to your Vue Component.
I think, the easiest way to solve this issue is by creating a new variable to refer to your Vue Component
fetchData: function() {
var url = 'j.json';
var self = this;
axios.get(url)
.then(function(res) {
self.myData = res.data.models;
});
}
Related
I need to load data to a hands-on table,
When I use:
case: if used directly into data, its work good, but I need to load data when is created from Axios, using Axios. This doesn't work.
data: function() {
return {
info:[],
hotSettings: {
data: [['a','b','c'],['ra','rb','rc']],
}
}
}
case: if use in my variable info, it doesn't work either.
data: function() {
return {
info:[['a','b','c'],['ra','rb','rc']],
hotSettings: {
data: this.info,
}
}
}
case: using hook created. This doesn't work.
<template>
<div>
<hot-table ref="hotTableComponent" :settings="hotSettings"></hot-table>
</div>
</template>
<script>
import { HotTable } from '#handsontable/vue';
import Handsontable from 'handsontable';
export default {
created: function (){
this.newData()
},
data: function() {
return {
info:[],
hotSettings: {
data: this.info,
colHeaders: ['ID','Name',' pain'],
rowHeaders: true,
minRows: 2,
minCols: 3,
}
}
},
methods: {
newData() {
//dont work 1rs,
this.info = ['a','b','c'],['ra','rb','rc']];
// don't work, change 2dn
// let urlsecciones = 'seccion/show';
// axios.get(urlsecciones).then(response => {
// this.info = response.data;
// console.log(response.data) // run good
// });
}
},
components: {
HotTable
}
}
</script>
You canĀ“t reference data properties between them, instead you can use a computed property to handle what you want:
new Vue({
el: "#app",
created: function (){
this.newData()
},
data() {
return {
info: [],
}
},
computed:{
hotSettings(){
return {
data: this.info,
colHeaders: ['ID','Name',' pain'],
rowHeaders: true,
minRows: 2,
minCols: 3,
}
}
},
methods: {
newData() {
this.info = [
["a", "b", "c"],
["ra", "rb", "rc"]
]
// Handle Axios logic here
}
},
components: {
'hottable': Handsontable.vue.HotTable
}
});
<div id="app">
<HotTable :settings="hotSettings"></HotTable>
</div>
Jsfiddle: https://jsfiddle.net/hansfelix50/069s1x35/
I am using the Vue Tribute component https://github.com/syropian/vue-tribute
When initially loading the page when the "show" data property is set to true I get "No Match!". However if I set the "show" data property to false on page load then set it to true manually I will get the two results as expected. I have tried to wrap the function call to getTributeOptions() inside of "mounted, created and updated" but I receive the same results. I am using the setTimeout() to mimic the AJAX call I am using to load the remote data.
var app = new Vue({
el: '#myApp',
data: function() {
return {
show: true,
tributeOptions: {
values: []
}
};
},
mounted: function() {
this.getTributeOptions();
},
methods: {
getTributeOptions: function(resource) {
var vm = this;
setTimeout(function() {
vm.tributeOptions.values = [
{ key: 'Phil Heartman', value: 'pheartman' },
{ key: 'Gordon Ramsey', value: 'gramsey' }
];
}, 500)
}
}
})
<div id="myApp">
<div v-if="show">
<vue-tribute :options="tributeOptions">
<input type="text" placeholder="#" />
</vue-tribute>
</div>
</div>
https://codepen.io/anon/pen/QBQaNB?editors=1111
I found the answer on this question: Vuejs mount the child components only after data has been loaded
Updated Code:
var app = new Vue({
el: '#myApp',
data: function() {
return {
userDataLoaded: false,
tributeOptions: {
values: []
}
};
},
mounted: function() {
this.getTributeOptions();
},
methods: {
getTributeOptions: function(resource) {
var vm = this;
setTimeout(function() {
vm.tributeOptions.values = [
{ key: 'Phil Heartman', value: 'pheartman' },
{ key: 'Gordon Ramsey', value: 'gramsey' }
];
vm.dataLoaded = true;
}, 500)
}
}
})
<div id="myApp">
<template>
<template v-if="dataLoaded">
<vue-tribute :options="tributeOptions">
<input type="text" placeholder="#" />
</vue-tribute>
</template>
</template>
</div>
While your workaround above would probably work, the problem lays in the library you use
In https://github.com/syropian/vue-tribute/blob/master/src/index.js#L19
mounted() {
const $el = this.$slots.default[0].elm;
this.tribute = new Tribute(this.options);
...
}
The options value is only used once in mounted(), and there is no handler for updating the values when the options are changed.
A better way to do it would be to watch for changes in this.options, and update the value inside the component respectively.
Check Vue Tribute source code at Github, you will see it will only create one new Tribute instance in mounted(). That means even you change the value of props=options once mounted, it will not affect anything.
So one solution is make sure tributeOptions is ready before mount, so update the value in created() will be an idea.
var app = new Vue({
el: '#myApp',
data: function() {
return {
tributeOptions: {
values: []
}
};
},
created: function () {
this.tributeOptions.values = [
{ key: 'Phil Heartman', value: 'pheartman' },
{ key: 'Gordon Ramsey', value: 'gramsey' }
]
},
mounted: function() {
//this.getTributeOptions();
},
methods: {
getTributeOptions: function(resource) {
var vm = this;
setTimeout(function() {
vm.tributeOptions.values = [
{ key: 'Phil Heartman', value: 'pheartman' },
{ key: 'Gordon Ramsey', value: 'gramsey' }
];
}, 500)
}
}
})
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<script src="https://unpkg.com/vue-tribute"></script>
<div id="myApp">
<vue-tribute :options="tributeOptions">
<input type="text" placeholder="#" />
</vue-tribute>
</div>
another solution is download the source codes for Vue Tribute in Github, then implement update Tribute instance by yourself.
Update: create one pull request which implement update Tribute options.
the third solution will be force re-mount by bind different key every time once tributeOptions is updated:
like below demo.
var app = new Vue({
el: '#myApp',
data: function() {
return {
tributeOptions: {
values: []
},
tributeKey: 0
};
},
mounted: function() {
this.getTributeOptions();
},
methods: {
getTributeOptions: function(resource) {
var vm = this;
setTimeout(function() {
vm.tributeOptions.values = [
{ key: 'Phil Heartman', value: 'pheartman' },
{ key: 'Gordon Ramsey', value: 'gramsey' }
];
vm.tributeKey+=1
}, 500)
}
}
})
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<script src="https://unpkg.com/vue-tribute"></script>
<div id="myApp">
<vue-tribute :options="tributeOptions" :key="tributeKey">
<input type="text" placeholder="#" />
</vue-tribute>
</div>
Im absolutely new in Vue framework and I need create reusable component with live BTC/LTC/XRP price
For live prices Im using Bitstamp websockets API. Here is example usage with jQuery - run this snippet, is really live.
var bitstamp = new Pusher('de504dc5763aeef9ff52')
var channel = bitstamp.subscribe('live_trades')
channel.bind('trade', function (lastTrade) {
$('p').text(lastTrade.price)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pusher/4.1.0/pusher.min.js"></script>
<h3>BTC/USD price</h3>
<p>loading...</p>
As you can see, its really simple. But, I need to use Vue.js component. So I created this, and its also fully functional:
var bitstamp = new Pusher('de504dc5763aeef9ff52')
Vue.component('live-price', {
template: '<div>{{price}}</div>',
data: function () {
return {
price: 'loading...'
}
},
created: function () {
this.update(this)
},
methods: {
update: function (current) {
var pair = current.$attrs.pair === 'btcusd'
? 'live_trades'
: 'live_trades_' + current.$attrs.pair
var channel = bitstamp.subscribe(pair)
channel.bind('trade', function (lastTrade) {
current.price = lastTrade.price
})
}
}
})
new Vue({
el: '.prices'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/pusher/4.1.0/pusher.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.1/vue.min.js"></script>
<section class="prices">
<live-price pair="btcusd"></live-price>
<live-price pair="ltcusd"></live-price>
<live-price pair="xrpusd"></live-price>
</section>
But, there is big BUT. Am I using Vue right way? WHERE IS IDEAL PLACE to run Pusher? In "created" or "mounted" method? In "computed"? In "watch"? Or where? Am i doing it right? I really dont known, I started with Vue ... today :(
Looks pretty good for your first day using Vue! I would just make a few changes.
The component is reaching out and using a global, bitstamp. Generally with components, you want them to be independent, and not reaching out of themselves to get values. To that end, declare the socket as a property that can be passed in to the component.
Likewise, the pair is passed in as a property, but you do not declare it and instead, use current.$attrs.pair to get the pair. But that's not very declarative and makes it harder for anyone else to use the component. Moreover, by making it a property, you can reference it using this.pair.
When using something like a socket, you should always remember to clean up when you are done using it. In the code below, I added the unsubscribe method to do so. beforeDestroy is a typical lifecycle hook to handle these kinds of things.
Computed properties are useful for calculating values that are derived from your components data: the channel you are subscribing to is a computed property. You don't really need to do this, but its generally good practice.
A Vue can only bind to a single DOM element. You are using a class .prices which works in this case because there is only one element with that class, but could be misleading down the road.
Finally, created is an excellent place to initiate your subscription.
console.clear()
var bitstamp = new Pusher('de504dc5763aeef9ff52')
Vue.component('live-price', {
props:["pair", "socket"],
template: '<div>{{price}}</div>',
data() {
return {
price: 'loading...',
subscription: null
}
},
created() {
this.subscribe()
},
beforeDestroy(){
this.unsubscribe()
},
computed:{
channel(){
if (this.pair === 'btcusd')
return 'live_trades'
else
return 'live_trades_' + this.pair
}
},
methods: {
onTrade(lastTrade){
this.price = lastTrade.price
},
subscribe() {
this.subscription = this.socket.subscribe(this.channel)
this.subscription.bind('trade', this.onTrade)
},
unsubscribe(){
this.subscription.unbind('trade', this.onTrade)
this.socket.unsubscribe(this.channel)
}
}
})
new Vue({
el: '#prices',
data:{
socket: bitstamp
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/pusher/4.1.0/pusher.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.1/vue.min.js"></script>
<section id="prices">
<live-price pair="btcusd" :socket="bitstamp"></live-price>
<live-price pair="ltcusd" :socket="bitstamp"></live-price>
<live-price pair="xrpusd" :socket="bitstamp"></live-price>
</section>
Rewrited - is it ok now?
var config = {
key: 'de504dc5763aeef9ff52'
}
var store = new Vuex.Store({
state: {
pusher: null
},
mutations: {
initPusher (state, payload) {
state.pusher = new Pusher(payload.key)
}
}
})
var livePrice = {
template: '#live-price',
props: ['pair'],
data () {
return {
price: 'loading...',
subscription: null
}
},
computed: {
channel () {
return this.pair === 'btcusd'
? 'live_trades'
: 'live_trades_' + this.pair
}
},
methods: {
onTrade (lastTrade) {
this.price = lastTrade.price
},
subscribe () {
this.subscription = this.$store.state.pusher.subscribe(this.channel)
this.subscription.bind('trade', this.onTrade)
},
unsubscribe () {
this.subscription.unbind('trade', this.onTrade)
this.$store.state.pusher.unsubscribe(this.channel)
}
},
created () {
this.subscribe()
},
beforeDestroy () {
this.unsubscribe()
}
}
new Vue({
el: '#prices',
store,
components: {
'live-price': livePrice
},
created () {
store.commit({
type: 'initPusher',
key: config.key
})
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/pusher/4.1.0/pusher.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.1/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.3.1/vuex.min.js"></script>
<section id="prices">
<live-price pair="btcusd"></live-price>
<live-price pair="ltcusd"></live-price>
<live-price pair="xrpusd"></live-price>
</section>
<template id="live-price">
<div>
{{price}}
</div>
</template>
I have a vue router with two components,a list and a detail view, with a link back to the list view. On the same page I have a list of the same data that is shown in the list-view component.
The data is fetched via ajax, and as such is not ready when the router-view is drawn. When the data is ready however, the non-router list is updated, but the router view is not.
How do I communicate to the router view that it should redraw with new data?
If I click on an item in the non-router list, the router-view changes to the details component as expected, and if I then click the "show list" it changes back to the list-component, but this time it is populated with the right data.
I have created a js-fiddle that contains the relevant code: https://jsfiddle.net/pengman/jkwvphf9/2/
(It fakes the ajax by using setTimeout, but the effect is the same)
Html code:
<div id="app">
Router-view:
<router-view class="view"></router-view>
<br>
Unrouted list:
<div class="list-group">
<router-link v-for="plante in planter" class="list-group-item" :to="{ name: 'plante', params: { nummer: plante.Nummer }}">{{ plante.Navn }} | </router-link>
</div>
</div>
<template id="plante-listing-template">
<ul>
<li v-for="plante in planter">
{{ plante.Navn }} <router-link :to="{ name: 'plante', params: { nummer: plante.Nummer }}">Vis</router-link>
</li>
</ul>
</template>
<template id="plante-detail-template">
<div>
plante detail template:
<h3>{{ plante.Navn }}</h3>
<router-link to="/">Show List</router-link>
</div>
<br>
</template>
Javascript code:
var PlanteListing = {
template: '#plante-listing-template',
data: function () {
return {
planter: this.$parent.planter
}
},
watch: {
'$route'(to, from) {
// vi skal opdatere data, saa vi skal beregne igen
this.planter = this.$parent.planter;
},
'dataloaded'() {
this.planter = this.$parent.planter;
}
}
};
var PlanteDetail = {
template: '#plante-detail-template',
data: function () {
var parent = this.$parent;
var nummerFromRoute = this.$route.params.nummer;
//console.log(nummerFromRoute);
var filtered = this.$parent.planter.filter(function (item) {
return (item.Nummer == nummerFromRoute) ? item : false;
});
return {
plante: filtered[0]
}
},
watch: {
'$route'(to, from) {
// vi skal opdatere data, saa vi skal beregne igen
var nummerFromRoute = this.$route.params.nummer;
var filtered = this.$parent.planter.filter(function (item) {
return (item.Nummer == nummerFromRoute) ? item : false;
});
this.plante = filtered[0];
},
}
};
var router = new VueRouter({
mode: 'hash',
base: window.location.href,
routes: [
{ path: '/', component: PlanteListing },
{ name: 'plante', path: '/:nummer', component: PlanteDetail }
]
});
var app = new Vue({
router,
data: {
planter: []
},
components: { PlanteListing: PlanteListing },
methods: {
getJson: function () {
var self = this;
/* Real code:
$.getJSON("content/planter.json", function (param) {
this.planter = param;
}.bind(this));
*/
/* Simulation code: */
setTimeout(function(){self.planter = [ { "Nummer": "0", "Navn": "Bertha Winters" }, { "Nummer": "1", "Navn": "Jeannie Small" }, { "Nummer": "2", "Navn": "Mckay Joyner" }, { "Nummer": "3", "Navn": "Janelle Banks" }, { "Nummer": "4", "Navn": "Bray Moran" }, { "Nummer": "5", "Navn": "Hooper Schwartz" }]; console.log('data loaded')}, 500);
}
},
created: function () {
this.getJson();
}
}).$mount('#app');
Typically you do not want to have a component reach out of itself to get data (as you are doing with this.$parent.planter). Instead, you want to pass it props. To that end, I've modified your code a bit.
The first thing is I upgraded your vue-router to the latest version. This allows you to use the props argument on routes.
var router = new VueRouter({
mode: 'hash',
base: window.location.href,
routes: [
{ path: '/', component: PlanteListing },
{ name: 'plante', path: '/:nummer', component: PlanteDetail, props: true }
]
});
Secondly, you are using planter in all your routes, so I have provided it as a property on the router-view.
<router-view class="view" :planter="planter"></router-view>
This allows us to clean up your component routes and add the data they need as props.
var PlanteListing = {
template: '#plante-listing-template',
props:["planter"]
};
var PlanteDetail = {
template: '#plante-detail-template',
props:["planter", "nummer"],
data: function () {
var filtered = this.planter.filter(item => item.Nummer == this.nummer);
return {
plante: filtered[0]
}
}
};
There is no need to tell the router to redraw; because the data are props, Vue just takes care of that for us.
Here is your updated fiddle.
Trying my hand at Polymer v1.0, Am currently loading 2 datasets. The first set will set the view and repeat my custom elements. The 2nd set is async loaded, upon loading it will try to match via the routeid if available. On match it will insert itself as an object into the 1st object.
When i do this, the view doesn't get updated, but if i do a console.log and trace it, the data is there.
I discovered if I were to clear the 1st object, use async and set it back later, the view gets updated, but this would cause my display to be blank for a while.
How can i force it to redraw with the updated content?
First one sets the view of items.
[
{
"anchors":[
{"anchorid":1,
"routes":[
{"name":"route 1", "routeid":1 },
{"name":"route 2", "routeid":2 },
{"name":"route 3", "routeid":3 }
]
},
{"anchorid":2,
"routes":[
{"name":"route 4", "routeid":4 },
{"name":"route 5", "routeid":5 },
{"name":"route 6", "routeid":6 }
]
}
]
},
{
"anchors":[
{"anchorid":3,
"routes":[
{"name":"route 7", "routeid":7 },
{"name":"route 8", "routeid":8 },
{"name":"route 9", "routeid":9 }
]
},
{"anchorid":4,
"routes":[
{"name":"route 10", "routeid":10 },
{"name":"route 11", "routeid":11 },
{"name":"route 12", "routeid":12 }
]
}
]
}
]
2nd dataset
[
{"routeid":3, "status":2},
{"routeid":5, "status":3},
{"routeid":1, "status":1}
]
My element codes
<dom-module id="anchor-listdebug">
<template>
<h1>Anchor list</h1>
<template is="dom-repeat" items="{{data}}">
<h2>Anchor Group </h2>
<template is="dom-repeat" items="{{item.anchors}}" as="anchordata">
<anchor-item anchor="{{anchordata}}"></anchor-item>
</template>
</template>
</template>
<script>
Polymer ({
is:"anchor-listdebug",
ready : function () {
//data is loaded here via iron-ajax
this.data = data;
//data2 is loaded on data response
this.data2 = data2;
// emulate 2nd set of data loading, if this.processData is called immediately, content updates fine.
this.async (this.processData, 1000);
},
processData: function () {
this.data.forEach (function (element) {
element.anchors.forEach (function (anchorElement){
anchorElement.routes.forEach (function (routeElement){
var myID = routeElement.routeid;
var data = this.data2.filter(function (record){
return record.routeid == myID;
});
if (data.length >0) {
data = data[0];
routeElement.data = data;
}
});
});
});
}
});
</script>
</dom-module>
<dom-module id="anchor-item">
<template>
<h2>Anchor ID: {{anchor.anchorid}} </h2>
<template is="dom-repeat" items="{{anchor.routes}}" as="routedata">
<route-item route="{{routedata}}"></route-item>
</template>
</template>
<script>
Polymer ({
is:"anchor-item",
properties: {
anchor:{ type:Object, notify:true}
}
});
</script>
</dom-module>
<dom-module id="route-item">
<template>
<h3>Route id: <span>{{route.routeid}}</span></h3>
<h3>Route name: <span>{{route.name}}</span></h3>
<h3>Route status: <span>{{route.data.status}}</span></h3>
</template>
<script>
Polymer ({
is:"route-item",
properties: {
route:{ type:Object, notify:true}
}
});
</script>
</dom-module>
Make sure that you use the set API for manipulating your objects (docs). Instead of this.data.anchors = someValue use this.set("data.anchors", someValue).
First, you will need to use this.set("data.anchors", someValue) as was previously stated. You will also need to change your template binding, or the resulting notification will be ignored. Instead of using anchor="{{anchordata}}", you'd either have to use anchor="{{anchordata.*}}" to listen to all record changes, or anchor="{{anchordata.SPECIFIC_KEY}}" if you want the changes to respond to just one specific key within the data.
Updated my forEach loops to include the index to use to construct the path to use for the set() function.
<script>
Polymer ({
is:"anchor-listdebug",
ready : function () {
//data is loaded here via iron-ajax
this.data = data;
//data2 is loaded on data response
this.data2 = data2;
this.async (this.processData, 1000);
//this.processData();
},
processData: function () {
var scope = this;
this.data.forEach (function (element, index1) {
element.anchors.forEach (function (anchorElement, index2){
anchorElement.routes.forEach (function (routeElement, index3){
var myID = routeElement.routeid;
var data = this.data2.filter(function (record){
return record.routeid == myID;
});
if (data.length >0) {
data = data[0];
var tpath = "data."+index1+".anchors."+index2+".routes."+index3+".data";
scope.set(tpath, data);
}
});
});
});
}
});
</script>