I've been writing a code that uses ng-if to display a div with a message if an array is empty([]). The ng-if isn't displaying the div even though I have console.log the array and it shows up empty.
I am still new to angularjs so I am not sure if I am using the ng-if directive correctly. Here is my code, anything helps, thank you!
js:
(function () {
'use strict';
var data = [];
var shoppingList = [
{
name: "Donuts",
quantity: "10"
},
{
name: "Cookies",
quantity: "10"
},
{
name: "Drinks",
quantity: "10"
},
{
name: "Shrimp",
quantity: "10"
},
{
name: "Ice Cream tub",
quantity: "100"
}
];
console.log(data);
angular.module('shoppingListCheckOffApp', [])
.controller('toBuyListController', toBuyListController)
.controller('boughtListController', boughtListController)
.service('shoppingListService', shoppingListService);
toBuyListController.$inject = ['shoppingListService'];
function toBuyListController(shoppingListService) {
var buy = this;
buy.shoppingList = shoppingList;
buy.shoppingListBought = function (itemIndex) {
shoppingListService.dataTransfer(buy.shoppingList[itemIndex].name, buy.shoppingList[itemIndex].quantity);
shoppingListService.remove(itemIndex);
};
}
boughtListController.inject = ['shoppingListService'];
function boughtListController(shoppingListService) {
var bought = this;
bought.data = shoppingListService.getData();
console.log(bought.data);
}
function shoppingListService() {
var service = this;
service.dataTransfer = function (itemName, quantity) {
var item = {
name: itemName,
quantity: quantity
};
data.push(item);
}
service.remove = function (itemIndex) {
shoppingList.splice(itemIndex, 1);
};
service.getData = function () {
return data;
};
};
})();
html:
<!doctype html>
<html ng-app="shoppingListCheckOffApp">
<head>
<title>Shopping List Check Off</title>
<meta charset="utf-8">
<script src="angular.min.js"></script>
<script src="app.js"></script>
</head>
<body>
<div>
<h1>Shopping List Check Off</h1>
<div>
<!-- To Buy List -->
<div ng-controller="toBuyListController as buy">
<h2>To Buy:</h2>
<ul>
<li ng-repeat="item in buy.shoppingList">Buy {{item.quantity}} {{item.name}}(s)<button
ng-click="buy.shoppingListBought($index);" ng-click="myVar = true"><span></span>
Bought</button></li>
</ul>
<div ng-if="buy.shoppingList === []">Everything is bought!</div>
</div>
<!-- Already Bought List -->
<div ng-controller="boughtListController as bought">
<h2>Already Bought:</h2>
<ul>
<li ng-repeat="item in bought.data">Bought {{item.quantity}} {{item.name}}(s)</li>
</ul>
<div ng-if="bought.data === []">Nothing bought yet.</div>
</div>
</div>
</div>
</body>
</html>
You should use ng-if (for arrays) in this way:
<div ng-if="!bought.data.length">Nothing bought yet.</div>
This will show the message when the list is empty.
If you do this:
buy.shoppingList === []
You are comparing you buy.shoppingList array with a new empty array, then it will return false.
Related
I want to include the html page with the help of ng-click inside the ng-repeat.
But, it loading all the content for all ng-repeat elements.
My requirement was I want to bind(ng-include) only the clicked element.
Please find the attachment for your reference.
HTML
<body ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="message in messages">
<a ng-click="clickMe()">Clike Me</a>
<span>{{ message.text }}---{{ message.type }}</span>
<div ng-include="templateUrl"></div>
</div>
</body>
JS
var app = angular.module('myApp', []);
app.controller('myCtrl', function ($scope) {
var messages = [
{ text: "Standard Message", type: "success000" },
{ text: "Success Message!", type: "success111" },
{ text: "Alert Message!", type: "alert222" },
{ text: "secondary message...", type: "secondary333" }
]
$scope.name = 'shiva';
$scope.count = 0;
$scope.messages = messages;
$scope.clickMe = function () {
// alert('clicked');
$scope.count++;
$scope.templateUrl = "Page.html";
};
});
page.Html
<b> Included html Code.{{message.type}}---count={{count}}
Add to message new property clicked. Set ng-include only when message.clicked is true using ng-if. And when you click, change state of message.clicked on true
Somthing like this:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl',function($scope){
var messages = [
{ text: "Standard Message", type: "success0" ,clicked:false},
{text:"Success Message!", type:"success00",clicked:false},
{text:"Alert Message!", type : "alert2",clicked:false},
{text:"secondary message...", type : "secondary3",clicked:false}
]
$scope.name = 'shiva';
$scope.count = 0;
$scope.messages = messages;
$scope.clickMe = function (message) {
// alert('clicked');
$scope.count++;
message.clicked=true;
$scope.templateUrl = "Page.html";
};
});
</script>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="message in messages">
<a ng-click="clickMe(message)">Clike Me</a>
<span>{{message.text}}---{{message.type}}</span>
<div ng-if="message.clicked" ng-include="templateUrl"></div>
</div>
</body>
</html>
First you need to add an id your json data as follows:
var messages = [
{ id: "101", text: "Standard Message", type: "success0", clickCount: 0 },
{ id: "102", text: "Success Message!", type: "success00", clickCount: 0 },
{ id: "103", text: "Alert Message!", type: "alert2", clickCount: 0 },
{ id: "104", text: "secondary message...", type: "secondary3", clickCount: 0 }
]
Next you need to run a filter and find the item on ng-click
$scope.clickMe = function (obj) {
$scope.messages.forEach(function (value, key) {
if (value.id === obj.id) {
value.isClicked = true;
value.clickCount++;
}
});
$scope.count++;
$scope.templateUrl = "Page.html";
};
After that, make changes into your Page.html
<p class="info">{{ name }}--{{ message.type }}--{{ message.text }}</p>
<p class="element">
Current Count = ({{ message.clickCount }}), Total Count = ({{ count }})
</p>
At last you need to make the following two HTML changes
1) Add the object in ng-click
<a ng-click="clickMe(message)">Clike Me</a>
2) Specify the condition on ng-include
<div ng-include="templateUrl" ng-if="message.isClicked"></div>
Demo
SUMMARYI have a list of brands and a list of products. I am using an ng-repeat to show the list of brands, and an ng-repeat with a filter to show the list of products within their respective brands. I want each brand and each product to have a button that shows more about that brand/product. All of these buttons should use the same function on the controller.
PROBLEMThe button that shows more about the brand also shows more about each of that brand's products, UNLESS (this is the weird part to me) I click the button of a product within that brand first, in which case it will work correctly.
CODEPlease see the Plunker here, and note that when you click on 'show type' on a brand, it also shows all the types of the products within that brand: http://plnkr.co/edit/gFnq3O3f0YYmBAB6dcwe?p=preview
HTML
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="MyController as vm">
<div ng-repeat="brand in brands">
<h1>
{{brand.name}}
</h1>
<button ng-click="showType(brand)">
Show Brand Type
</button>
<div ng-show="show">
{{brand.type}}
</div>
<div ng-repeat="product in products
| filter:filterProducts(brand.name)">
<h2>
{{product.name}}
</h2>
<button ng-click="showType(product)">
Show Product Type
</button>
<div ng-show="show">
{{product.type}}
</div>
</div>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src="script.js"></script>
</body>
</html>
JAVASCRIPT
var app = angular.module('myApp', []);
app.controller('MyController', function($scope) {
$scope.brands = [{
name: 'Kewl',
type: 'Cereal'
}, {
name: 'Joku',
type: 'Toy'
}, {
name: 'Loko',
type: 'Couch'
}]
$scope.products = [{
name: 'Kewlio',
type: 'Sugar Cereal',
brand: 'Kewl'
}, {
name: 'Kewliano',
type: 'Healthy Cereal',
brand: 'Kewl'
}, {
name: 'Jokurino',
type: 'Rattle',
brand: 'Joku'
}, {
name: 'Lokonoko',
type: 'Recliner',
brand: 'Loko'
}, {
name: 'Lokoboko',
type: 'Love Seat',
brand: 'Loko'
}]
$scope.showType = function(item) {
this.show = !this.show;
}
$scope.filterProducts = function(brand) {
return function(value) {
if(brand) {
return value.brand === brand;
} else {
return true;
}
}
}
});
IMPORTANT NOTE: I realize I could add an attribute to the object (brand.show) and pass the object into the function, then change that attribute to true/false, but I don't want to do this because in my actual application, the button will show a form that edits the brand/product and submits the info to Firebase, and I don't want the object to have a 'show' attribute on it. I would rather not have to delete the 'show' attribute every time I want to edit the info in Firebase.
ng-repeat directive create own scope, when you do
this.show = !this.show
you create/change show property in current scope, if click brand button - for brand scope, that global for product, and when click in product button - for scope concrete product.
To avoid this, you should create this property before clicking button, for example with ng-init, like
ng-init="show=false;"
on element with `ng-repeat" directive
Sample
var app = angular.module('myApp', []);
app.controller('MyController', function($scope) {
$scope.brands = [{
name: 'Kewl',
type: 'Cereal'
}, {
name: 'Joku',
type: 'Toy'
}, {
name: 'Loko',
type: 'Couch'
}]
$scope.products = [{
name: 'Kewlio',
type: 'Sugar Cereal',
brand: 'Kewl'
}, {
name: 'Kewliano',
type: 'Healthy Cereal',
brand: 'Kewl'
}, {
name: 'Jokurino',
type: 'Rattle',
brand: 'Joku'
}, {
name: 'Lokonoko',
type: 'Recliner',
brand: 'Loko'
}, {
name: 'Lokoboko',
type: 'Love Seat',
brand: 'Loko'
}]
$scope.showType = function(item) {
this.show = !this.show;
}
$scope.filterProducts = function(brand) {
return function(value) {
if (brand) {
return value.brand === brand;
} else {
return true;
}
}
}
});
/* Styles go here */
h1 {
font-family: impact;
}
h2 {
font-family: arial;
color: blue;
margin-bottom: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyController as vm">
<div ng-repeat="brand in brands" ng-init="show=false">
<h1>
{{brand.name}}
</h1>
<button ng-click="showType(brand)">
Show Brand Type
</button>
<div ng-show="show">
{{brand.type}}
</div>
<div ng-repeat="product in products
| filter:filterProducts(brand.name)" ng-init="show=false">
<h2>
{{product.name}}
</h2>
<button ng-click="showType(product)">
Show Product Type
</button>
<div ng-show="show">
{{product.type}}
</div>
</div>
</div>
</div>
</div>
The easiest fix for this, if you don't mind putting temporary properties in your data is the following changes:
<div ng-show="product.show">
{{product.type}}
</div>
and
<div ng-show="brand.show">
{{brand.type}}
</div>
and then in your controller
$scope.showType = function(item) {
item.show = !item.show;
}
Alternatively, if you don't want to touch the object properties, you can create an $scope.shownTypes array and have your click either push the object into or remove the object from the shown array. THen you can check for the object's existence in the array and show or not show the type appropriately. Let me know if you need a sample of that.
Your show boolean attribute same for whole tree (is in same scope). Using angular directive with child scope scope:true in ng-repeat helps to isolate each show property. I have forked your plunker code:
http://plnkr.co/edit/cMSvyfeCQOnTKG8F4l55?p=preview
I have developed a single page multiple choice quiz web application using AngularJS. I am trying to get the correct answer from the array list to show once the user selects a wrong answer after hitting the submit button inside the tags. I know it's something simple, I know I'm over analysing it to make it seem more complex than what it's not. Every attempt I made show all of the option choices from the array list or either the correct number position value in the array list instead of the single correct answer. I'm also still new to using AngularJS.
HTML
<!DOCTYPE html>
<html ng-app="quizApp">
<head>
<meta charset="utf-8" />
<title>QuizApp</title>
<link rel="stylesheet" href="style.css" />
<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body>
<div class="container">
<h1 class="title">QuizApp</h1>
<quiz/>
</div>
</body>
template
<div class="quiz-area" ng-show="inProgress">
<div ng-show="!quizOver">
<h2 id="question">{{question}}</h2>
<ul id="options">
<li ng-repeat="option in options">
<label>
<input type="radio" name="answer" value="{{option}}">
{{option}}
</label>
</li>
</ul>
<button ng-click="checkAnswer()" ng-show="answerMode">Submit</button>
<div ng-show="!answerMode">
<button ng-click="nextQuestion()" class="next-question">Next</button>
<span ng-show="correctAns">That is correct!</span>
<span ng-show="!correctAns">Sorry, that is an incorrect answer.</span>
</div>
</div>
<div ng-show="quizOver">
<h2>Quiz is over</h2>
<button ng-click="reset()">Play again</button>
</div>
<div id="score">
Score: {{score}}
</div>
app.js
var app = angular.module('quizApp', []);
app.directive('quiz', function(quizFactory) {
return {
restrict: 'AE',
scope: {},
templateUrl: 'template.html',
link: function(scope, elem, attrs) {
scope.start = function() {
scope.id = 0;
scope.quizOver = false;
scope.inProgress = true;
scope.getQuestion();
};
scope.reset = function() {
scope.inProgress = false;
scope.score = 0;
}
scope.getQuestion = function() {
var q = quizFactory.getQuestion(scope.id);
if(q) {
scope.question = q.question;
scope.options = q.options;
scope.answer = q.answer;
scope.answerMode = true;
} else {
scope.quizOver = true;
}
};
scope.checkAnswer = function() {
if(!$('input[name=answer]:checked').length) return;
var ans = $('input[name=answer]:checked').val();
if(ans == scope.options[scope.answer]) {
scope.score++;
scope.correctAns = true;
} else {
scope.correctAns = false;
}
scope.answerMode = false;
};
scope.nextQuestion = function() {
scope.id++;
scope.getQuestion();
}
scope.reset();
}
}
});
app.factory('quizFactory', function() {
var questions = [
{
question: "Which is the largest country in the world by population?",
options: ["India", "USA", "China", "Russia"],
answer: 2
},
{
question: "When did the second world war end?",
options: ["1945", "1939", "1944", "1942"],
answer: 0
},
{
question: "Which was the first country to issue paper currency?",
options: ["USA", "France", "Italy", "China"],
answer: 3
},
{
question: "Which city hosted the 1996 Summer Olympics?",
options: ["Atlanta", "Sydney", "Athens", "Beijing"],
answer: 0
},
{
question: "Who invented telephone?",
options: ["Albert Einstein", "Alexander Graham Bell", "Isaac Newton", "Marie Curie"],
answer: 1
}
];
return {
getQuestion: function(id) {
if(id < questions.length) {
return questions[id];
} else {
return false;
}
}
};
});
On my page I have a dynamic list of musicians (players) whereas a player can be removed and added to the list. Each player shall have multiple instruments which is also a dynamic list, whereas an instrument can be added or removed from a player's instrument list. So we are talking about two nested dynamic lists.
Here is the code and the problem description under it.
jamorg.html:
<!DOCTYPE html>
<html ng-app='jamorgApp'>
<head>
<link rel="stylesheet" type="text/css" href="C:\Users\jazzblue\Documents\Bootstrap\bootstrap-3.3.2-dist\css\bootstrap.min.css" />
<title>Jam Organizer</title>
</head>
<body>
<div ng-controller='JamOrgController as jamOrg'>
<h1>Jam</h1>
<div ng-repeat='player in players'>
<div>
<h3 style="display: inline-block;">player {{$index}}</h3>
<button ng-click="removePlayer($index)">Remove</button>
</div>
<br/>
<div ng-controller='JamOrgPlayerController as jamOrgPlayer'>
<div ng-repeat='instrument in player'>
<span>Instrument: {{instrument.instrument}},</span>
<span>Level: {{instrument.level}}</span>
<button ng-click="remove($index)">Remove</button>
</div>
<button ng-click="addInstrument()">Add Instrument</button>
Instrument: <input ng-model='newInstrument.instrument'>
Level: <input ng-model='newPlayer.level'>
</div>
</div>
</div>
<script type="text/javascript" src="C:\Users\jazzblue\Documents\AngularJS\angular.min.js"></script>
<script type="text/javascript" src="jamorgApp.js"></script>
</body>
</html>
jamorgApp.js
var app = angular.module('jamorgApp', []);
app.controller('JamOrgController', ['$scope', function($scope){
$scope.players = players;
$scope.removePlayer = function(index) {
$scope.players.splice(index, 1);
}
}]);
app.controller('JamOrgPlayerController', ['$scope', function($scope){
$scope.newInstrument = newInstrument;
$scope.remove = function(index) {
$scope.player.splice(index, 1);
}
$scope.addInstrument = function() {
$scope.player.push(newInstrument);
}
}]);
var players = [
[{instrument: 'Guitar', level: 3}, {instrument: 'Keyboard', level: 3}],
[{instrument: 'Bass', level: 4}],
[{instrument: 'Drums', level: 3}]
];
var newInstrument = [
{instrument: 'x', level: 'y'}
]
Here is my problem: the same newInstrument is being added to all the different players lists which is wrong: each player's instrument list should have its own newInstrument.
How should I change it to get the right design?
Thanks!
Where you do:
$scope.addInstrument = function() {
$scope.player.push(newInstrument);
}
Try doing:
$scope.addInstrument = function() {
$scope.player.push(angular.copy(newInstrument));
}
Update:
In your HTML:
<button ng-click="addInstrument(player)">Add Instrument</button>
In your JS:
$scope.addInstrument = function(player) {
player.push(angular.copy(newInstrument));
}
UPDATE
I created a fiddle where you can check some possible modifications to your code. It uses just one controller and fixes the duplicated object issues.
<button ng-click="addInstrument($index)">Add Instrument</button>
Instrument: <input ng-model='newInstrument.instrument'>
Level: <input ng-model='newPlayer.level'>
and your addInstrument function should be like this
$scope.addInstrument = function(index) {
$scope.players[index].push($scope.newInstrument);
}
I am trying to add objects to $scope.orderitems and if the object is already in the $scope.orderitems I want to change the quantity property of the object using angular.forEach instead of adding another object. Whenever I call the additem function I get an error that orderitems is not defined.
Here is my code
HTML
<input type="text" ng-model="item.name">
<button type="button" ng-click="additem(item)">
<ul>
<li ng-repeat="orderitem in orderitems">
<p>{{ orderitem.name }} | {{ orderitem.qty }}</p>
</li>
</ul>
JS
app.controller('OrderCtrl', function($scope) {
$scope.orderitems = [];
$scope.additem = function(data) {
angular.forEach(orderitems, function(orderitem) {
if (orderitem.id === data.id) {
orderitem.qty = orderitem.qty++;
} else {
data.qty = 1;
$scope.orderitems.push(data);
}
});
};
});
Here's a working fiddle: http://jsfiddle.net/rodhartzell/hbN4G/
HTML
<body ng-app="app">
<div ng-controller="OrderCtrl">
<input type="text" ng-model="item.name">
<button type="button" ng-click="additem()">add</button>
<ul>
<li ng-repeat="orderitem in orderitems">
<p>{{ orderitem.name }} | {{ orderitem.qty }}</p>
</li>
</ul>
</div>
CONTROLLER
var app = angular.module('app', []);
app.controller('OrderCtrl', function($scope) {
$scope.orderitems = [{name: 'foo', id:1, qty:1}];
$scope.additem = function() {
var exists = false;
var data = {
id : $scope.orderitems.length + 1,
name: $scope.item.name,
qty: 1
}
angular.forEach($scope.orderitems, function (item) {
if (item.name == data.name) {
exists = true;
item.qty++;
return false;
}
});
if(!exists){
$scope.orderitems.push(data);
}
};
});
$scope.orderitems = [];
$scope.additem = function(data) {
var item = null
angular.forEach(orderitems, function(orderItem) {
if (orderItem.id === data.id) {
item = orderItem
return false;
}
});
if (item == null) {
item = {
id: data.id, qty : 0
}
$scope.orderitems.push(item)
}
item.qty++
};