Ng-click/ ng-repeat issues - javascript

I'm trying to create an accordion using angular js and angular material. The problem is that when i'm using ng-repeat, all accordions in the array open when I click the button. I want only want to open the one that i click. Any ideas how I accomplish this? I have tried to google but i'm not finding exactly what i'm looking for
This is my html
<div class="accordionwrapper" layout="column" layout-align="center center">
<div class="accordion" ng-repeat="question in questions">
<div class="accordionheader" layout="row" layout-align="space-between center">
<h3>{{question.q}}</h3>
<md-button class="md-icon-button md-accent" aria-label="Favorite" ng-click="toggleaccordion()" ng-if="!accordionOpen">
<md-icon md-svg-icon="images/add.svg"></md-icon>
</md-button>
<md-button class="md-icon-button md-accent" aria-label="Favorite" ng-click="toggleaccordion()" ng-if="accordionOpen">
<md-icon md-svg-icon="images/minus.svg"></md-icon>
</md-button>
</div>
<div class="accordioncontent" ng-show="accordionOpen">
<p>{{question.a}}</p>
</div>
</div>
And my js
$scope.accordionOpen = false;
$scope.toggleaccordion = function () {
$scope.accordionOpen = !$scope.accordionOpen;
console.log($scope.accordionOpen)
}
Thanks!

