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

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;
}

Related

Best practice displaying menu and toolbar in Angular2

I have a webapplication where a user can log into his account. When the user is logged in now the sidenav(menu) and toolbar appears. But when the user isn't logged in (maybe he is going to login or registrate) the toolbar and sidenav should be not visible.
What is the best practice to do that in Angular2(version: 2.1.0 and angular/router version: 3.1.0)
I tried to disable the toolbar and sidenav with ngIf but It doesn't really work and I think it is not a clean solution.
Then I thought about two <router-outlet>. One for the routing when the user is logged in and one when the user is not logged in. Is this the right way to solve my problem? If yes, how does it work?
Here my app.component.html where I define my toolbar, sidenav and the <router-outlet>:
<md-sidenav-layout class="{{themeService.ActualTheme}}" fullscreen>
<div *ngIf="menuToolbarConfigService.ActualPage !== 'landing' && menuToolbarConfigService.ActualPage !== 'register'">
<md-sidenav #sidenav mode="{{menuToolbarConfigService.setSideNav(sidenav)}}">
<div>
<div class="sidenav-header">
<div class="logo-circle">
<span class="logo">GT</span>
</div>
<span class="user-name">Nico</span>
<span class="open-tasks">Offene Tasks: 4</span>
</div>
<md-list>
<div [ngClass]="{'highlight': menuToolbarConfigService.ActualPage === 'start'}" class="nav-list-item">
<md-list-item>
<md-icon>assignment</md-icon> Taskboard
</md-list-item>
</div>
<div [ngClass]="{'highlight': menuToolbarConfigService.ActualPage === 'groups'}" class="nav-list-item">
<md-list-item>
<md-icon>group</md-icon> Gruppen
</md-list-item>
</div>
<div [ngClass]="{'highlight': menuToolbarConfigService.ActualPage === 'user'}" class="nav-list-item">
<md-list-item>
<md-icon>account_circle</md-icon> Benutzer
</md-list-item>
</div>
<div [ngClass]="{'highlight': menuToolbarConfigService.ActualPage === 'user'}" class="nav-list-item">
<md-list-item>
<md-icon>search</md-icon> Taskbrowser
</md-list-item>
</div>
</md-list>
</div>
</md-sidenav>
</div>
<md-toolbar color="primary">
<span *ngIf="menuToolbarConfigService.ShowMenuIcon" (click)="sidenav.open();" class="menu-icon"><md-icon>menu</md-icon></span>
<span class="app-title">GroupTasking</span>
<!-- This fills the remaining space of the current row -->
<span class="example-fill-remaining-space"></span>
</md-toolbar>
<router-outlet></router-outlet>
</md-sidenav-layout>
Thank you in advance!

set all ng-if to false on click except clicked item

I have an ng-repeat which has a button that has a function that toggles an ng-show element inside that ng-repeat.
The inside the class movie_option span has an ng-click=toggleInfo($index):
And the div additional_info has an ng-show that shows or hides an element.
<ul ng-cloak="showResult">
<li class="search_results" ng-repeat="movie in movies | orderBy: '-release_date' track by $index">
<div class="movie_info">
<div class="movie_options">
<div class="slide">
<span class="movie_option">
<span><i class="fa fa-question-circle" aria-hidden="true" ng-click="toggleInfo($index)"></i></span>
</span>
</div>
</div>
</div>
<div class="additional_info" ng-show="hiddenDiv[$index]">
{{movie.overview}}
</div>
</li>
</ul>
When a user clicks on the icon it calls this function:
$scope.toggleInfo = function (index) {
$scope.hiddenDiv[index] = !$scope.hiddenDiv[index];
}
This toggles the ng-show state from the hiddenDiv ng-show. This works fine.
What I wanted to do is put all hiddenDiv states on false except the one that is clicked so only one ng-show would be true.
That's a pure algorithm problem, not related to Angular.
Instead of having a boolean per item, it would be much simpler to just remember the element (index) that should be displayed:
<ul ng-cloak="showResult">
<li class="search_results" ng-repeat="movie in movies | orderBy: '-release_date' track by $index">
<div class="movie_info">
<div class="movie_options">
<div class="slide">
<span class="movie_option">
<span><i class="fa fa-question-circle" aria-hidden="true" ng-click="model.displayedIndex = $index"></i></span>
</span>
</div>
</div>
</div>
<div class="additional_info" ng-show="$index === model.displayedIndex">
{{movie.overview}}
</div>
</li>
</ul>
And in your controller $scope.model = {}
Fiddle: http://jsfiddle.net/6dkLqgfL/
I think this would do:
$scope.toggleInfo = function(index) {
for(var i in $scope.hiddenDiv) {
if(index != i)
$scope.hiddenDiv[i] = false;
}
$scope.hiddenDiv[index] = !$scope.hiddenDiv[index];
}
You could just manually turn all the hiddenDiv elements to false when the toggleInfo function is fired.
$scope.toggleInfo = function(index){
for(int i = 0; i<($scope.hiddenDiv).length; i++)
$scope.hiddenDiv[i] = false;
$scope.hiddenDiv[index] = !$scope.hiddenDiv[index];
}
See if this works?

