AngularJS only showing one list item at a time - javascript

HTML
<ul class="column__list">
<li class="column__list--item" ng-repeat="task in tasks | orderBy:'name':false" data-ng-class="{active:taskIndex=={{$index}}}">
<div class="column__list--expand fa" ng-click="expand = !expand" ng-class="{'fa-chevron-down': !expand, 'fa-chevron-up': expand}"></div>
<div class="column__list--actions slide" ng-show="expand">
{{ children }}
</div>
</li>
</ul>
ANGULAR
angular.module('App.animate', [])
.animation('.slide', function() {
var NgHideClassName = 'ng-hide';
return {
beforeAddClass: function(element, className, done) {
if(className === NgHideClassName) {
jQuery(element).slideUp(done);
console.log('0');
}
},
removeClass: function(element, className, done) {
if(className === NgHideClassName) {
jQuery(element).hide().slideDown(done);
console.log('1');
}
}
}
});
I have the above which when clicked a div will slideDown and when clicked again will slideUp .column__list--expand
This works but what if I only wanted to have one li on show at a time and also update the ng-class too?
jsFiddle example

Change your template to:
<div ng-app="testApp" ng-controller="testController">
<ul class="column__list">
<li class="column__list--item" ng-repeat="task in tasks | orderBy:'name':false" data-ng-class="{active:taskIndex=={{$index}}}">
<div class="column__list--expand fa" ng-click="$parent.selectedTask = $parent.selectedTask == task ? null : task" ng-class="{'fa-chevron-down': $parent.selectedTask!=task, 'fa-chevron-up': $parent.selectedTask==task}">{{ task.title }}</div>
<div class="column__list--actions slide" ng-show="$parent.selectedTask==task">
{{ task.description }}
</div>
</li>
</ul>
</div>
jsfiddle solution

Related

toggle active class on list menu items - vue js

I want that, clicking on a menu item, the active class is triggered only for that specific item and removed for the others, until now I wrote this:
<template>
<nav class="navbar">
<div class="navbar__brand">
<router-link to="/">Stock Trader</router-link>
</div>
<div class="navbar__menu">
<ul class="navbar__menu--list">
<li #click="isActive=!isActive" class="navbar__menu--item" :class="{active:isActive}">
<router-link to="/portfolio">Portfolio</router-link>
</li>
<li #click="isActive=!isActive" class="navbar__menu--item" :class="{active:isActive}">
<router-link to="/stocks">Stocks</router-link>
</li>
</ul>
</div>
<div class="navbar__menu--second">
<ul class="navbar__menu--list">
<li #click="isActive=!isActive" class="navbar__menu--item" :class="{active:isActive}">
End Day
</li>
<li #click="isActive=!isActive" class="navbar__menu--item" :class="{active:isActive}">
Save / Load
</li>
</ul>
</div>
</nav>
</template>
<script>
export default {
data(){
return{
isActive: false
}
}
}
</script>
now of course, when I click on one item the active class is inserted/removed for all the items, what is the best solution for making that only a specific item, on clicking on it, receives the active class?
You'll want some sort of identifier for each clickable item and set that to your data property. For example
data() {
return { active: null }
}
and in your list items (for example)
<li #click="active = 'portfolio'"
class="navbar__menu--item"
:class="{active:active === 'portfolio'}">
In this example, the identifier is "portfolio" but this could be anything, as long as you use a unique value per item.
You could keep an object of links you have and handle a click on each of items. E.g.
data() {
return {
links: [
{
title : 'Portfolio',
to : '/portfolio',
isActive : false,
location : 'first',
},
{
title : 'Stocks',
to : '/stocks',
isActive : false,
location : 'first',
},
{
title : 'End Day',
to : '#',
isActive : false,
location : 'second',
},
{
title : 'Save / Load',
to : '#',
isActive : false,
location : 'second',
},
]
};
},
methods: {
handleNavClick(item) {
this.links.forEach(el, () => {
el.isActive = false;
});
item.isActive = true;
}
},
I do this in vue3.
the template is:
<li
v-for="(title, index) in titles"
class="title"
:key="index"
:class="{ active: active === index }"
#click="updateActive(index)"
>
{{ title }}
</li>
and the script is
<script lang="ts" setup>
import { ref } from "vue"
const titles = ["title1","title2","title3"]
const active = ref(-1)
function updateActive(val: number) {
active.value = val
}
</script>
If you have several ul => use title instead of index. For example :
<ul>
<div>
Applicants
</div>
<li
v-for="(title, index) in applicantMenuTitles"
:key="index"
:class="{ active: active === title }"
#click="updateActive(title)"
>
{{ title }}
<div
v-if=" active === title "
class="cursor"
/>
</li>
</ul>
<ul>
<div>
Offices
</div>
<li
v-for="(title, index) in officeMenuTitles"
:key="index"
:class="{ active: active === title }"
#click="updateActive(title)"
>
{{ title }}
<div
v-if=" active === title "
class="cursor"
/>
</li>
</ul>
And on script :
...
const active = ref('')
function updateActive(title: string) {
active.value = title
}

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?

