Auto Increment in ng-repeat object - javascript

I am taking game info from mlb.com and displaying them using angularjs and ng-repeat directive. an example of the JSON feed is below.
{
"data": {
"games": {
"next_day_date": "2017-08-19",
"modified_date": "2017-08-18T16:57:16Z",
"month": "08",
"year": "2017",
"game": {
"0": [{
"away_team_name": "CUBS"
}, {
"home_team_name": "INDIANS"
}],
"1": [{
"away_team_name": "CUBS"
}, {
"home_team_name": "INDIANS"
}]
},
"day": "18"
}
}
I am only able to display the data properly however, only 1 game. html below
<div class="card mb-3" ng-repeat="x in scoreboard" ng-if="$index > 1">
<div class="card-header" align="center">
{{x.games.game[0].away_team_name}} ({{x.games.game[0].away_win}}-{{x.games.game[0].away_loss}}) At {{x.games.game[0].home_team_name}} ({{x.games.game[0].home_win}}-{{x.games.game[0].home_loss}})<br>
<small>{{x.games.game[0].time}}</small>
</div>
<div class="card-block"></div>
</div>
I do understand [0] refers to the game ID '0' in the json feed, however is there a way to auto increment that number in {{x.games.game[0].time}} to loop through all games instead of doing each game individually like below?
<div class="card mb-3" ng-repeat="x in scoreboard" ng-if="$index > 1">
<div class="card-header" align="center">
{{x.games.game[0].away_team_name}} ({{x.games.game[0].away_win}}-{{x.games.game[0].away_loss}}) At {{x.games.game[0].home_team_name}} ({{x.games.game[0].home_win}}-{{x.games.game[0].home_loss}})<br>
<small>{{x.games.game[0].time}}</small>
</div>
<div class="card-block"></div>
</div>
<div class="card mb-3" ng-repeat="x in scoreboard" ng-if="$index > 1">
<div class="card-header" align="center">
{{x.games.game[1].away_team_name}} ({{x.games.game[1].away_win}}-{{x.games.game[1].away_loss}}) At {{x.games.game[1].home_team_name}} ({{x.games.game[1].home_win}}-{{x.games.game[1].home_loss}})<br>
<small>{{x.games.game[1].time}}</small>
</div>
<div class="card-block"></div>
</div>
I have tried
{{x.games.game[$index + 1].time}} but still return only 1 game
Any insight would be appreciated!

I adjusted my $scope in the controller from
.success(function (data) { $scope.scoreboard = data;} to
.success(function (data) {$scope.scoreboard = response.data.games.game;}
html now looks like
<div class="card mb-3" ng-repeat="x in scoreboard track by $index" ng-if="$index > 1">
<div class="card-header" align="center">{{x.away_team_name}} </div>
</div>
works perfectly

There are a couple of problems:
An iteration over the wrong object, the iteration should be made over scoreboard.games.game instead of scoreboard.
The syntax x in list should only be used in the ng-repeat to iterate over arrays, both scoreboard and scoreboard.games.game are NOT arrays, they're Object literals
The syntax to iterate over Object literals in ng-repeat is (key, value) in object as mentioned in the documentation.
To properly solve this problem we must iterate over the right object with the right syntax, here is how:
<div class="card mb-3" ng-if="key > 1"
ng-repeat="(key, game) in scoreboard.games.game">
<div class="card-header" align="center">
{{ game.away_team_name }}
({{ game.away_win }}-{{ game.away_loss }}) At
{{ game.home_team_name }}
({{ game.home_win }}-{{ game.home_loss }})<br />
<small>{{ game.time }}</small>
</div>
<div class="card-block"></div>
</div>

Related

How to create new bootstrap row per two items on Angular?

I'd like to add row on each 2 elements of ngFor loop.But I couldnt handle it.
I have studentNames array like below
studentNames=[
{
name:"Jonas",
age:22,
number:"1234"
},
{
name:"Mathilda",
age:25,
number:"5432"
},
{
name:"Jacob",
age:20,
number:"4321"
},
{
name:"Ivan",
age:23,
number:"6984"
},
{
name:"Kate",
age:21,
number:"3432"
},
{
name:"James",
age:20,
number:"4312"
},
{
name:"Sean",
age:23,
number:"4321"
},
{
name:"Julia",
age:23,
number:"4321"
},
]
Here what I tried
<ng-container *ngFor="let item of studentNames;let i=index">
<div class="row" *ngIf="i%2==0">
<div class="col md-12">
{{item.name}}
</div>
</div>
</ng-container>
This only skipped when index is not even.
Here how I want to see them below(order doesnt matter only should be 2 by 2 per row).
Stackblitz example: https://stackblitz.com/edit/bootstrap-wpfukz?file=app%2Fapp.component.html
This is a bit more "hacky" approach but it's HTML-only:
<ng-container *ngFor="let item of studentNames;let i=index">
<div *ngIf="i%2==0">
<div class="row">
<div class="col md-12">
{{studentNames[i].name}}
</div>
<div class="col md-12">
{{studentNames[i + 1].name}}
</div>
</div>
</div>
</ng-container>
You can try like below. Use *ngFor exported value index and use half length of the array to continue the loop
<ng-container *ngFor="let item of studentNames; let j = index;">
<ng-container *ngIf="j < (studentNames.length / 2)">
<div class="row">
<div class="col md-6">
{{item.name}}
</div>
<div class="col md-6">
{{ studentNames[ j + (studentNames.length / 2) ].name }}
</div>
</div>
</ng-container>
</ng-container>
Working stackblitz
Note : Array length should be an even number ;)

Computed prop in VueJS not updated until input lost focus

same as title says. A computed property from a object it´s not reactive until the input box lost the focus.
But if the object is in data, the value changes as soon as changed in the input.
I created a codepen with the example: https://codepen.io/Albvadi/pen/QWwMOQV
<div id="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Match Component</div>
<div class="card-body">
<div class="container">
<div class="row align-items-center">
<div class="col-4 text-right">{{homeTeam.desc}}</div>
<div class="col-2">
<input class="form-control text-center" type="number" min="0" v-model.number="homeTeam.score" />
</div>
<div class="col-2">
<input class="form-control text-center" type="number" min="0" v-model.number="awayTeam.score" />
</div>
<div class="col-4">{{ awayTeam.desc }}</div>
<div class="row">
<div class="col">
DATA:
<b>{{ homeTeam.score }}</b>
</div>
<div class="row">
<div class="col">
COMPUTED:
<b>{{ awayTeam.score }}</b>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
new Vue({
el: "#container",
data: {
homeTeam: {
name: "SPA",
desc: "Spain",
score: 0
}
},
computed: {
awayTeam: function() {
return {
name: "GER",
desc: "Germany",
score: 0
};
},
},
mounted() {
console.log("Component mounted.");
}
});
If you change the first input, the data value is changed inmediate.
If you change the second input, the computed value if changed only when input lost focus.
How can I solve this?
== EDIT ==
Thanks for your answers!
I know that putting awayteam in the data section solves the problem, it was just an example to simplify my problem.
My problem was that along with the two results, I wanted to have a computed property to be able to issue to another component.
I edited the codepen with the final result: https://codepen.io/Albvadi/pen/jOELYwN and it´s working correctly.
But #Sami Kuhmonen comments that computed properties are only read properties but in my checkFinalResult I´m setting the computedMatch..
It´s the right approach??
Computed properties should be considered read only. Every time there's a change in the properties they reference their value is computed from other values and all changes are lost (or at least should be thought that way, there may be cases where some values remain). Even though sometimes changing their properties seems to work it's not guaranteed and they may change whenever.
In the codepen you already have the computed property referencing other properties from the object, so you'll have to add all the things you want to change similarly and then change that value. This will cause the computed value to be re-evaluated and the changes will be visible and persisted.
Note that building a computed object from a lot of separate things might not be the most performant way and it depends on the situation how it should be handled. If the values are only used within the component then it's easy to just use them directly but if you need an object of specific form for something and need it reactive then computed property might be the way to go.
In your case, why this computed property does not update DOM element instantly,because it has no data reference with Vue instance.Here, it return some static value only,which was not the computed property's purpose. computed property came to purpose,when you need to calculate or compute some decision on the basis of your data property of your vue instance.
new Vue({
el: "#container",
data: {
homeTeam: {
name: "SPA",
desc: "Spain",
score: 0
},
awayTeam: {
name: "GER",
desc: "Spain",
score: 0
},
},
mounted() {
console.log("Component mounted.");
}
});
<div id="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Match Component</div>
<div class="card-body">
<div class="container">
<div class="row align-items-center">
<div class="col-4 text-right">{{homeTeam.desc}}</div>
<div class="col-2">
<input class="form-control text-center" type="number" min="0" v-model.number="homeTeam.score" />
</div>
<div class="col-2">
<input class="form-control text-center" type="number" min="0" v-model.number="awayTeam.score" />
</div>
<div class="col-4">{{ awayTeam.desc }}</div>
<div class="row">
<div class="col">
DATA:
<b>{{ homeTeam.score }}</b>
</div>
<div class="row">
<div class="col">
COMPUTED:
<b>{{ awayTeam.score }}</b>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
Why do you use a computed property for this task? I would simply do it this way.
new Vue({
el: "#container",
data: {
homeTeam: {
name: "SPA",
desc: "Spain",
score: 0
},
awayTeam: {
name: "GER",
desc: "Germany",
score: 0
},
},
mounted() {
console.log("Component mounted.");
}
});

How to read data from a v-for (vue.js)?

Given the next v-for:
<div class="container-fluid" id="networdapp" style="display:none;">
<div class="row" >
<div v-for="result in results" class="col-sm-6" >
<div class="card m-3 h-240 bg-light" >
<div class="card-header text-center" > {{ result.title }} </div>
<div class="card-body" style="height:200px" >
<p class="card-text" v-html="result.prevDesc"></p>
</div>
<div class="card-footer bg-transparent border-info">
<a href="/details" class="btn btn-info" #click="getData(result)" >Details</a>
</div>
</div>
</div>
</div>
</div>
And the next Vue.js script:
<script type="text/javascript">
const vm = new Vue({
el: '#networdapp',
data: {
results:[]
},
methods: {
getData: function(result){
window.alert($(this).parents("#networdapp").find(".card-header.text-center").outerHTML);
window.alert(document.getElementsByClassName("card-header").outerHTML);
window.alert(result.outerHTML);
}
},
mounted() {
axios.get('/getJson')
.then(response => {
this.results = response.data;
})
.catch( e => {
console.log(e);
});
}
});
</script>
I want to get data from a specific iteration,let's say if I click the "Details" button of the 3rd div from the v-for I want to get the {{result.title }} data from the 3rd for.Is it possible?I've been reading the Vue.js documentation but I didn't find anything about reading the data from DOM.If it is not possible,than how can I do that without Vue.js?Is there any other option?
The main goal is to get this data and to put it into a js object passing it to another webpage.
you have to pass index key and use is to get from results's position.
change the for loop div into
<div v-for="(result,i) in results" :key="i" class="col-sm-6" >
also chnange the methods parameter
<a href="/details" class="btn btn-info" #click="getData(i)" >Details</a>
and the method will get the index key and here i have used console to see the result.title that you have wanted. you can use it any how you want.
getData: function(key){
console.log(this.results[key].title)
}
so
Given the next v-for:
<div class="container-fluid" id="networdapp" style="display:none;">
<div class="row" >
<div v-for="(result,i) in results" :key="i" class="col-sm-6" >
<div class="card m-3 h-240 bg-light" >
<div class="card-header text-center" > {{ result.title }} </div>
<div class="card-body" style="height:200px" >
<p class="card-text" v-html="result.prevDesc"></p>
</div>
<div class="card-footer bg-transparent border-info">
<a href="/details" class="btn btn-info" #click="getData(i)" >Details</a>
</div>
</div>
</div>
</div>
And the next Vue.js script:
<script type="text/javascript">
const vm = new Vue({
el: '#networdapp',
data: {
results:[]
},
methods: {
getData: function(key){
console.log(this.results[key].title)
}
},
mounted() {
axios.get('/getJson')
.then(response => {
this.results = response.data;
})
.catch( e => {
console.log(e);
});
}
});
To get the data you want to access in the results array, you can use an index in your v-for loop
v-for="(result, index) in results"
you can check the docs here https://v2.vuejs.org/v2/guide/list.html
I also strongly recommend you to add a key attribute after the v-for to help vue.js
keep track of each result, see https://v2.vuejs.org/v2/guide/list.html#key

Filter json objects with Angular js ng-repeat directive

In my angular js controller, I have a json array that contains continents and inside the continents contain an array of countries
//CONTROLLER
app.controller('CountryController', function(){
this.continents =
[
{
name:'Africa',
countries:
[
{ name: 'Algeria', code:'DZ', operators:'3'},
{ name: 'Angola', code:'AO', operators:'1'},
{ name: 'Benin', code:'BJ', operators:'2'},
{ name: 'Burkina Faso', code:'BF', operators:'1'}
],
},
{
name:'AsiaPacific',
countries:
[
{ name: 'Afghanistan', code:'AF', operators:'4'},
{ name: 'Bahrain', code:'BH', operators:'2'}
],
}
];
});
In my view i want to display the countries in tabbed elements arranged by continents i.e I have tabs to hold the countries in each continent. e.g i have a tab for Africa and inside this tab i want to display the countries in the Africa object.
I have tried using the 'filter' to display only countries in a particular continent in each tab but it is not working neither does anything show up. On inspecting the element the code seems to be commented. This is the first task I am trying to accomplish using Angular-js so I am still learning the ropes.
This is what my view looks like
<div ng-app="countriessupported" class="container container_with_height">
<div ng-controller="CountryController as countryCtrl" id="myTabContent" class="tab-content hidden-xs">
<div class="tab-pane fade active in" id="africa">
<div class="col-md-12 countries_col-12" ng-repeat="continent in countryCtrl.continents.name | filter: 'Africa' ">
<a href="">
<div class="col-md-3 country_container" ng-repeat="country in continent.countries">
<div class="country_name">
{{ country.name }}
</div>
</div>
</a>
</div>
</div>
</div>
</div>
So the idea is to have the countries repeated in the div with class col-md-3
How can i achieve this please?
Try this html Code
<div ng-app="countriessupported" class="container container_with_height">
<div ng-controller="CountryController as countryCtrl" id="myTabContent" class="tab-content hidden-xs">
<div class="tab-pane fade active in" id="africa">
<div class="col-md-12 countries_col-12" ng-repeat="continent in countryCtrl.continents | filter: 'Africa' ">
<a href="">
<div class="col-md-3 country_container" ng-repeat="country in continent.countries">
<div class="country_name">{{ country.name }}</div>
</div>
</a>
</div>
</div>
</div>
</div>
let me know if this is helpful
Try to change you first repeater to this ng-repeat="continent in countryCtrl.continents instead of ng-repeat="continent in countryCtrl.continents.name the filter sholud be working anyway

multidimensional array in angular

I have a multidimensional array from an API. Is it possible to programatically loop through the array?
{
success: true,
categories: [{
cat_id: "2",
name: "This is category One",
description: null,
photo_url: "/img/test.png",
order: "1",
items: [{
item_id: "1",
title: "Desk",
item_url: "/690928460",
photo_url: "/desk.png",
}, {
item_id: "2",
title: "Chair",
item_url: "/18882823",
photo_url: "/chair.png",
},
}]
}]
}
My controller looks like this:
myApp.controller('items', function($scope, $http, $location, Data) {
var apiUrl = '/api/items';
$http.get(apiUrl).
success(function(data) {
$scope.data = Data;
$scope.allData = data;
});
$scope.changeView = function(view) {
$location.path(view);
}
});
Angular index file just has: <div ng-view=""></div>
View file
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="row" ng-repeat="categories in allData">
<div class="col-xs-6" ng-repeat="category in categories">
<div class="items">
<div class="title">
{{ category.name }}
</div>
</div>
</div>
</div>
</div>
</div>
I can loop through the category names fine, but when trying to return items for EACH category I don't understand the logic behind it...
I would suggest some simple nested for loops, as for each gives rise to more complexity.
As I'm not sure what you want to do with the data let's just create an array of all item names and one of all category names:
Within your success function:
var items = [], categories = []
for(var i = 0; i < data.categories.length;i++){
categories.push(data.categories[i].name);
for(var j = 0; j < data.categories[i].items.length;j++){
items.push(data.categories[i].items[j].name);
}
}
console.log(categories);
console.log(items);
EDIT:
Completely missed your html code somehow, here is my solution:
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="col-xs-6" ng-repeat="category in allData.categories">
<div class="items">
<div class="title">
{{ category.name }}
</div>
</div>
</div>
</div>
</div>
EDIT 2:
As to your comment:
If you want to select the secondary view's contents(ie the items) based on the selection of a category I would suggest a ng-click attribute. A directive could be used but isn't necessary:
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="col-xs-6" ng-repeat="category in allData.categories">
<div class="title" ng_click="selected_category = category">
{{ category.name }}
</div>
</div>
<div class="col-xs-6" ng-repeat="item in selected_category.items">
<div class="title">
{{ item.name }}
</div>
</div>
</div>
</div>
So when your categories data is loaded the first ng-repeat is populated with the categories. Each div with class title will have a function called on click which will make the selected_category object equal the selected category.
This will then cause the second view to be populated with all the items in the selected category by Angular's two way bind.

Categories

Resources