Using AngularJS, I'm iterating over a JSON object containing an array of event objects, containing an array of competition objects.
I wish to show each event in a table, and then each competition in a td, but only three cells per row
I'm using ng-repeat to return a list of tables for each event, but I'm having trouble with splitting the competitions into a new <tr> every three <td>s
Aside from rebuilding my own massive object from the JSON, what is the best way to do what I'm describing in Angular?
Current view:
<table ng-repeat="listing in listings">
<tr>
<th colspan="3">{{listing.name}}</th>
</tr>
<tr>
<td ng-repeat="competition in listing.competitions">
{{competition.id}} - {{competition.name}}
</td>
</tr>
</table>
Desired output:
<table>
<tr>
<th colspan="3">Event Name</th>
</tr>
<tr>
<td>competition id - competition name</td>
<td>competition id - competition name</td>
<td>competition id - competition name</td>
</tr>
<tr>
<td>competition id - competition name</td>
<td>competition id - competition name</td>
<td>competition id - competition name</td>
</tr>
</table>
Controller:
app.controller('EventsCtrl', function ($scope, $http) {
$scope.loading = true;
$http.get('scripts/JSON/events.json').then(function (response) {
var listings = response['data'].events;
$scope.listings = listings;
});
});
events.json
{
"events": [
{
"id": 418,
"name": "et ullamco",
"competitions": [
{
"id": 933,
"name": "qui in deserunt occaecat et",
"startTime": 1381092189
},
{
"id": 853,
"name": "eu enim ex incididunt do",
"startTime": 1380708266
},
{
"id": 5738,
"name": "ad est ut aliquip et",
"startTime": 1381366623
},
{
"id": 7599,
"name": "sit ex voluptate aliqua dolor",
"startTime": 1381284106
},
{
"id": 7481,
"name": "laborum consequat deserunt do aliqua",
"startTime": 1380874273
},
{
"id": 3441,
"name": "amet reprehenderit sint sunt proident",
"startTime": 1380554850
},
{
"id": 1959,
"name": "ullamco minim minim in voluptate",
"startTime": 1380651981
}
]
},
You are using a table but the semantics of your data indicate you have a list of lists. You should consider outputting this with <ul><li> instead of a grid.
<ul ng-repeat="listing in listings">
<li>
<h2>{{listing.name}}</h2>
<ul ng-repeat="competition in listing.competitions">
<li>
{{competition.id}} - {{competition.name}}
</li>
</ul>
</li>
</ul>
You can achieve your desired layout with CSS quite easily using the above HTML. Checkout the "block grids" in Twitter Bootstrap or Foundation for examples. Tables should generally be used only for data that is actually tabular.
However...
You're question does still bear answering since there may be other reasons to alternate templates in the way you suggest. You could use a function for this to get things three at a time:
<table ng-repeat="listing in listings">
<tr>
<th colspan="3">{{listing.name}}</th>
</tr>
<tr ng-repeat="group in competitions">
<td ng-repeat="competition in group">
{{competition.id}} - {{competition.name}}
</td>
</tr>
</table>
In your controller you can create a "list of lists" to bind to the nested repeaters
// Adapted from Fresheyeball's solution
$scope.competitions = []
compSet = []
for(var i; i < $scope.listings.competitions; i++){
var competition = $scope.listings.competitions[i];
if( i % 3 ){
$scope.competitions.push(compSet);
compSet = [];
}
compSet.push(competition);
}
Related
I am building both android and iOS app with Framework7-vue.
The Android app works perfectly, but the iOS app gives me troubles.
The user will be given the option to view a popup, this popup will have an accordion inside.
The animation in iOS is terribly slow and it looks awful.
I've been trying some stuff and googled a bit but no success.
Bellow you will find my implementation of the app.js and a snippet containing the popup.
I will be happy to hear suggestions, thank you.
app.js
const questions = {
questionOne:'Does the subject have a high CCA?',
options: {
yes:'Yes',
no:'No'
},
selected:{
yes: {
questionTwo: 'High PTH and yes to high CCA',
options: [
'PTH > 80pmol/L',
'PTH < 80pmol but > 50pmol/L and increasing',
'PTH < 50 but higher than base line'
]
},
no: {
questionTwo: 'PTH > 80pmol/L',
options: [
'Refer for surgical parathyroidectomy',
'Start Cinacalcet if parathyroidectomy is'
]
}
}
}
// this.$$ - this.Dom7;
Vue.use(Framework7Vue);
// Init Page Components
Vue.component('page-education', {
template: '#page-education'
});
Vue.component('page-mbddef', {
template: '#page-mbddef'
});
Vue.component('page-ckdmbdeffects', {
template: '#page-ckdmbdeffects'
});
Vue.component('page-algorithms', {
template: '#page-algorithms'
});
Vue.component('page-highpth', {
template: '#page-highpth'
});
Vue.component('page-dietary', {
template: '#page-dietary'
});
// Handle device ready event
// Note: You may want to check out the vue-cordova package on npm for cordova specific handling with vue - https://www.npmjs.com/package/vue-cordova
document.addEventListener('deviceready', () => {
console.log("DEVICE IS READY!");
}, false)
// Init App
// var $$ = dom7;
// console.log($$);
// var $$ = this.Dom7;
// console.log($$);
const mainApp = new Vue({
el: '#app',
data: {
popupOpened: false,
navBarShowing: true,
showingGreenCard: false,
currQuestion: questions.questionOne,
currOptCount: 2,
currOptions: questions.options,
showing: false,
isShowing: true,
icon: "<i class='far fa-file-alt'></i>",
homeIcon: "<i id='sidemenu-icon' class='fas fa-home'></i>",
pencilIcon: "<i id='sidemenu-icon' class='fas fa-pencil-alt'></i>",
healthIcon: "<i id='sidemenu-icon' class='fas fa-plus'></i>",
algIcon: "<i id='sidemenu-icon' class='fas fa-align-justify'></i>",
dietIcon: "<i id='sidemenu-icon' class='fas fa-utensils'></i>",
clipIcon: "<i id='sidemenu-icon' class='far fa-clipboard'></i>",
linkIcon: "<i id='sidemenu-icon' class='fas fa-link'></i>"
},
progress: {
value: 10
},
on: {
pageInit: function(){
console.log('page');
}
},
methods: {
setInlineProgress: function(value) {
this.value = value;
const self = this;
const app = self.$f7;
app.setProgressbar('#demo-inline-progressbar', this.value);
},
toHomeScreen: function() {
this.$f7.getCurrentView().router.back({ pageName: 'home-page', force: true, refreshPrevious: true});
this.$f7.closePanel();
},
shouldShow: function() {
this.showing = true;
},
generateQuestion: function(answer){
this.currQuestion = questions.selected.yes.questionTwo;
this.optionsOne = 'Yes';
this.optionTwo = 'No';
this.shouldShow();
},
showPopUp: function(e) {
this.showingGreenCard = true;
},
closePopUp: function(){
this.showingGreenCard = false;
},
},
on:{
},
// Init Framework7 by passing parameters here
framework7: {
root: '#app',
/* Uncomment to enable Material theme: */
material: true,
routes: [
{
path:'/',
name: 'home'
}
,
{
path: '/education/',
component: 'page-education'
},
{
path: '/ckdmbddef/',
component: 'page-mbddef'
},
{
path: '/ckdmbdeffects/',
component: 'page-ckdmbdeffects'
},
{
path: '/algorithms/',
component: 'page-algorithms',
},
{
path: '/highpth/',
component: 'page-highpth'
},
{
path: '/dietary/',
component: 'page-dietary'
},
{
path: '/form/',
component: 'page-form'
},
{
path: '/dynamic-route/blog/:blogId/post/:postId/',
component: 'page-dynamic-routing'
}
],
}
});
Here is the popup snippet:
<div class="popup-card-four" v-if="mainApp.showingGreenCard">
<p class="popup-text">Suitable low phosphate foods</p>
<i id="close-icon" #click="mainApp.closePopUp()" class="far fa-times-circle"></i>
<f7-list class="main-acc-list" accordion-list>
<f7-list-item id="acc-one-title" accordion-item title="Lower phosphate diary">
<f7-accordion-content class="table-wrapper">
<f7-block >
<table class="main-table">
<tr class="table-underline">
<td class="popup-inner">Milk</td>
<td class="popup-inner">Rice or milk</td>
</tr>
<tr>
<td class="popup-inner">Cheese</td>
<td class="popup-inner">Cottage Cheese</td>
</tr>
<tr>
<td></td>
<td class="popup-inner">Cream cheese</td>
</tr>
<tr>
<td></td>
<td class="popup-inner">Quark</td>
</tr>
<tr class="table-underline">
<td></td>
<td class="popup-inner">Ricotta</td>
</tr>
<tr>
<td class="popup-inner">Cream</td>
<td class="popup-inner">Single, double, whipping,</td>
</tr>
<tr>
<td></td>
<td class="popup-inner">pouring, clotted,</td>
</tr>
<tr>
<td></td>
<td class="popup-inner">Marscapone</td>
</tr>
</table>
</f7-block>
</f7-accordion-content>
</f7-list-item>
<f7-list-item id="acc-one-title" accordion-item title="Lower phosphate diary">
<f7-accordion-content>
<f7-list>
<f7-list-item title="Item 1"></f7-list-item>
<f7-list-item title="Item 2"></f7-list-item>
<f7-list-item title="Item 3"></f7-list-item>
<f7-list-item title="Item 4"></f7-list-item>
</f7-list>
</f7-accordion-content>
</f7-list-item>
<f7-list-item id="acc-one-title" accordion-item title="Lower phosphate diary">
<f7-accordion-content>
<f7-block>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean elementum id neque nec commodo. Sed vel justo at turpis laoreet pellentesque quis sed lorem. Integer semper arcu nibh, non mollis arcu tempor vel. Sed pharetra tortor vitae est rhoncus, vel congue dui sollicitudin. Donec eu arcu dignissim felis viverra blandit suscipit eget ipsum.
</p>
</f7-block>
</f7-accordion-content>
</f7-list-item>
</f7-list>
</div>
Resolved the issue by removing the framework7-vue component for accordion and replacing it with the standart framework7 html accordion component.
It seems that iOS does not like framework7-vue components.
Hope this will be helpful to someone.
Cheers.
I have a simple json load list which uses angular directive to render the data from local json.
I wanted to display likes in a separate column and dislikes in the separate column. but I tried the below code snippet, but achieved likes in first column but not sure how to render dislikes in the second column.
Here is the HTML
<ul class="nav">
<li class="active" ng-repeat="item in artists.People">
<a href ng-click='setSelectedArtist(item)'>{{item.name}}
</a>
</li>
</ul>
<!-- Displaying table -->
<table>
<tr>
<th>Likes</th>
<th>Dislikes</th>
</tr>
<tr ng-repeat = "favourites in items.Likes">
<td>{{ favourites }}</td>
/* stuck here */
</tr>
</table>
JSON:
{
"People":[
{
"name":"Andrew Amernante",
"rating":3,
"img":"http://www.fillmurray.com/200/200",
"Description":"Glutenfree cray cardigan vegan. Lumbersexual pork belly blog, fanny pack put a bird on it selvage",
"Likes":[
"Dogs",
"Long walks on the beach",
"Chopin",
"Tacos"
],
"Dislikes":[
"Birds",
"Red things",
"Danish food",
"Dead Batteries"
]
},
{
"name":"Frank Wang",
"rating":5,
"img":"http://www.fillmurray.com/200/201",
"Description":"Before errors, mails were only pressures. This is not to discredit the idea that a magic is the prose of anelizabeth. This could be, or perhaps some posit the outmost coil to be less than dedal. Some assert that those treatments are nothing more than carp.",
"Likes":[
"Frank",
"Manchester United",
"Football",
"Programming"
],
"Dislikes":[
"Dogs",
"Long walks on the beach",
"Chopin",
"Tacos"
]
}
]
}
Controller:
$http.get('people.json').success(function(response) {
$scope.artists = response;
});
$scope.setSelectedArtist = function(artist) {
$scope.items = artist;
};
This code will work even when the lengths of Likes and Dislikes are not equal
<table>
<thead>
<th>Likes</th>
<th>Dislikes</th>
</thead>
<tbody ng-repeat="items in test.People">
<tr ng-repeat="i in range(items.Likes.length, items.Dislikes.length) track by $index">
<td>{{items.Likes[$index]}}</td>
<td>{{items.Dislikes[$index]}}</td>
</tr>
</tbody>
</table>
$scope.range = function(a,b) {
var n = Math.max(a,b);
return new Array(n);
};
Sample JSON
{
"People":[
{
"name":"Andrew Amernante",
"rating":3,
"img":"http://www.fillmurray.com/200/200",
"Description":"Glutenfree cray cardigan vegan. Lumbersexual pork belly blog, fanny pack put a bird on it selvage",
"Likes":[
"Dogs",
"Long walks on the beach",
"Chopin",
"Tacos"
],
"Dislikes":[
"Birds",
"Red things",
"Danish food",
"Batteries",
"Football",
"Dead Batteries"
]
},
{
"name":"Frank Wang",
"rating":5,
"img":"http://www.fillmurray.com/200/201",
"Description":"Before errors, mails were only pressures. This is not to discredit the idea that a magic is the prose of anelizabeth. This could be, or perhaps some posit the outmost coil to be less than dedal. Some assert that those treatments are nothing more than carp.",
"Likes":[
"Frank",
"Manchester United",
"Football",
"Programming",
"Dead Batteries"
],
"Dislikes":[
"Dogs",
"Long walks on the beach",
"Chopin",
"Tacos"
]
}
]
}
Output:
Sample output
Try the following:
<tr ng-repeat = "favourites in items.Likes">
<td>{{ favourites }}</td>
<td ng-if="$index <= items.Dislikes.length-1">{{items.Dislikes[$index]}}</td>
</tr>
try this code :
<table ng-repeat="items in artists.People">
<tr>
<th>Likes</th>
<th>Dislikes </th>
</tr>
<tr ng-if="items.Likes.length <= items.Dislikes.length" ng-repeat="d in items.Dislikes track by $index">
<td>{{d}}</td>
<td ng-if="items.Likes[$index]">{{items.Likes[$index]}}</td>
</tr>
<tr ng-if="items.Likes.length > items.Dislikes.length" ng-repeat="d in items.Likes track by $index">
<td>{{d}}</td>
<td ng-if="items.Dislikes[$index]">{{items.Dislikes[$index]}}</td>
</tr>
</table>
I creating a web app using express-generator using handlebar as templating engine. If I display array of object from response, it displays. But, when I each loop in view display nothing.
/********************************
model file ../controllers/books.js
*********************************/
var request = require('request');
exports.list = function(req, res, next){
request.get({ url: "https://jsonplaceholder.typicode.com/posts" }, function(error, response, body) {
if (!error && response.statusCode == 200) {
res.render('index', { title: 'speed Tracker', list: body });
}
});
};
/***********************
route file
************************/
var express = require('express');
var router = express.Router();
var books = require('../controllers/books');
/* GET home page. */
router.get('/', books.list);
<!-- language: lang-html -->
<table id="datatable" class="table table-striped table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Album Id</th>
<th>title</th>
<th>url</th>
<th>image</th>
</tr>
</thead>
{{list}}
<tbody>
{{#each list}}
<tr>
<td>{{id}}</td>
<td>{{userId}}</td>
<td>{{title}}</td>
<td>{{body}}</td>
<td></td>
</tr>
{{/each}}
</tbody>
</table>
json of list
[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"userId": 1,
"id": 2,
"title": "qui est esse",
"body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
},
{.....
I have added code above data is not display in each loop.
I have to convert list from String to Object in book.js file. I have add the code below.
var request = require('request');
exports.list = function(req, res, next){
request.get({ url: "https://jsonplaceholder.typicode.com/posts" }, function(error, response, body) {
if (!error && response.statusCode == 200) {
res.render('index', { title: 'speed Tracker', list: JSON.parse(body) }); // add JSON.parse to convert string to object :)
}
});
};
I have an already written (in html) table and i want to "rewrite" it when i press the "refresh data" button. So far, i lose all of my css style for a reason (like i' m creating a new table from scratch). My code:
HTML - Table :
<div id="priceInfo">
<button id="priceInfoB">refresh data</button>
</div>
<table id="myTable" border='1'>
<tr class="head">
<th></th>
<th data-city="ny">New York</th>
<th data-city="il">Chicago</th>
<th data-city="ca">San Francisco</th>
</tr>
<tr>
<th class='rowTH' id="one">A Poetic Perspective</th>
<td>Sat, 4 Feb 2012<br />11am - 2pm</td>
<td>Sat, 3 Mar 2012<br />11am - 2pm</td>
<td>Sat, 17 Mar 2012<br />11am - 2pm</td>
</tr>
<tr class="even">
<th class='rowTH' id="two">Walt Whitman at War</th>
<td>Sat, 7 Apr 2012<br />11am - 1pm</td>
<td>Sat, 5 May 2012<br />11am - 1pm</td>
<td>Sat, 19 May 2012<br />11am - 1pm</td>
</tr>
<tr>
<th class='rowTH' id="three">Found Poems & Outsider Poetry</th>
<td>Sat, 9 Jun 2012<br />11am - 2pm</td>
<td>Sat, 7 Jul 2012<br />11am - 2pm</td>
<td>Sat, 21 Jul 2012<br />11am - 2pm</td>
</tr>
<tr class="even">
<th class='rowTH' id="four">Natural Death: An Exploration</th>
<td>Sat, 4 Aug 2012<br />11am - 4pm</td>
<td>Sat, 8 Sep 2012<br />11am - 4pm</td>
<td>Sat, 15 Sep 2012<br />11am - 4pm</td>
</tr>
</table>
JSON Var :
var eventsJson='{"events":{"event":[{"id":"1","name":"A Poetic Perspective","isFree":"true","locations":[{"location":"New York","eventDate":"2015-05-02","eventTime":"14:00"},{"location":"Chicago","eventDate":"2015-05-01","eventTime":"14:00"},{"location":"San Francisco","eventDate":"2015-06-01","eventTime":"15:00"}],"descr":"Vivamus elementum, diam eget ullamcorper fermentum, ligula libero euismod massa, quis condimentum tellus lacus sit."},{"id":"2","name":"Walt Whitman at War","isFree":"false","locations":[{"location":"New York","eventDate":"2015-07-02","eventTime":"14:00"},{"location":"Chicago","eventDate":"2015-07-01","eventTime":"14:00"},{"location":"San Francisco","eventDate":"2015-08-01","eventTime":"15:00"}],"descr":"Donec convallis eu metus eget dictum. Etiam non lobortis dui."},{"id":"3","name":"Found Poems & Outsider Poetry","isFree":"false","locations":[{"location":"New York","eventDate":"2015-06-02","eventTime":"11:00"},{"location":"Chicago","eventDate":"2015-07-01","eventTime":"14:00"},{"location":"San Francisco","eventDate":"2015-06-01","eventTime":"15:00"}],"descr":"Ut fermentum, elit vel iaculis viverra, dui libero ultrices nibh, ut ornare."},{"id":"4","name":"Natural Death: An Exploration","isFree":"true","locations":[{"location":"New York","eventDate":"2015-05-02","eventTime":"14:00"},{"location":"Chicago","eventDate":"2015-05-01","eventTime":"14:00"},{"location":"San Francisco","eventDate":"2015-06-01","eventTime":"15:00"}],"descr":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent aliquet urna ut tortor consequat."}]}}';
Any help will be appreaciated. Any manual and/or command i havent thought about will be helpful!
Loop through the json data
For each record, clone the table rows you need. Demo here
Using jQuery to build table rows from Ajax response(Json)
Swap the cell data with data from your JSON
Append the rows to the table
I have a simple JSON file as shown below:
{
"products": [
{
"title": "United Colors of Benetton Men's Shirt",
"description": "Cool, breezy and charming – this solid green shirt from United Colors of Benetton is born on the beach. Effortlessly classy, this full sleeved shirt is perfect when worn with faded blue jeans and a pair of shades for a weekend get-together.",
"quantity": "10",
"cost": "3.00",
"brand": "United",
"image": "catalog/images/img2.jpg",
"category": "1",
"popularity": "100"
}
]
}
I am displaying this JSON file using Mustache.js into the template blow:
<table class="product-list">
{{#products}}
<tr>
<td>
<table class="product">
<tr>
<td class="product-image">
<img src"{{image}}" height="150" width="150" />
</td>
<td class="product-details">
<p class="title">{{title}}</p>
<p class="description">{{description}}</p>
<p class="quantity"><b>Quanity Available: </b>{{quantity}}</p>
<p class="cost"><b>Cost: </b>£ {{cost}}</p>
<p class="brand"><b>Brand:</b> {{brand}}</p>
</td>
</tr>
</table>
</td>
</tr>
{{/products}}
</table>
Everything works fine but for some reason the slashes in the image property are escaped due to which the images don't show up.
I've tried escaping slashes in the JSON file by adding a backslash in front of them. But instead of correct path I get this.
catalog\/images\/img2.jpg
I also try disabling HTML escaping by using {{{ image }}} and I get this.
catalog\="" images\="" img2.jpg=\""
How can I display the image property properly?
Can anyone please help me with this?
Edit: JS used to generate the template:
$template = $('#product-template').html();
$renderedHtml = Mustache.render($template, $data);
$('#content').html($renderedHtml);
From what I see it should work with triple mustaches {{{image}}}. You are also missing = after src.
Example fiddle:
var jsn = {
"products": [{
"title": "United Colors of Benetton Men's Shirt",
"description": "Cool, breezy and charming – this solid green shirt from United Colors of Benetton is born on the beach. Effortlessly classy, this full sleeved shirt is perfect when worn with faded blue jeans and a pair of shades for a weekend get-together.",
"quantity": "10",
"cost": "3.00",
"brand": "United",
"image": "http://static.cilory.com/26111-large_default/united-colors-of-benetton-men-white-t-shirt.jpg",
"category": "1",
"popularity": "100"
}
]
};
var t = document.getElementById('template').innerHTML;
var m = Mustache.to_html(t, jsn);
document.getElementById('res').innerHTML = m;
console.log(m);
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.7.2/mustache.min.js"></script>
<script id="template" type="text/template">
<table class="product-list">
{{#products}}
<tr>
<td>
<table class="product">
<tr>
<td class="product-image">
<img src="{{{image}}}" height="180" width="150" />
</td>
<td class="product-details">
<p class="title">{{title}}</p>
<p class="description">{{description}}</p>
<p class="quantity"><b>Quanity Available: </b>{{quantity}}</p>
<p class="cost"><b>Cost: </b>£ {{cost}}</p>
<p class="brand"><b>Brand:</b> {{brand}}</p>
</td>
</tr>
</table>
</td>
</tr>
{{/products}}
</table>
</script>
<div id="res"></div>
override the mustache.js escape function to not escape texts:
mustache.escape = function (text) {
return text;
};