Inbuilt directives do not work within custom directive

var app = angular.module("myDiscuss", []);
app.controller("TabController", function() {
this.tab = 0;
this.subTab = 0;
this.like = 0;
this.selectLike = function(setTab) {
this.like= setTab;
};
this.selectTab = function(setTab) {
this.tab= setTab;
};
this.selectSubTab = function(setTab){
this.subTab = setTab;
};
this.isSelected = (function(checkTab){
return this.tab === checkTab;
});
this.isSelectedSub = (function(checkTab){
return this.subTab === checkTab;
});
this.isSelectedLike = (function(checkTab) {
return this.like === checkTab;
});
});
app.controller('FormController', function($scope) {
$scope.person = {
name: null
};
$scope.people = [];
$scope.submit = function() {
if ($scope.person.name) {
$scope.people.push({name: $scope.person.name});
$scope.person.name = '';
}
};
});
app.directive('replyBox', function(){
return {
restrict:'A',
templateUrl:'../templates/reply-box.html'
};
});
app.directive ('commentSection', function(){
return {
restrict:'A',
scope :{},
templateUrl:'../templates/comment-section.html'
};
});
<body ng-app="myDiscuss">
<div class="container">
<div class="row">
<div>
<div class="thumbnail" ng-controller="TabController as tabs">
<div ng-show="tabs.isSelectedLike(1)">
</div>
<section class="caption">
<ul class="nav nav-pills">
<li ng-class="{ active:like === 1 }" >
<a href ng-click="tabs.selectLike(1)">Helpful</a>
</li>
<li ng-class= "{ active:tab === 2 }">
<a href ng-click="tabs.selectTab(2)">Comment</a>
</li>
</ul>
<div comment-section ng-show="tabs.isSelected(2)"></div>
</section>
</div>
</div>
</div>
</div>
</body>
<!--comment-section.html-->
<div class="panel" >
<form ng-submit="submit()" ng-controller="FormController">
<blockquote ng-repeat="(index,object) in people" >
<ul class="nav nav-pills">
<li ng-class="{ active:subTab === 3 }" >
<a href ng-click="tabs.selectSubTab(3)">Helpful</a>
</li>
<li ng-class= "{ active:subTab === 4}">
<a href ng-click="tabs.selectSubTab(4)">Reply</a>
</li>
</ul>
<div reply-box ng-show="tabs.isSelectedSub(4)"></div>
</blockquote>
<input type="text" ng-model="person.name" name="person.name" />
</form>
</div>
<!-- reply-box.html -->
<div>
<input type="text">
</div>
When I add the reply-box directive to the comment-section directive it does not perform the 'submit' action. When the "reply" link in the commentSection directive is clicked, the ng-show directive does not working for the reply-box directive.
Well I don't see any input type='submit' in your code, maybe thats why ng-submit is not working,
Moreover i think your ng-show directive isn't working because the ng-controller="TabController as tabs" ends here
<div class="thumbnail" ng-controller="TabController as tabs">
<div ng-show="tabs.isSelectedLike(1)">
</div>
<section class="caption" >
<ul class="nav nav-pills">
<li ng-class="{ active:like === 1 }" >
<a href ng-click="tabs.selectLike(1)">Helpful</a>
</li>
<li ng-class= "{ active:tab === 2 }">
<a href ng-click="tabs.selectTab(2)">Comment</a>
</li>
</ul>
<div comment-section ng-show="tabs.isSelected(2)"></div>
</section>
</div>
Thus you are calling your ng-show="tabs.isSelectedSub(4)" wont return any thing because this method is not defined in your FormController.
Hope it helps.
The errors occur because the scope for the comment section directive does not inherit from the parent scope.
Define a scope that inherits from the parent scope
To inherit from the parent scope, you'll need to set the scope property of the comment-section directive to true.
From the AngularJS documentation:
scope: true - the directive creates a new child scope that prototypically inherits from the parent scope. If more than one directive (on the same DOM element) requests a new scope, only one new child scope is created. Since we have "normal" prototypal inheritance, this is like ng-include and ng-switch, so be wary of 2-way data binding to parent scope primitives, and child scope hiding/shadowing of parent scope properties.
The comment-section directive can be rewritten thus:
app.directive ('commentSection', function(){
return {
restrict:'A',
scope :true,
templateUrl:'../templates/comment-section.html'
};
});

