Dynamically change button label inside ng-repeat - javascript

Consider a simple ng-repeat which creates a directive in each iteration. Each directive contains a button that triggers a function. These buttons should have the label set to "Show", and should change to "Hide" when the button is clicked. When I click on a button I want to check if there are other buttons set to "hide": if yes, they should revert to "show". Basically my goal is to only have one button with the label set to "Hide", others should always be "Show". How can I do that?
<div ng-repeat="campaign in $root.transactions">
<my-directive campaign='campaign' index='$index></my-directive>
</div>
myDirective.html:
<div>
..some stuff...
<button ng-click="toggleDetail()">{{labelButton}}</button>
</div>
js:
$scope.labelButton = 'Show';
$scope.detailOpened = false;
$scope.labelButton = 'Show';
$scope.$root.selectedIndex = -1;
$scope.toggleDetail = function($event, index){
...do stuff...
$scope.detailOpened = !$scope.detailOpened;
$scope.$root.selectedIndex = index;
$(element).toggleClass('selectedActivity');
if($scope.detailOpened === false) {
$scope.labelButton = 'Show';
}else {
$scope.labelButton = 'Hide';
}
};

With ng-repeat, you'll need an array in $scope. Using directive will do, but may not be necessary.
I have made a jsfiddle here: http://jsfiddle.net/goodman/z9kg0md0/15/embedded/result/
I wonder if this is what you want. Codes are here:
angular.module("MyApp",[])
.controller( 'myController', [ '$scope', function( $scope ){
$scope.buttons = [
{ detailOpened: false, label: 'Show1'},
{ detailOpened: false, label: 'Show2'},
{ detailOpened: false, label: 'Show3'},
{ detailOpened: false, label: 'Show4'},
{ detailOpened: false, label: 'Show5'}
];
$scope.toggleDetail = function(index){
$scope.buttons[index].detailOpened = !$scope.buttons[index].detailOpened;
if(!$scope.buttons[index].detailOpened) {
$scope.buttons[index].label = 'Show';
}else {
$scope.buttons[index].label = 'Hide';
}
if( $scope.buttons[index].detailOpened ){
for( var i = 0; i < $scope.buttons.length ; i++ ){
if( i != index && $scope.buttons[i].detailOpened) {
$scope.buttons[i].detailOpened = false;
$scope.buttons[i].label = 'Show';
}
}
}
};
}]);
and html:
<div ng-app="MyApp" ng-controller="myController">
<div ng-repeat="button1 in buttons">
<button ng-click="toggleDetail($index)">
{{button1.label}}
</button>
</div>
</div>

Related

How to toggle a form through button?

