I'm learning AngularJS.
I created a page index.html (code see below), which is supposed to display data of the product variable defined in the store module (file app.js below). It worked fine, until I added the Reviews section. Thereafter, placeholder substitution stopped to work (instead of {{product.name}}, the actual product name should be displayed).
But I can't figure out, what exactly is wrong now.
How can I debug this code (find out the reason, why all of a sudden no data are displayed) ?
app.js
(function(){
var app = angular.module('store', [ ]);
app.controller('StoreController', function(){
this.products = gems;
});
app.controller('PanelController', function(){
this.tab = 1;
this.selectTab = function(setTab) {
this.tab = setTab;
};
this.isSelected = function(checkTab) {
return this.tab === checkTab;
}
});
var gems = [
{
name: "Dodecahedron",
price: 2,
description: 'Some description',
canPurchase: true,
images : [
{
full: 'img01.jpg',
thumb: 'img01.jpg'
},
{
full: 'img02.jpg',
thumb: 'img02.jpg'
},
{
full: 'img03.jpg',
thumb: 'img03.jpg'
},
],
reviews = [
{
stars: 5,
body: "I love this product!",
author: "joe#thomas.com"
},
{
stars: 1,
body: "I hate this product!",
author: "mike#thomas.com"
},
]
},
{
name: "Pentagonal Gem",
price: 5.95,
description: 'Some description 2',
canPurchase: false,
images : [
{
full: 'img04.jpg',
thumb: 'img04.jpg'
},
{
full: 'img05.jpg',
thumb: 'img05.jpg'
},
{
full: 'img06.jpg',
thumb: 'img06.jpg'
},
]
}
]
})();
index.html
<!DOCTYPE html>
<html ng-app="store">
<head>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
</head>
<body ng-controller="StoreController as store">
<script type="text/javascript" src="js/angular.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
<div ng-repeat="product in store.products">
<h1>{{product.name}}</h1>
<section ng-init="tab = 1" ng-controller="PanelController as panel">
<ul class="nav nav-pills">
<li ng-class="{ active:panel.isSelected(1) }">
<a href ng-click="panel.selectTab(1)">Description</a>
</li>
<li ng-class="{ active:panel.isSelected(2) }">
<a href ng-click="panel.selectTab(2)">Specifications</a>
</li>
<li ng-class="{ active:panel.isSelected(3) }">
<a href ng-click="panel.selectTab(3)">Reviews</a>
</li>
</ul>
<div class="panel" ng-show="panel.isSelected(1)">
<h4>Description</h4>
<p>{{product.description}}</p>
</div>
<div class="panel" ng-show="panel.isSelected(2)">
<h4>Specifications</h4>
<blockquote>None yet</blockquote>
</div>
<div class="panel" ng-show="panel.isSelected(3)">
<h4>Reviews</h4>
<blockquote ng-repeat="review in product.reviews">
<b>Stars: {{review.stars}}</b>
{{review.body}}
<cite>by: {{review.author}}</cite>
None yet
</blockquote>
</div>
</section>
<h2>{{product.price | currency}}</h2>
<img ng-src="img/{{product.images[0].full}}"/>
<button ng-show="product.canPurchase">Add to cart</button>
</div>
</body>
</html>
Most of errors go to browser console. Also there are several techniques for custom errors handing, here is a good article. There is also other one, which describes several other code guards for common Angular pitfalls.
In your particular case definition of reviews = [ is not correct.
Sometimes I have this "problem" too with my AngularJS Apps, even when I have written much stuff, then suddenly it looks like your current result... :-)
Since now it was almost every time a missing ";" on end of a line, or a missing "{" or "}" brackets somewhere.... In your case I would take a look on something like this, before I change anything else...
Edit Found the Bug
Its like I said... Your Bug is inside your gems JSONArray.... I have replaced all ' with " and all = with :.... after that your app was back to live for me
var gems = [
{
name: "Dodecahedron",
price: 2,
description: "Some description",
canPurchase: true,
images : [
{
full: "img01.jpg",
thumb: "img01.jpg"
},
{
full: "img02.jpg",
thumb: "img02.jpg"
},
{
full: "img03.jpg",
thumb: "img03.jpg"
},
],
reviews : [
{
stars: 5,
body: "I love this product!",
author: "joe#thomas.com"
},
{
stars: 1,
body: "I hate this product!",
author: "mike#thomas.com"
},
]
},
{
name: "Pentagonal Gem",
price: 5.95,
description: "Some description 2",
canPurchase: false,
images : [
{
full: "img04.jpg",
thumb: "img04.jpg"
},
{
full: "img05.jpg",
thumb: "img05.jpg"
},
{
full: "img06.jpg",
thumb: "img06.jpg"
},
]
}
]
Related
I have a list of articles (105) that i paginated with 10 articles per page. All seems fine on the first page and i am able to go to the next pages with the correct content, but when i click an article on page 2 and onward it is grabbing the index of the very first article in the array.
<div class="news-article-list">
<ul>
<li v-for="(article, index) in allArticles" :key="index">
<a #click="showArticle(index)">
<img :src="article.featureImg" v-if="article.featureImg" alt="Article Feature Image" />
<h2 class="news-title">{{ article.title }}</h2>
<p class="news-date">{{ article.date }}</p>
<p class="short-desc">{{ article.shortDesc }}...</p>
</a>
<a class="read-more" #click="showArticle(index)">Read More</a>
</li>
</ul>
</div>
<ul class="pagination">
<li class="page-item">
<button type="button" class="page-link" v-show="page != 1" #click="page--"><</button>
</li>
<li id="page-numbers">
<button type="button" class="page-link" v-for="pageNumber in pages" #click="page = pageNumber">{{pageNumber}}</button>
</li>
<li class="page-item">
<button type="button" class="page-link" v-show="page < pages.length" #click="page++">></button>
</li>
</ul>
the showArticle function:
showArticle: function showArticle(selected) {
this.selectedArticle = selected;
window.location = this.filteredArticles[this.selectedArticle].url;
}
to explain my array a little more, once the page is "created" i put it in a new array called filteredArticles as i have functions that are in place for filtering based on category, which is why you see the window.location is equal to this.filteredArticles.
My Pagination js:
data: function() {
return {
filteredArticles: [],
timer: null,
page: 1,
currentPage: 1,
perPage: 10,
pages: [],
articles: [
{
title: 'title here',
date: 'date here',
shortDesc: 'description here',
url: 'url to article - had to put each article into a separate file',
category: 'category',
featureImg: 'Image for article'
},
{}
]
},
computed: {
allArticles() {
return this.paginate(this.filteredArticles);
}
},
setPages() {
let numberOfPages = Math.ceil(this.filteredArticles.length / this.perPage);
for (let index = 1; index <= numberOfPages; index++) {
this.pages.push(index);
}
},
paginate(filteredArticles) {
let page = this.page;
let perPage = this.perPage;
let from = (page * perPage) - perPage;
let to = (page * perPage);
return filteredArticles.slice(from, to);
},
watch: {
filteredArticles() {
this.setPages();
}
}
I know that I may have done this in a round about way, so any suggestions would be greatly appreciated, but overall I just need to be able to select any of the articles and it find the correct index and direct you to the correct article no matter what page you are on.
I tried to clean up and simplify your code. I removed the "round-about-ness" that you mentioned and renamed the variables to make more sense (at least to me).
Also, if you need to change the location anyway, so we're not in a single page app, why not use the anchor tag as intended with an href attribute, with the added bonus of accessibility?
You won't see any change when clickin an article in this demo, because I set the URLs simply to hashes ( #1, #2, etc.), but you'll see the resulting URL chaning with each article in the bottom left corner of your browser.
const app = {
data: function() {
return {
timer: null,
currentPage: 1,
perPage: 2,
pages: [],
articles: [{
title: 'Title 1',
date: 'date article 1',
shortDesc: 'description article 1',
url: '/#1',
category: 'category',
featureImg: 'Image for article'
},
{
title: 'Title 2',
date: 'date article 2',
shortDesc: 'description article 2',
url: '#2',
category: 'category',
featureImg: 'Image for article'
},
{
title: 'Title 3',
date: 'date article 3',
shortDesc: 'description article 3',
url: '#3',
category: 'category',
featureImg: 'Image for article'
},
{
title: 'Title 4',
date: 'date article 4',
shortDesc: 'description article 4',
url: '#4',
category: 'category',
featureImg: 'Image for article'
},
{
title: 'Title 5',
date: 'date here',
shortDesc: 'description here',
url: '#5',
category: 'category',
featureImg: 'Image for article'
}
]
}
},
computed: {
pagedArticles() {
let page = this.currentPage;
let perPage = this.perPage;
let from = (page * perPage) - perPage;
let to = (page * perPage);
return this.articles.slice(from, to);
}
}
};
Vue.createApp(app).mount('#app');
nav li {
display: inline;
}
.news-article-list li>a {
display: block;
text-decoration: none;
color: inherit;
}
<script src="https://unpkg.com/vue#3.0.11/dist/vue.global.prod.js"></script>
<div id="app">
<nav>
<ul>
<li v-for="p in Math.ceil( articles.length / perPage )"><button :disabled="currentPage === p" #click="currentPage = p">{{ p }}</buttpn></li>
</ul>
</nav>
<div class="news-article-list">
<ul>
<li v-for="(article, index) in pagedArticles" :key="index">
<a :href="article.url">
<img :src="article.featureImg" v-if="article.featureImg" alt="Article Feature Image" />
<h2 class="news-title">{{ article.title }}</h2>
<p class="news-date">{{ article.date }}</p>
<p class="short-desc">{{ article.shortDesc }}...</p>
<span class="read-more">Read More</span>
</a>
</li>
</ul>
</div>
</div>
You are mixing indexes of two arrays: allArticles and filteredArticles. Thy have different content and indexes. To get the correct index of an element you can use findIndex:
showArticle(selected) {
let index = this.filteredArticles.findIndex(el => el.id === this.allArticles[selected].id)
this.selectedArticle = index;
window.location = this.filteredArticles[index].url;
}
I have some code for creating a Hierarchial tree in my angular project
I'm not sure where to use this code.
So there are only two codes- one is for HTML and another is a javascript
I created an angular project using ng new command, so it generated all the files.
Can you guide me on where I can put these codes?
html code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>AngularJS Tree Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script>
</head>
<body ng-app ng-controller="SampleController">
<script type="text/ng-template" id="treeLevel.html">
<ul>
<li ng-repeat="item in items">
<input type="checkbox"
name="itemSelection"
ng-model="item._Selected" />
{{item.text}}
<div ng-include=" 'treeLevel.html'"
onload="items = item.children">
</div>
</li>
</ul>
</script>
<div ng-include=" 'treeLevel.html' "
onload="items = sourceItems">
</div>
<pre> {{sourceItems | json}} </pre>
</body>
</html>
and the java script is:
function SampleController($scope) {
$scope.sourceItems = [
{
text: "Item A",
children: [
{
text: "Item A-1",
children: [
{
text: "Item A-1-1"
}, {
text: "Item A-1-2"
}
]
}, {
text: "Item A-2"
}, {
text: "Item A-3"
},
]
}, {
text: "Item B",
children: [
{ text: "Item B-1" },
{ text: "Item B-2" },
{
text: "Item B-3",
children: [
{ text: "Item B-3-1" },
{ text: "Item B-3-2" },
]
}
]
}
];
}
So basically Angular works with components. A component is basically just an independent piece of the website like it could be a navbar, a sidebar a post component etc. A component has two main parts, the HTML file and the JS file and the are both linked together. So basically the first piece of code you just showed would go on the HTML and the Javascript on the JS part of the component.
Here is more information on how this works and specially on how to create an Angular component.
PS: in the TS (Typescript, a language that inherits from Javascript but with strong typing) file is where your Javascript would go.
I hope this helped, let me know if you need more info :D
I'm having a bit of an issue understanding the jQuery Pagination plugin. This is my first time using it. On my prev question one guy offered me this solution, but I just can't make it work for me; I don't understand why each next room goes inside the previous one. Here is a pic
function __createRoomCard(data) {
for (var i = 0, len = data.length; i < len; i++) {
data[i] = `<div class="rooms__item-wrapper" id="rooms__item-wrapper">
<picture><img class="rooms__image" src="img/${data[i].img}" alt="Room image"></picture>
<ul class="rooms__item">
<li><h2 class="rooms__item-heading">${data[i].name}</h2></li>
<li class="rooms__item-description"><p class="rooms__item-description--list">
<sup class="rooms__item-description--dollar">$</sup>
<span class="rooms__item-price">${data[i].price}</span><sub>/per night</sub></p></li>
</ul>`;
}
return data.join("");
}
$(document).ready(function() {
DATA_SOURCE = [{
img: 'rooms_img_1.jpg',
price: 125,
name: 'Single Room'
}, {
img: 'rooms_img_2.jpg',
price: 240,
name: 'Standart Room'
}, {
img: 'rooms_img_3.jpg',
price: 325,
name: 'Double Room'
}, {
img: 'rooms_img_4.jpg',
price: 450,
name: 'Family Room'
}, {
img: 'rooms_img_5.jpg',
price: 500,
name: 'VIP Room'
}, {
img: 'rooms_img_6.jpg',
price: 600,
name: 'Suite'
}]
$('#list').pagination({
dataSource: DATA_SOURCE,
pageSize: 3, //here put number of items per page
callback: function(data, pagination) {
// template method of yourself
var html = __createRoomCard(data);
$('#Z').html(html);
}
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://pagination.js.org/dist/2.1.5/pagination.min.js"></script>
<div id="Z">Loading...</div>
<div id="list"></div>
hi my friend please add </div> at the end of data[i] like this
</ul></div>
I am very new to knockout.js (just picked it up yesterday), but I was suggested it for what I'm trying to do.
My dilemma is this:
i set the following "initialData" as a json array:
var initialData = [
{
id: 0,
pcName: "Test1"
},
{
id: 1,
pcName: "Test2"
},
{
id: 2,
pcName: "Test3"
},
{
id: 3,
pcName: "Test4"
},
{
id: 4,
pcName: "Test5"
}
];
followed by the following (simple) model:
var PCModel = function (pcs) {
var self = this;
self.pcsList = ko.observableArray(ko.utils.arrayMap(pcs, function (pc) {
return { id: pc.id, pcName: pc.pcName };
}));
and apply my bindings as such:
ko.applyBindings(new PCModel(initialData));
i then try to loop through my (what should be) pcsList:
<ul class="nav nav-tabs" id="sortable" data-bind="foreach: pcsList">
<li>
<a data-bind="attr: {href: '#' + id}, text: pcName"></a>
</li>
</ul>
And yet, nothing seems to happen. I cant seem to figure out why.
Please help.
For anyone that may stumble around here, my problem was simply not calling applyBindings AFTER the document was ready. wrapping it in $("document").ready(... solved the problem.
How can I do recursive template with Angular 2 without ng-include. I don't understand why Angular 2 won't include this skill...
HTML:
<div ng-app="app" ng-controller='AppCtrl'>
<script type="text/ng-template" id="categoryTree">
{{ category.title }}
<ul ng-if="category.categories">
<li ng-repeat="category in category.categories" ng-include="'categoryTree'">
</li>
</ul>
</script>
<ul>
<li ng-repeat="category in categories" ng-include="'categoryTree'"></li>
</ul>
JS:
var app = angular.module('app', []);
app.controller('AppCtrl', function ($scope) {
$scope.categories = [
{
title: 'Computers',
categories: [
{
title: 'Laptops',
categories: [
{
title: 'Ultrabooks'
},
{
title: 'Macbooks'
}
]
},
{
title: 'Desktops'
},
{
title: 'Tablets',
categories: [
{
title: 'Apple'
},
{
title: 'Android'
}
]
}
]
},
{
title: 'Printers'
}
];
});
Use ngFor instead of ng-repeat in Angular2.
https://angular.io/docs/ts/latest/api/common/NgFor-directive.html