I am quite new to Vue and am attempting to retrieve a JSON response from an API and then print this on my page.
This is what I have so far:
<body>
<div id="mystats" class="container">
<h1>My Top Tracks</h1>
<ol>
<li v-for="track in tracks">#{{ track.name }}</li>
</ol>
<pre>#{{ tracks|json }}</pre>
<button v-on:click="fetchStats">Fetch stats</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-resource/1.0.3/vue-resource.min.js"></script>
<script type="text/javascript">
$vue = new Vue({
el: '#mystats',
data: {
tracks: [],
},
methods: {
fetchStats: function()
{
this.$http.get('/mystatsdata', {params: {type: 'tracks'}}).then((response) => {
this.tracks.push(response.body);
console.log(this.tracks.name);
}, (response) => {
// error callback
});
}
}
})
</script>
</body>
Below is the response I am getting back:
[
[
{
"name": "Falling Away - Acoustic (Bonus Track)",
"track_number": 8,
"type": "track",
}
]
]
The issue is that the:
<ol>
<li v-for="track in tracks">#{{ track.name }}</li>
</ol>
is not printing out the track name.
There's no error in my console, and so being a little new to Javascript and Vue.js I'm not really sure where I am going wrong.
How do I get the name to display?
Edit
Here is the response with more than one entry (limited to 2 currently):
[
[
{
"name": "Falling Away - Acoustic (Bonus Track)",
"track_number": 8,
"type": "track",
},
{
"name": "Perfect Strangers",
"track_number": 1,
"type": "track",
}
]
]
The response that you are getting back is an array containing another array - which in turn contains the actual objects representing your tracks.
So inside: <li v-for="track in tracks">#{{ track.name }}</li> , the track refers to the inside array and not to each object.
For quick-fix, you can change your code to:
<li v-for="track in tracks[0]">#{{ track.name }}</li>
and try.
But the proper fix would be to fix the backend, to return the result as a single array of objects.
As #craig_h suggested it looks like you're receiving an array of array of objects instead of an array of objects.
I would recommand you to send a better formatted json like this:
[
{
"name": "Falling Away - Acoustic (Bonus Track)",
"track_number": 8,
"type": "track",
},
{
"name": "Falling Away2 - Acoustic (Bonus Track)",
"track_number": 9
"type": "track",
}
]
If you don't have access to the backend, using this.tracks.push(response.body[0]) in your fetchStats method should do the trick.
Related
I'm currently having an issue accessing nested objects that are referred to through numbers. I made a service call to retrieve a JSON object, and then mapped each field to another object. I'll be using this object to display each field in the HTML.
My problem is occurring when I reach the nested objects. Here's an example :
{
"name": {
"title": "mr",
"first": "something",
"last": "something"
},
"role": "something",
"projects": {
"0": {
"title": "something",
"account": "something",
"steps": {
"total": 30,
"completed": 28
},
"dueDate": "2021-07-19 09:00:00",
"status": "fine"
},
"1": {
"title": "something",
"account": "something",
"steps": {
"total": 10,
"completed": 5
},
"dueDate": "2021-07-20 09:00:00",
"status": "fine"
},
}
}
The projects field gets tricky when trying to display all projects in the HTML. At the moment, I've created a person variable initialized to an empty array, and I add all of the fields from t he subscription to it. To solve this problem, I figured I should create a separate variable such as projects: any = []; and then set it to a new field in the person variable. Then iterate through it using an *ngFor, and display every project. Something like this
<li *ngFor="let project of person.projects">
{{ project }}
</li>
However that approach still doesn't reach the nested fields. How do I access the numbered objects here, and generally iterate through all of the nested fields?
Any advice will be very helpful. Thank you in advance.
I think you are looking for the KeyValuePipe
You would use it like this:
<li *ngFor="let item of person.projects | keyvalue">
{{ item.value }}
</li>
But based on your comment on doing this for more nested values, it might be worth flattening the data a bit in the component to simplify the template logic.
I am using Knockout.js as a way to dynamically update a view from a JSON response. The JSON looks like the following:
var data = {
"Properties": {
"Engine Capacity": "1499cc",
"Doors": 3,
"Extras": [
"LED lights",
"4WD"
],
"Model": "123a"
}
};
I have figured out a way in JavaScript to construct my <li> elements:
for (var field in data['Properties']) {
var value = data['Properties'][field];
var out = '<li>' + field + ': ' + value + '</li>';
console.log(out);
// <li>Engine Capacity: 1499cc</li>
// <li>Doors: 3</li>
}
I know this isn't an ideal way as constructing HTML in JavaScript isn't best practice. There is a way to print out the value in Knockout but with hardcoded values:
<ul data-bind="foreach:$root.Properties">
<li data-bind="text:$data:Doors"></li>
<li data-bind="text:$data.Model"></li>
But I was wondering whether it could be possible to get Knockout.js to look like what I'm returning in the JavaScript code?
Here's how you can do it:
var data = {
"Properties": {
"Engine Capacity": "1499cc",
"Doors": 3,
"Extras": [
"LED lights",
"4WD"
],
"Model": "123a"
}
};
var viewModel = {
data: ko.mapping.fromJS({"Properties": data.Properties})
};
ko.applyBindings(viewModel);
<ul data-bind="foreach: data.Properties">
<li>
<b data-bind="foreachprop: props"> :
<span data-bind="value"> </span>
</b>
</li>
</ul>
More reference:
How to use knockout to iterate over an object (not array)
Mapping: foreach binding work only the first time
How do I map the example in the json output to a string based on the position? It's rather difficult to explain, but I have included my code and further details below:
JSON output
[
{
"phrase": "Training {{group}} to develop their {{attribute}} by ensuring they are comfortable with {{factor}}",
"example": "the team",
"position": 0
},
{
"phrase": "Training {{group}} to develop their {{attribute}} by ensuring they are comfortable with {{factor}}",
"example": "match skills",
"position": 1
},
{
"phrase": "Training {{group}} to develop their {{attribute}} by ensuring they are comfortable with {{factor}}",
"example": "defence techniques",
"position": 2
}
]
controller.js
PhraseService.getPhraseExample($scope.phraseId).then(function(dataResponse) {
$scope.phraseExampleList = dataResponse.data;
})
services.js
function PhraseService($http) {
this.getPhraseExample = function(phraseId) {
return $http({
method: 'GET',
url: server + '/api/phrase-example/' + phraseId,
});
}
}
phrase-detail.html
<div class="item item-text-wrap">
<span ng-repeat="e in phraseExampleList">{{ e }}</span>
</div>
Current output
Desired output
You want something as described in https://stackoverflow.com/a/18234317/3856625 Seems that StackOverflow has the same method you need :)
So you would map over the entries you have and just use it to replace the needed.
So I have a JSON file from the Etsy API that returns the listings and their info. I want to get the title, url for the product, first image of the product, price, shop title, and shop url for each product, which is in this JSON (this is the first product):
{
"count": 657352,
"results": [
{
"title": "Clink illustration print - rosy pink cheeks nerds kissing - perfect gift for your love, a wedding, valentine, or anniversary",
"price": "20.00",
"url": "https:\\/\\/www.etsy.com\\/listing\\/55086613\\/clink-illustration-print-rosy-pink?utm_source=producttemp&utm_medium=api&utm_campaign=api",
"Images": [
{
"listing_image_id": 333410839,
"hex_code": "EAEBEB",
"red": 234,
"green": 235,
"blue": 235,
"hue": 180,
"saturation": 0,
"brightness": 92,
"is_black_and_white": false,
"creation_tsz": 1335891579,
"listing_id": 55086613,
"rank": 1,
"url_75x75": "https:\\/\\/img1.etsystatic.com\\/000\\/0\\/5470068\\/il_75x75.333410839.jpg",
"url_170x135": "https:\\/\\/img1.etsystatic.com\\/000\\/0\\/5470068\\/il_170x135.333410839.jpg",
"url_570xN": "https:\\/\\/img1.etsystatic.com\\/000\\/0\\/5470068\\/il_570xN.333410839.jpg",
"url_fullxfull": "https:\\/\\/img1.etsystatic.com\\/000\\/0\\/5470068\\/il_fullxfull.333410839.jpg",
"full_height": 594,
"full_width": 700
}
],
"Shop": {
"shop_name": "GenevieveSantos",
"url": "https:\\/\\/www.etsy.com\\/shop\\/GenevieveSantos?utm_source=producttemp&utm_medium=api&utm_campaign=api"
}
},
I used this to make the request for the data:
https://openapi.etsy.com/v2/featured_treasuries/listings?api_key=&includes=Images:1:0,Shop(shop_name,url,)&fields=title,price,url&limit=42
Now I'm using Vue.js to repeat the first 42 listings on a 3 x 14 grid in an html file but I'm having trouble reaching into the arrays just to return the first image and the title. Here is the HTML:
<section class="listings">
<div v-repeat="42" class="column" id="listings">
<a href={{item_url}}>
<img src={{url_fullxfull}}>
<h4>{{title}}</h4>
</a>
<a href="{{url}}">
<p class="username">{{shop_name}}</p>
</a>
<p class="price">{{price}}</p>
</div>
</section>
Here is the JS that grabs the JSON file and tries to make a Vue object:
$.getJSON('../../api/etsy/listings.json')
.then(function(listings){
var listings = new Vue({
el: '#listings',
data: {
title: titles,
images: images,
price: prices,
url: urls
}
});
});
So basically I want the url_fullxfull, title, price, url, shop_name, and shop url to fill in that html and repeat that block of html 42 times. Any suggestions? Thank you!
For HTTP requests, you should use the Vue Resource library:
https://cdnjs.cloudflare.com/ajax/libs/vue-resource/0.1.3/vue-resource.min.js
Then you can write the code like this:
<div v-repeat="etsyData">
...
</div>
var vue = new Vue({
el: '#app',
data: {
apiUrl: "........",
etsyData: {}
},
ready: function(){
this.$http.get(this.apiUrl, function(data, status, request){
this.etsyData = data;
})
}
})
You've put the number 42 in your v-repeat instead of the actual vue property.
If you insist on using jquery instead, then you have to access the vue object from the outside, like this: vue.etsyData = data, or if the data object is in side a component, it will be something like this: vue.$children[0].etsyData = data.
I'm trying to show all lists and the tasks associated with each list. In my controller I have:
$http.get('api/list/').success(function (data) {
$scope.lists = data;
$scope.tasks = data[0].Task;
});
This works for the first item but of course data[0].Task needs to be dynamic. The problem I'm having is that this is being called once for each list. I tried using a variable but it gets reset to it's original value. I've also tried using a callback but no luck. I'm not sure what I'm overlooking or if I'm going about it all wrong.
Your best bet is to wrap the http.get in a factory and let it return new representations of your Lists that have the tasks in them. This way you get new references and it won't overwrite your existing objects. Essentially, you want the http.get to return new List objects in its success resolution.
After that, the controller gets the promise resolution, takes the new list object, and binds it into something thats on the scope. This will filter through to the rest of the page and let you preserve existing lists/tasks for the life of the page.
Your GET api/list/ request you probably return something like this:
[
{
"id": 1,
"name": "List #1",
"tasks": [
{
"id": 1,
"name": "Task #1 on List #1"
},
{
"id": 2,
"name": "Task #2 on List #1"
},
{
"id": 3,
"name": "Task #3 on List #1"
}
]
},
{
"id": 2,
"name": "List #2",
"tasks": [
{
"id": 1,
"name": "Task #1 on List #2"
},
{
"id": 2,
"name": "Task #2 on List #2"
},
{
"id": 3,
"name": "Task #3 on List #2"
}
]
}
]
This is assuming that you always want to return the associated tasks in an api/list/ command.
You then only need to call this once every time you want to refresh all lists and all tasks.
You should have a single controller which is bound to a view, in which your $http.get is called. It should set $scope.lists = data on success.
In your view you simply need two nested ng-repeat tags. For example, you could use unordered lists:
<div ng-controller="ListsController">
<ul>
<li ng-repeat="list in lists">
<ul>
<li ng-repeat="task in list.tasks">
</li>
</ul>
</li>
</ul>
</div>
I haven't used angular but I'm pretty sure this is all you need to do. A single AJAX call will populate a <li> element for each list, with nested <li> elements for each task belonging to that list.
If each list has a task and you want a list of them you can do this:
$scope.tasks = data.map(function(obj){return obj.task;})
map creates an array based on what the function returns for each list.