You need to keep track of the state of each individual accordion, using an array. This means that accordionOpen should be an array and that toggleaccordion should be like this :
$scope.toggleaccordion = function($index){
$scope.accordionOpen[$index] = !$scope.accordionOpen[$index]
Finally, you should call the function using the $index variable that is provided by angular inside an ng-repeat :
<md-button class="md-icon-button md-accent" aria-label="Favorite" ng-click="toggleaccordion($index)" ng-if="accordionOpen[$index]">

All your accordions created by using ng-repeat depend on one variable, which is accordionOpen. Create an array of boolean flags and put it into questions array so that each of the accordions would have its own flag.

When you're calling $scope.accordionOpen = !$scope.accordionOpen; the scope is the parent of all the ngRepeat scopes. They inherit the accordionOpen value.
There might be more solutions to this - set the accordionOpen = true in ngClick instead of calling controller function: EDIT: Note: This won't probably work because the md-button ng-if uses it's own scope. It's always better to stick to some kind of model object, that other answers or second solution suggest.
<md-button
class="md-icon-button md-accent"
aria-label="Favorite"
ng-click="accordionOpen = true"
ng-if="!accordionOpen">
<md-icon md-svg-icon="images/add.svg"></md-icon>
</md-button>
or add a property to the question itself
js:
$scope.toggleaccordion = function (question) {
question.$accordionOpen = !question.$accordionOpen;
}
html
<md-button ng-click="toggleaccordion(question)" ng-if="!question.$accordionOpen">
<md-icon md-svg-icon="images/add.svg"></md-icon>
</md-button>
<div class="accordioncontent" ng-show="question.$accordionOpen">
<p>{{question.a}}</p>
</div>
Read more on scopes: https://docs.angularjs.org/guide/scope

You are using a single variable in scope to toggle a collection of accordian , because of this all accordians toggle on the change of single varialbe .
To avoid it every accordian should have it's own set of toggle flag.To achieve it keep the flag with the record itself( in your case the question object)
<div class="accordionheader" layout="row" layout-align="space-between center">
<h3>{{question.q}}</h3>
<md-button class="md-icon-button md-accent" aria-label="Favorite" ng-click="toggleaccordion(question)" ng-if="!question.accordionOpen">
<md-icon md-svg-icon="images/add.svg"></md-icon>
</md-button>
<md-button class="md-icon-button md-accent" aria-label="Favorite" ng-click="toggleaccordion(question)" ng-if="question.accordionOpen">
<md-icon md-svg-icon="images/minus.svg"></md-icon>
</md-button>
</div>
<!-- In ng-show use a variable which will make sure that every object will get it's own toggle status field -->
<div class="accordioncontent" ng-show="question.accordionOpen">
<p>{{question.a}}</p>
</div>
</div>
And in your js use like this
$scope.questions.forEach(function(question){
question.accordionOpen = false ;
});
$scope.toggleaccordion = function (question) {
question.accordionOpen = !question.accordionOpen;
}

Related

Expand collapse list item using angular material

Can anyone give any suggestions for adding an Expand/Collapse view shown here.
I'd like to use Angular material with AngularJS only and not be dependant on Bootstrap etc however I can't find anything suitable in the AngularMaterial docs.
Thanks
One way is to use 2 consecutive md-list-item.
Here is the HTML Code.
<md-list flex>
<md-list-item ng-click="toggle.list1 = !toggle.list1">
<md-icon>explore</md-icon>
<span flex>Item List 1</span>
<md-icon ng-show="!toggle.list1">expand_more</md-icon>
<md-icon ng-show="toggle.list1">expand_less</md-icon>
</md-list-item>
<md-list-item ng-repeat="item in data" ng-show="toggle.list1">
<span flex-offset="5">{{item}}</span>
</md-list-item>
<md-list-item ng-click="toggle.list2 = !toggle.list2">
<md-icon>explore</md-icon>
<span flex>Item List 2</span>
<md-icon ng-show="!toggle.list2">expand_more</md-icon>
<md-icon ng-show="toggle.list2">expand_less</md-icon>
</md-list-item>
<md-list-item ng-repeat="item in data" ng-show="toggle.list2">
<span flex-offset="5">{{item}}</span>
</md-list-item>
</md-list>
JS Code:
angular.module('myApp',['ngMaterial'])
.controller('TempController', function($scope){
$scope.data = [ "Item 1", "Item 2", "Item 3", "Item 4"]
$scope.toggle = {};
});;
Here is the working Codepen.
This doesn't seem to use bootstrap.
https://github.com/LukaszWatroba/v-accordion
this should be possible code to make your own accordion with material
http://blog.sodhanalibrary.com/2016/02/accordion-with-angularjs-material-ui.html#.WKxqI1UrJaQ

change a class using angular ng-click and ng-class

I have a series of menu items with submenus and they all have an angle-down icon that it's supposed to flip upwards using a class rotate180. Problem is, I can't isolate the functionality so that it only happens in the menu I am opening and not all of them. ALSO, I need it to stay in the desired position according to the menu being open or closed. By either opening/closing the menu on clicking it or by canceling the icon flip once menu is opened. Hope that makes sense.
Here is my html
<div class="menu-item">
<md-list id="playerTrigger" ng-click="menuIsOpen = 1; changeClass()" layout="row" layout-padding="" class="layout-row" layout-align="center center" flex>
<span class="title flex" flex=""> Players</span>
<span ng-class="class"></span>
</md-list>
<div class="sub-menu" ng-show="menuIsOpen===1" ng-animate="'animate'">
<md-menu-item ng-repeat="item in vm.players">
<md-button>
<div layout="row" flex="">
<a ui-sref="{{item.linkto}}" class="">
<p flex="">{{item.title}}</p>
</a>
</div>
</md-button>
</md-menu-item>
</div>
</div>
And the controller
$scope.class = "ti-icon ti-mini right ti-angle-down";
$scope.changeClass = function() {
if ($scope.class === "ti-icon ti-mini right ti-angle-down")
$scope.class = "ti-icon ti-mini right ti-angle-down rotate180";
else
$scope.class = "ti-icon ti-mini right ti-angle-down";
};
Since you only need to toggle rotate180 class, I would keep the static classes in class attribute and the changing one in ng-class:
<span class="ti-icon ti-mini right ti-angle-down" ng-class="{'rotate180': rotate}"></span>
and in the controller:
$scope.rotate = false; //initial value
$scope.changeClass = function() {
$scope.rotate = !$scope.rotate;
}

ngClick on mdListItem and secondary button

I use angular-material (1.0.7) on my project and I would like to create a clickable list (copied from the official doc) with secondary button.
The main issue is the list-item clickable event is also fired when I click on secondary button. In the doc, they show a dialog with targetEvent but it is not what I try to do.
There is my code:
<md-list-item class="md-2-line" ng-repeat="tag in tagsCtrl.showedTags|orderBy:'title'" ng-click="tagsCtrl.navigate(tag)">
<ng-md-icon icon="label"></ng-md-icon>
<div class="md-list-item-text">
<h3>{{ tag.title }}</h3>
<p>{{ tag.slug }}</p>
</div>
<span class="md-secondary" ng-show="tag.onProcess">
<md-progress-circular md-mode="indeterminate" md-diameter="24"></md-progress-circular>
</span>
<span class="md-secondary" ng-hide="tag.onProcess">
<md-button class=" md-icon-button md-hue-3" ng-click="tagsCtrl.editTag(tag, $event)" aria-label="Edit tag">
<ng-md-icon icon="create"></ng-md-icon>
</md-button>
<md-button class=" md-icon-button md-hue-3" ng-click="tagsCtrl.deleteTag(tag)" aria-label="Delete tag">
<ng-md-icon icon="delete"></ng-md-icon>
</md-button>
</span>
</md-list-item>
The navigation function is not a dialog.
Do you know how I can fix this?
I don't know if I understood it right, but you should stop the propagation of the event from bubbling to its parent (in this case from the md-button to the md-list-item)
Check more documentation here jQuery event.stopPropagation() Method
So where you have:
ng-click="tagsCtrl.editTag(tag, $event)"
ng-click="tagsCtrl.deleteTag(tag)"
you can replace with
ng-click="tagsCtrl.editTag(tag, $event) && $event.stopPropagation()"
ng-click="tagsCtrl.deleteTag(tag) && $event.stopPropagation()"

Popover Does Not Display When Opened

I set up a basic example with Ionic Popover. However, when I open the popover, the opacity stays at zero, preventing the popover from showing. I know the method openPopover is called because I receive the opened console log in my web console. If I remove the opacity property from the console, the popover displays.
My controller..
angular.module('search')
.controller('SearchResultsController', searchResultsController)
searchResultsController.$inject = ['$ionicPopover', '$scope'];
function searchResultsController($ionicPopover, $scope) {
var vm = this;
vm.openPopover = openPopover;
activate();
function activate( ) {
$ionicPopover.fromTemplateUrl('/templates/search/filter-popover.html', {
scope: $scope
}).then(function(popover) {
console.log(popover)
vm.popover = popover;
});
}
function openPopover( $event ) {
console.log("opened")
vm.popover.show($event);
}
}
My view page...
<ion-view hide-nav-bar="true">
<signed-in-header></signed-in-header>
<ion-content class="padding has-header">
<div class="row">
<div class="col col-75 text-left">
<div>4 RESULTS FOR "263355"</div>
</div>
<div class="col col-25 text-right">
<div ng-click="searchResults.openPopover()">
<i class="icon ion-arrow-down-b"></i>
Filter
</div>
</div>
</div>
</ion-content>
<ion-footer-bar>
Ad here 1
</ion-footer-bar>
</ion-view>
My popover template.
<ion-popover-view>
<ion-header-bar>
<h1 class="title">My Popover Title</h1>
</ion-header-bar>
<ion-content>
Hello!
</ion-content>
</ion-popover-view>
Why does the popover not show and how can I fix this?
I did some digging in the ionic CSS for popover, and the opacity is set to 0 by default. You can override the opacity to 1 and the popover will display, but I found this: https://github.com/driftyco/ionic/issues/2343. Basically, you have to pass the event ($event) to popover.show() and it will work. The ionic example shows this but the documentation could be more explicit. In your code, change your template to ng-click="searchResults.openPopover($event)".
In my case i just didn't put the popover component in the entryComponent section in my shared module. I didn’t get an error until I restarted the server, don't know why

Change button's theme when sidenav toggles (angular-material)

I'm making a website using angular-material. There is a sidenav and a button. Whenever the sidenav toggles, I want the button to changes it's theme. Like, when the sidenav is open, the button's md-theme attribute is set to "cyan" and when sidenav is close, the button is set to another theme.
My html code:
<div ng-app="app" ng-controller="navCtrl" layout="column">
<section layout="row" flex>
<md-content flex class="md-padding">
<div layout="column" layout-fill layout-align="center center">
<md-button class="md-fab md-primary" md-theme="cyan" ng-click="toggle()" aria-lebel="Navicon">
<md-icon class=""></md-icon>
</md-button>
</div>
</md-content>
<md-sidenav class="md-sidenav-left" md-component-id="nav"></md-sidenav>
</section>
</div>
And my controller:
angular.module('app', ['ngMaterial'])
.controller('navCtrl', function($scope, $timeout, $mdSidenav) {
$scope.toggle = function() {
$mdSidenav('nav').toggle();
};
});
I couldn't find a way to listen to this toggling and change the theme.
The directive supports a md-is-open attribute that identifies a $scope or controller attribute value that is updated when the sidenav state is changed ( open <-> closed ).
Then, the $mdSidenav('nav').toggle() function returns a promise, so define the 'then' to set a 'theme' attribute in your controller that is referenced from the md-theme in your side-nav markup:
<md-button class="md-fab md-primary"
ng-click="toggle()" aria-label="Navicon"
md-theme="buttonTheme" >
...
<md-sidenav class="md-sidenav-left" md-component-id="nav"
md-is-open="isOpen"></md-sidenav>
...
angular.module('app', ['ngMaterial'])
.controller('navCtrl', function($scope, $timeout, $mdSidenav) {
$scope.isOpen = false;
$scope.buttonTheme = 'cyan';
$scope.toggle = function() {
$mdSidenav('nav').toggle().then( function() {
$scope.buttonTheme = ($scope.isOpen ? 'indigo' : 'cyan');
});
};
});

Categories

Resources