How to trigger ng-hide on all elements

I have a list that when you click on an li, it reveals a hidden list of information about that person.
In the footer, theres simple navigation showing the different views and when you click, angular filters through the original list for the matched elements.
All I am stuck on is this;
if you click an li element and reveal the info for that person, then click one of the navigation buttons, it will still show that person but with the hidden element revealed...not closed.
Ideally, id prefer that when the user clicks any of the footer navigation buttons, the list reveals just the names, not the hidden info..regardless of whether it was clicked or not.
If this was just in Jquery or javascript, i would know how to approach this but, Im sure theres an 'angular specific' approach I just don't know about.
Heres the HTML:
<div ng-controller="MyCtrl">
<ul id="housewrapper" ng-cloak>
<li ng-repeat="item in house track by item.member" class="listings" ng-click="showComments = !showComments;" ng-show="([item] | filter:filters).length > 0" >
<span ng-if="item.whereHesheStands == 'oppose'"><img class="dot againstdot" src="img/against.png">{{item.member}}
</span>
<span ng-if="item.whereHesheStands == 'leanoppose'">
<img class="dot againstdot" src="img/against.png">{{item.member}}
</span>
<span ng-if="item.whereHesheStands == 'support'" ng-click="clickMeImg($event);">
<img class="dot supportdot" src="img/support.png">{{item.member}}
</span>
<span ng-if="item.whereHesheStands == 'leansupport' ">
<img class="dot supportdot" src="img/support.png">{{item.member}}
</span>
<span ng-if="item.whereHesheStands == 'unknown' ">
<img class="dot undecideddot" src="img/undecided.png">{{item.member}}
</span>
<span ng-if="item.whereHesheStands == 'undecided' ">
<img class="dot undecideddot" src="img/undecided.png">{{item.member}}
</span>
<div class="memberdetail" ng-show="showComments" ng-click="$event.stopPropagation();" >
<ul class="memberbreakdown">
<li class="partyline" >
{{item.party}} - {{item.state}}</li>
<li class="comments">
<span style="color:#a4a4a4;" ng-if="!(item.comments)">Comment not available</span>
<span>{{item.comments}}</span>
</li>
</ul>
</div>
</li>
</ul>
<div id="appfooterWrapper">
<ul id="appfooter">
<li ng-click="myFunctionRepublican();" ng-class="class">R</li>
<li ng-click="myFunctionDemocrat();" ng-class="class2">D</li>
<li ng-click="myFunctionSupport();" ng-class="class3">S</li>
<li ng-click="myFunctionOppose();" ng-class="class4">A</li>
<li ng-click="myFunctionUnknown();" ng-class="class5">U</li>
</ul>
</div>
and the javascript of the "R" navigation button
$scope.myFunctionRepublican = function() {
$('.memberdetail').removeClass('ng-show');
$('.memberdetail').bind('click');
$scope.filters = function(house) {
return house.party == 'R' ;
};
if ($scope.class === ""){
$scope.class = "rep";
$scope.class2 = "";
$scope.class3 = "";
$scope.class4 = "";
$scope.class5 = "";
}
else{
$scope.class = "";
}
$('html, body').animate({scrollTop:0}, 'fast');
var loading;
loading = true;
if (loading == true) {
setTimeout(function() {
spinner.stop();
$('.listings').not('.ng-hide').addClass('republican');
console.log($('.republican').length);
$('#housewrapper').stop().fadeIn(350).addClass(
'marginAdd');
$('#subhead').removeClass('slidedown');
$('#subhead').html('Republicans').css('color', '#d41600');
setTimeout(function() {
$('#subhead').addClass('slidedown');
}, 300);
}, 500);
}
}
Here's the Fiddle
A couple changes, attach a property onto each member.
View changes:
ng-click="item.showComments = !item.showComments;"
<div class="memberdetail" ng-show="item.showComments" ng-click="$event.stopPropagation();" >
Controller changes:
function resetShow() {
for(var i = 0, l = $scope.house.length; i < l; i++) {
$scope.house[i].showComments = false;
}
}
Then just call it when you navigate:
$scope.myFunctionUnknown = function() {
resetShow();
....
Forked Fiddle

Keep track of tabs with knockoutjs + twitter bootstrap

I'm trying to keep track of the selected tab in the view model but I can't seem to make it work.
In the following code when you click a tab the header will update correctly but the content of the tab is not displayed. If you remove , click: $parent.selectSection then the contents are shown but the header does not update.
Now if you remove the data-bind="css: { active: selected }" from the li then it seems to work when you click the tabs but the button to select the second tab doesn't.
How can I make this work?
See: http://jsfiddle.net/5PgE2/3/
HTML:
<h3>
<span>Selected: </span>
<span data-bind="text: selectedSection().name" />
</h3>
<div class="tabbable">
<ul class="nav nav-tabs" data-bind="foreach: sections">
<li data-bind="css: { active: selected }">
<a data-bind="attr: { href: '#tab' + name }
, click: $parent.selectSection" data-toggle="tab">
<span data-bind="text: name" />
</a>
</li>
</ul>
<div class="tab-content" data-bind="foreach: sections">
<div class="tab-pane" data-bind="attr: { id: 'tab' + name }">
<span data-bind="text: 'In section: ' + name" />
</div>
</div>
</div>
<button data-bind="click: selectTwo">Select tab Two</button>
JS:
var Section = function (name) {
this.name = name;
this.selected = ko.observable(false);
}
var ViewModel = function () {
var self = this;
self.sections = ko.observableArray([new Section('One'),
new Section('Two'),
new Section('Three')]);
self.selectedSection = ko.observable(new Section(''));
self.selectSection = function (s) {
self.selectedSection().selected(false);
self.selectedSection(s);
self.selectedSection().selected(true);
}
self.selectTwo = function() { self.selectSection(self.sections()[1]); }
}
ko.applyBindings(new ViewModel());
There are several ways that you can handle this either using bootstrap's JS or by just having Knockout add/remove the active class.
To do this just with Knockout, here is one solution where the Section itself has a computed to determine if it is currently selected.
var Section = function (name, selected) {
this.name = name;
this.isSelected = ko.computed(function() {
return this === selected();
}, this);
}
var ViewModel = function () {
var self = this;
self.selectedSection = ko.observable();
self.sections = ko.observableArray([
new Section('One', self.selectedSection),
new Section('Two', self.selectedSection),
new Section('Three', self.selectedSection)
]);
//inialize to the first section
self.selectedSection(self.sections()[0]);
}
ko.applyBindings(new ViewModel());
Markup would look like:
<div class="tabbable">
<ul class="nav nav-tabs" data-bind="foreach: sections">
<li data-bind="css: { active: isSelected }">
<a href="#" data-bind="click: $parent.selectedSection">
<span data-bind="text: name" />
</a>
</li>
</ul>
<div class="tab-content" data-bind="foreach: sections">
<div class="tab-pane" data-bind="css: { active: isSelected }">
<span data-bind="text: 'In section: ' + name" />
</div>
</div>
</div>
Sample here: http://jsfiddle.net/rniemeyer/cGMTV/
There are a number of variations that you could use, but I think that this is a simple approach.
Here is a tweak where the active tab used the section name as a template: http://jsfiddle.net/rniemeyer/wbtvM/

Categories

Resources