I would like to show a form through button click in JS vanilla but nothing work. Here is my code
/* ======= Model ======= */
var model = {
currentCat: null,
cats: [
{
clickCount : 0,
name : 'Tabby',
imgSrc : 'img/434164568_fea0ad4013_z.jpg',
imgAttribution : 'https://www.flickr.com/photos/bigtallguy/434164568'
},
{
clickCount : 0,
name : 'Tiger',
imgSrc : 'img/4154543904_6e2428c421_z.jpg',
imgAttribution : 'https://www.flickr.com/photos/xshamx/4154543904'
},
{
clickCount : 0,
name : 'Scaredy',
imgSrc : 'img/22252709_010df3379e_z.jpg',
imgAttribution : 'https://www.flickr.com/photos/kpjas/22252709'
},
{
clickCount : 0,
name : 'Shadow',
imgSrc : 'img/1413379559_412a540d29_z.jpg',
imgAttribution : 'https://www.flickr.com/photos/malfet/1413379559'
},
{
clickCount : 0,
name : 'Sleepy',
imgSrc : 'img/9648464288_2516b35537_z.jpg',
imgAttribution : 'https://www.flickr.com/photos/onesharp/9648464288'
}
]
};
/* ======= Octopus ======= */
var octopus = {
init: function() {
// set our current cat to the first one in the list
model.currentCat = model.cats[0];
// tell our views to initialize
catListView.init();
catView.init();
adminView.init();
},
getCurrentCat: function() {
return model.currentCat;
},
getCats: function() {
return model.cats;
},
// set the currently-selected cat to the object passed in
setCurrentCat: function(cat) {
model.currentCat = cat;
},
// increments the counter for the currently-selected cat
incrementCounter: function() {
model.currentCat.clickCount++;
catView.render();
}
};
/* ======= View ======= */
var catView = {
init: function() {
// store pointers to our DOM elements for easy access later
this.catElem = document.getElementById('cat');
this.catNameElem = document.getElementById('cat-name');
this.catImageElem = document.getElementById('cat-img');
this.countElem = document.getElementById('cat-count');
// on click, increment the current cat's counter
this.catImageElem.addEventListener('click', function(){
octopus.incrementCounter();
});
// render this view (update the DOM elements with the right values)
this.render();
},
render: function() {
// update the DOM elements with values from the current cat
var currentCat = octopus.getCurrentCat();
this.countElem.textContent = currentCat.clickCount;
this.catNameElem.textContent = currentCat.name;
this.catImageElem.src = currentCat.imgSrc;
}
};
var catListView = {
init: function() {
// store the DOM element for easy access later
this.catListElem = document.getElementById('cat-list');
// render this view (update the DOM elements with the right values)
this.render();
},
render: function() {
var cat, elem, i;
// get the cats we'll be rendering from the octopus
var cats = octopus.getCats();
// empty the cat list
this.catListElem.innerHTML = '';
// loop over the cats
for (i = 0; i < cats.length; i++) {
// this is the cat we're currently looping over
cat = cats[i];
// make a new cat list item and set its text
elem = document.createElement('li');
elem.textContent = cat.name;
// on click, setCurrentCat and render the catView
// (this uses our closure-in-a-loop trick to connect the value
// of the cat variable to the click event function)
elem.addEventListener('click', (function(catCopy) {
return function() {
octopus.setCurrentCat(catCopy);
catView.render();
};
})(cat));
// finally, add the element to the list
this.catListElem.appendChild(elem);
}
}
};
var adminView = {
init: function() {
this.formElement = document.getElementById('admin-form');
this.buttonAdmin = document.getElementById('admin-button');
console.log(this);
this.render();
},
render: function() {
this.formElement.style.display = 'none';
console.log(this);
this.buttonAdmin.addEventListener('click', (function(form){
return form.style.display = 'block';
})(this.formElement));
console.log(this);
}
};
// make it go!
octopus.init();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cat Clicker</title>
</head>
<body>
<ul id="cat-list"></ul>
<div id="cat">
<h2 id="cat-name"></h2>
<div id="cat-count"></div>
<img id="cat-img" src="" alt="cute cat">
</div>
<div>
<button type="button" id="admin-button">Admin</button>
</div>
<div id="admin-form">
<p>Name</p>
<input type="text" name="new-cat-name" value="">
<p>Img URL</p>
<input type="text" name="new-img-url" value="">
<p># Count</p>
<input type="text" name="new-count-number" value="">
<br>
<button type="button" name="save">Save</button>
<button type="button" name="cancel">Cancel</button>
</div>
<script src="js/app.js"></script>
<script>
if(!document.getElementById('admin-button').active){
document.getElementById('admin-form').style.display ='none';
}
</script>
</body>
</html>
I don't understand why the form (#admin-form) is shown when the page is loaded, it should be shown only when the button (#admin-button) is clicked.
Update: I added the script to hide the form in HTML file.
Here's your problem:
this.buttonAdmin.addEventListener('click', (function(form){
return form.style.display = 'block';
})(this.formElement));
You wrap the handler function in parens and call it immediately. Try:
this.buttonAdmin.addEventListener('click', function(){
this.formElement.style.display = 'block';
}.bind(this));
Or, if you don't want to use bind for whatever reason:
var form = this.formElement;
this.buttonAdmin.addEventListener('click', function(){
form.style.display = 'block';
});

Strange behavior when splicing array, javascript