Ng-click/ ng-repeat issues

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;
}

AngularJs styling a single clicked element within an ng-repeat

I have a collapsible accordion using ng-repeat to render its options. On the right there is a downward facing glyphicon. This glyphicon should become upward facing once the job is clicked and expanded.
Update: Using Amine's response, the clicked header gets a glyphicon-down as it should be. But when collapsed and clicked outside, the glyphicon stays downward as the index is set and actually needs to become upward facing again when collapsed. How can I set $index to undefined again when the item is clicked again?
My code:
<div class="col span_2_of_3">
<div ng-repeat="item in jobs">
<accordion>
<accordion-group ng-click="state.selected = $index">
<accordion-heading>
<h2>
"{{item.title}}"
<i class="pull-right glyphicon "
ng-class="{'glyphicon-chevron-up': $index == state.selected,
'glyphicon-chevron-down': $index != state.selected }"
ng-click="state.selected = $index"
></i>
</h2>
</accordion-heading>
{{item.description}}
</accordion-group>
</accordion>
</div>
</div>
In my controller I have:
$scope.state = { selected: undefined };
You're not removing the glyphicon-chevron-down class when you click on the element. You end up with an element that has both classes ; whatever CSS rule comes last will be applied.
Try this :
<i class="pull-right glyphicon "
ng-class="{'glyphicon-chevron-up': $index == state.selected,
'glyphicon-chevron-down': $index != state.selected }"></i>
Here is a fiddle showing a solution (note that I have removed the ng-click binding on the icon as that's already handled by the accordion-group).

No ion-slide-box update after active-slide changed with $index

This is the controller for the particular view with which I'm having an issue.
.controller('MenuCtrl', ['$rootScope','$scope','$state','$timeout','$ionicSlideBoxDelegate',
function($rootScope, $scope, $state, $timeout, $ionicSlideBoxDelegate) {
$scope.state = { detail: false, selected: 0 };
$scope.stateSwitch = function() {
$scope.state.detail = !$scope.state.detail;
$timeout( function() { $ionicSlideBoxDelegate.update(); }, 50);
}
$scope.activateSlide = function($index) {
$ionicSlideBoxDelegate.slide($index);
}
Here is the view.
<ion-view left-buttons="leftButtons">
<ion-content class="has-header" has-tabs="true" ng-show="!state.detail">
<div class="list menu-list">
<a class="item" ng-repeat="item in items" ng-click="stateSwitch();activateSlide($index)">
...
</a>
</div>
</ion-content>
<ion-content class="has-header padding" has-tabs="true" ng-show="state.detail">
<div class="menu-detail">
<ion-slide-box does-continue="true" show-pager="false">
<ion-slide ng-repeat="item in items">
...
</ion-slide>
</ion-slide-box>
</div>
</ion-content>
</ion-view>
Upon clicking the list item, it triggers the state.detail flag which switches the view to a menu item detail page. From the menu item detail page, I'm using ion-slide-box so that users can paginate through the items as well.
This works great when I don't specify which slide index to start on. Even when I do pick a static number to start the slider on, it works. However, when I use the $index of the menu list as an argument to change the active slide on the slide box, the slide box just smashes all the slides together and their is no swipe functionality.
This happens even after I update the slide box.
Is there something wrong with how I am using $index? Or perhaps this was not the intended way to dynamically set the active slide? Any advice would be appreciated!
on your controller in your function $scope.activateSlide use without $ on index
here code that worked for me:
on html:
<h4 ng-repeat="type in view.menu.itens" ng-click="changeSlide($index)"></h4>
on my controller
$scope.changeSlide = function funChangeSlide(index) {
$ionicSlideBoxDelegate.slide(index);
}

Categories

Resources