I'm working with list of checkboxes and I have next logic behavior for it:
if all items selected, checkbox "select all" is checked
if one of all selected items has been unchecked, checkbox "select all" is unchecked as well
This logic is clear. Depends of what item is checked I extract its id to an additional array and then using this array for request that to get data.
For pushing everything works fine, but for slicing the logic is strange. So I can slice the array until first item is checked, however I unchecked the first item, pushed and sliced items no more related with checkboxes.
I have reproduced plunker with it, so I appreciate if anybody could help me to find what I'm missing.
$scope.modelClass = {
selectedAll: false
};
$scope.selectAllClass = function (array) {
angular.forEach(array, function (item) {
item.selected = $scope.modelClass.selectedAll;
$scope.param =''
});
};
$scope.checkIfAllClassSelected = function (array) {
$scope.modelClass.selectedAll = array.every(function (item) {
return item.selected == true
});
$scope.checked = array.filter(function (item) {
return item.selected == true
}).length;
angular.forEach(array, function (obj) {
if(obj.selected == true){
requestClass(obj)
}
});
};
var selectedClass = [];
var requestClass = function (obj) {
selectedClass.push(obj);
angular.forEach(selectedClass, function (val) {
if (val.selected != true) {
selectedClass.splice(selectedClass.indexOf(val.id), 1);
}
else {
selectedClass = selectedClass.filter(function (elem, index, self) {
return index == self.indexOf(elem);
})
}
});
$scope.param = _.map(selectedClass, 'id')
};
$scope.classes = [
{"id":4,"name":"Achievement","selected":false},
{"id":13,"name":"Information","selected":false},
{"id":6,"name":"Issue","selected":false},
{"id":5,"name":"Message","selected":false},
{"id":9,"name":"Request","selected":false}
]
The logic looks good for me, not sure what's wrong here. I've took the first solution from this post (it looks like you are using the second one) and slightly modified it for your needs.
$scope.model = {
selectedClass : []
}
$scope.isSelectAll = function(){
$scope.model.selectedClass = [];
if($scope.master){
$scope.master = true;
for(var i=0;i<$scope.classes.length;i++){
$scope.model.selectedClass.push($scope.classes[i].id);
}
}
else{
$scope.master = false;
}
angular.forEach($scope.classes, function (item) {
item.selected = $scope.master;
});
$scope.param = $scope.model.selectedClass
}
$scope.isChecked = function() {
var id = this.item.id;
if(this.item.selected){
$scope.model.selectedClass.push(id);
if($scope.model.selectedClass.length == $scope.classes.length ){$scope.master = true;
}
} else {
$scope.master = false;
var index = $scope.model.selectedClass.indexOf(id);
$scope.model.selectedClass.splice(index, 1);
}
$scope.param = $scope.model.selectedClass
}
$scope.classes = [
{"id":4,"name":"Achievement","selected":false},
{"id":13,"name":"Information","selected":false},
{"id":6,"name":"Issue","selected":false},
{"id":5,"name":"Message","selected":false},
{"id":9,"name":"Request","selected":false}
]
html
<div ng-class="{'selected': master, 'default': !master}">
<div>
<input type="checkbox" ng-model="master" ng-change="isSelectAll()" > Select all
</div>
</div>
<div ng-repeat="item in classes | orderBy : 'id'" ng-class="{'selected': item.selected, 'default': !item.selected}">
<div >
<input type="checkbox" ng-model="item.selected" ng-change="isChecked()">
{{ item.name }}
</div>
</div>
this is fixed plunker

Adding and removing items from arrays, based on a condition in AngularJS

I'm trying to make a dynamic "favorites" toggle-button.
When clicking the button, it should add the selected player to the users favorite-list. If the player is already favorited, it should remove it.
I've also tried to iterate through the favorites, to check if a player is already favorited. If true, it colors the favorite star gold.
A few problems here. My for loop for checking seems to be working properly as long as the array only contains one item. But as soon as I try adding more, the gold icon is only gold colored on the last player added to favorites. So the check only finds one favorite at a time, and I can add a player to favorites many times, as long as I vary the players I add.
If someone could point me in the right direction and help me understand why my loop isn't working correctly, that would be awesome!
http://codepen.io/utrolig/pen/LNgRwv
Javascript
angular.module('test', [])
.controller('TestController', function($scope){
$scope.players = [
{
uniqueid: "gem",
name: "Ole Christian",
cake: false,
},{
uniqueid: "utrolig",
name: "Stian",
cake: false,
},{
uniqueid: "drozo",
name: "Adrian",
cake: false,
}
];
$scope.user = {
name: "Stian",
username: "stiba",
favorites: [{uniqueid: "drozo"}],
}
$scope.checkFavorite = function(id){
fav = $scope.user.favorites;
var exists;
for (var i=0; i < fav.length; i++){
if(fav[i].uniqueid == id){
exists = true;
} else {
exists = false;
}
}
return exists;
}
$scope.toggleFavorite = function(id){
fav = $scope.user.favorites;
if(fav.length === 0){
var newfav = {uniqueid: id};
fav.push(newfav);
} else {
if($scope.checkFavorite(id) === true){
for(var i = 0; i < fav.length; i++){
if (fav[i].uniqueid === id) fav.splice(i, 1);
}
} else if ($scope.checkFavorite(id) === false) {
var newfav = {uniqueid: id};
fav.push(newfav)
} else {
console.log('Error!');
}
}
}
$scope.isFavorited = function(){
return true;
};
})
HTML
<body ng-app="test">
<div class="container" ng-controller="TestController">
<h3>Players</h3>
<div ng-repeat="player in players" class="player-cont">
<div class="player">
<div class="favorite" ng-click="toggleFavorite(player.uniqueid)" ng-class="{'active': checkFavorite(player.uniqueid)}">
<i class="material-icons">star</i>
</div>
<i class="material-icons player-icon">person</i>
</div>
<div>
<p ng-bind="player.uniqueid"></p>
<p ng-bind="player.name"></p>
</div>
</div>
<h3>Favorites</h3>
<div ng-repeat="favorite in user.favorites track by $index">
<h5>{{favorite.uniqueid}}</h5>
</div>
<p class="user">
{{user.favorites}}
</p>
</div>
</body>
There's a couple of errors in your code.
The first is checkFavorite, if you examine the code you'll see that only the last item is actually compared to id, since the exists flag is updated for each item. You need to "short circuit" the loop and return true as soon as you find a value.
btw, is* is a common name convention for checking boolean values.
$scope.isFavorite = function(id){
var fav = $scope.user.favorites;
for (var i=0; i < fav.length; i++){
if(fav[i].uniqueid == id){
return true;
}
}
return false;
}
Your toggle is also very verbose, if you "reduce" the code you end up with something like this
$scope.toggleFavorite = function(id){
var fav = $scope.user.favorites;
// no previous items, remove, OK
if(fav.length === 0) {
var newfav = {uniqueid: id};
fav.push(newfav);
return;
}
// if already a favorite, uncheck/remove
if($scope.isFavorite(id)) {
for(var i = 0; i < fav.length; i++){
if (fav[i].uniqueid === id) fav.splice(i, 1);
}
}
// otherwise add the item
// remember, isFavorite returns true of false, there is no third state
else { // if ($scope.isFavorite(id) === false) {
var newfav = {uniqueid: id};
fav.push(newfav)
}
}
This can be edited further, since the isFavorite function will return false if the list is empty, i.e. no need for the first if
$scope.toggleFavorite = function(id){
var fav = $scope.user.favorites;
// if already a favorite, uncheck/remove
if($scope.isFavorite(id)) {
for(var i = 0; i < fav.length; i++){
if (fav[i].uniqueid === id) {
fav.splice(i, 1);
// unless the item exists more than once, break the loop
break;
}
}
}
// otherwise add the item
else {
var newfav = {uniqueid: id};
fav.push(newfav)
}
}

Programatically unchecking ng-checkbox in AngularJS on removal from array

I am using a number of checkboxes to push their values to an array to be used as arguments for the filtering of a dataset.
The requirement is to:
Show child filters on selection of the parent category.
If parent is unchecked all its children should be unchecked (false) automatically (otherwise they will be hidden and still true)
Display active filters
remove active filter on click (also uncheck their corresponding checkbox programatically).
Clear all filters and uncheck all checkboxes.
My current code is below, however see attached fiddle.
$scope.IsChecked = false;
$scope.ActiveFilters = [];
$scope.clearFilters = function() {
$scope.ActiveFilters = [];
};
$scope.ModifyFilter = function (IsChecked, Filter) {
console.log(IsChecked);
if (IsChecked) {
$scope.ActiveFilters.push(Filter);
}
else {
var indexz = $scope.ActiveFilters.indexOf(Filter);
$scope.ActiveFilters.splice(indexz, 1);
}
};
A fiddle with a semi-working demonstration is here
-- EDIT --
Further explanation: When a checkbox is checked it pushed its value to the array. if I remove this from the array by clicking on its name in the 'Active Filters' section at the bottom of the fiddle then it does not uncheck the checkbox. Neither does clicking on 'Clear Filters'.
The problem is in your html bindings. so please post this code here.
You using for "IsChecked" variable. This is local scope variable created for each iteration of loop. You not change it from your script code.
Updated html:
<body ng-app="DemoApp" ng-controller="DashboardCtrl">
<div ng-repeat="group in Filters">
<input type="checkbox" value="{{group.title}}" ng-model="CheckboxParent" />
{{group.title}}
<div ng-show="CheckboxParent" class="animate-show" ng-repeat="filter in group.filters">
<input type="checkbox" class="filterChild" value="{{filter.name}}" ng-model="filter.checked" ng-change="ModifyFilter(filter.checked,filter)"/>
{{filter.name}}
</div>
</div>
<div>
<h4>Active Filters</h4>
<p class="clear-filters" ng-click="clearFilters()">Clear Filters</p>
<ul ng-repeat="activeFilter in ActiveFilters">
<li ng-model="activeFilter.checked" ng-click="removeFilter(ModifyFilter(activeFilter.checked,activeFilter))">{{activeFilter.name}}</li>
</ul>
</div>
</body>
Updated script:
var app = angular.module("DemoApp", [])
app.controller("DashboardCtrl", function($scope) {
$scope.clearFilters = function() {
angular.forEach($scope.Filters[0].filters, function(filter) {
filter.checked = false;
});
$scope.ActiveFilters = [];
};
$scope.IsChecked = false;
$scope.ActiveFilters = [];
$scope.ModifyFilter = function (IsChecked, Filter) {
console.log(IsChecked);
if (IsChecked) {
$scope.ActiveFilters.push(Filter);
}
else {
var indexz = $scope.ActiveFilters.indexOf(Filter);
$scope.ActiveFilters.splice(indexz, 1);
}
};
$scope.Filters =
[
{
"title": "Equipment",
"filters": [
{ name: "Rope", checked: false },
{ name: "Cables", checked: false },
{ name: "Dowel", checked: false },
{ name: "Ball", checked: false }
]
}
];
});
Look into my solution on js fiddle: JsFiddle

AngularJS : How to change a value in all items in an ng-repeat

I have a data model that comes in, by default I give the first ticker a selected status of true.
Now when a user clicks another item, I need to efficiently deselect all the others and set the selected value to true for the item clicked:
<li class="ticker-li"
ng-repeat="ticker in tickers"
ng-hide="ticker.removed"
ng-class="{'selected':ticker.selected}"
ng-mouseleave="hideTickerOptions()">
<div class="ticker"
ng-click="unselectAll(); ticker.selected = !ticker.selected;
selectTicker(ticker);">
{{ticker.ticker}}
</div>
</li>
Tried a forEach function here, but with the error [object Array] is not a function:
var vs = $scope;
vs.unselectAll = function() {
vs.tickers.forEach(vs.tickers, function(ticker) {
ticker.selected = false;
});
};
A regular for-loop will work, but is it an efficient way to toggle all the selected values to false?
for (var i = 0; i < vs.tickers.length; i++) {
vs.tickers[i].selected = false;
}
My thinking here is run this unselectAll function to deselect everything, then the next code in the markup which select the current item:
<div class="ticker"
ng-click="unselectAll(); ticker.selected = !ticker.selected;
selectTicker(ticker);">
{{ticker.ticker}}
</div>
I tried to do it using radio inputs.
var app = angular.module('AppForm', []);
app.controller('ctrForm', function ($scope) {
$scope.choices = [
{ id: 'Choice1', check: false },
{ id: 'Choice2', check: false },
{ id: 'Choice3', check: false },
{ id: 'Choice4', check: false },
{ id: 'Choice5', check: false },
{ id: 'Choice6', check: false },
{ id: 'Choice7', check: false }
];
$scope.setDefault = function (item) {
angular.forEach($scope.choices, function (p) {
p.check = false; //set them all to false
});
item.check = true; //set the clicked one to true
};
});
.selected {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.21/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div ng-app="AppForm" ng-controller="ctrForm">
<ul>
<li style="list-style:none" ng-class="{'selected':item.check}" ng-repeat="item in choices"><input type="radio" name="group" ng-model="item.check" ng-click="setDefault(item)" value="true" />{{item.id}}...{{item.check}}</li>
</ul>
</div>
The foreach wouldn't be less efficient than anything angular could do 'for free'. Either way it'll have to set the others as unselected..
What'd I'd propose instead would be to have a single thing "selectedItem" and a set of things "items".
then you can 'say'
<li class="ticker-li"
ng-repeat="ticker in tickers"
ng-hide="ticker.removed"
ng-class="{'selected':ticker == selectedTicker}"
ng-mouseleave="hideTickerOptions()">
<div class="ticker" ng-click="selectedTicker = ticker">
{{ticker.ticker}}
</div>
</li>
you could then access selectedTicker to get it's properties
<div>{{selectedTicker.name}}</div>
If your set on using ticker.selected, you can use a jquery .each loop to unselect all of them.
$scope.selectTicker = function(ticker)
{
//deselect all tickers
$.each(tickers, function() { this.selected = false; });
//select the ticker you passed in
ticker.selected = true;
}
and in your view:
<div class="ticker"ng-click="selectTicker(ticker);">
{{ticker.ticker}}
</div>

Categories

Resources