Toggle active class on item with angular.js - javascript

How can I toggle active class on element if I have the following code?
<div class="searchandfilter">
<span ng-repeat="taxonomy in taxonomies" class="tab-controller" ng-class="{'active': $index == 0}">
<ul>
<li class="tab" ng-click="onClickTab(taxonomy)">{{taxonomy.name}}</li>
</ul>
<span class="tab-content">
<span ng-repeat="child in taxonomy.children">
<input type="checkbox" checked="child.value" />{{child.name}}
</span>
</span>
</span>
On load I need to set the first tab-conroller span to active, which is now correct but I can't toggle the active class onclick. The number of tabs is dynamic, so it can be 1 or 10. Currently it generates the tabs like this:
<span class="tab-controller ng-scope active" ng-class="{'active': $index == 0}" ng-repeat="taxonomy in taxonomies">
<ul>
<li class="tab ng-binding" ng-click="onClickTab(taxonomy)">Tab 1</li>
</ul>
</span>
<span class="tab-controller ng-scope" ng-class="{'active': $index == 0}" ng-repeat="taxonomy in taxonomies">
<ul>
<li class="tab ng-binding" ng-click="onClickTab(taxonomy)">Tab2</li>
</ul>
</span>

You can do this by passing the index and setting a scope variable.
var app = angular.module('app', []);
app.controller('myController', function($scope) {
$scope.taxonomies = [
{name: 'a', children: [{name: 'a', value: false}]},
{name: 'b', children: [{name: 'a', value: true},
{name: 'b', value: false}]}
];
$scope.onClickTab = function(idx) {
$scope.selectedIndex = idx;
};
$scope.selectedIndex = 0;
});
.active {
color: green;
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<div ng-app='app' ng-controller='myController'>
<div class="searchandfilter">
<span ng-repeat="taxonomy in taxonomies" class="tab-controller" ng-class="{'active': $index == selectedIndex}">
<ul>
<li class="tab" ng-click="onClickTab($index)">{{taxonomy.name}}</li>
</ul>
<span class="tab-content">
<span ng-repeat="child in taxonomy.children">
<input type="checkbox" ng-model="child.value">{{child.name}}
</span>
</span>
</span>
</div>
</div>
The problem with that approach is if the taxonomies change, the selectedIndex may no longer match up with selected tab so instead of tracking index, you can just track the entire object:
var app = angular.module('app', []);
app.controller('myController', function($scope) {
$scope.taxonomies = [
{name: 'a', children: [{name: 'a', value: false}]},
{name: 'b', children: [{name: 'a', value: true},
{name: 'b', value: false}]}
];
$scope.onClickTab = function(selected) {
$scope.selected = selected;
};
$scope.selected = $scope.taxonomies[0];
});
.active {
color: green;
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<div ng-app='app' ng-controller='myController'>
<div class="searchandfilter">
<span ng-repeat="taxonomy in taxonomies" class="tab-controller" ng-class="{'active': taxonomy === selected}">
<ul>
<li class="tab" ng-click="onClickTab(taxonomy)">{{taxonomy.name}}</li>
</ul>
<span class="tab-content">
<span ng-repeat="child in taxonomy.children">
<input type="checkbox" ng-model="child.value">{{child.name}}
</span>
</span>
</span>
</div>
</div>

You need to have some property in your tabs to be able to compare against the $index in the ng-repeat.
For example:
<div class="tab-controller" ng-class="{'active': $index == currentTab}" ng-repeat="taxonomy in taxonomies">
<ul>
<li class="tab" ng-click="onClickTab(taxonomy.tab)">{{ taxonomy.name }}</li>
</ul>
</div>
And taxonomies should be an array of objects, something like:
var taxonomies = [
{ id: 0, name: 'Tab 1', tab: 'whatever you need here'},
{ id: 1, name: 'Tab 2', tab: 'whatever you need here'},
{ id: 2, name: 'Tab 3', tab: 'whatever you need here'}
];
And when you call your function onClickTab it should set the variable scope.currentTab to the id of the clicked tab.
Hope it helps.

sorry if am wrong,try this
http://plnkr.co/edit/FdueM4rAvd4k6hClK1HR?p=preview
your html will be
<html>
<head>
<script data-require="angular.js#*" data-semver="1.4.0-beta.6" src="https://code.angularjs.org/1.4.0-beta.6/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app">
<div class="searchandfilter" ng-controller="tab-controller">
<span ng-repeat="taxonomy in taxonomies">
<ul ng-class="{'active': $index == selectedIndex}">
<li class="tab" ng-click="onClickTab(taxonomy.id)">{{taxonomy.name}}
<br/>
<span class="tab-content">
<span ng-repeat="child in taxonomy.children">
<input type="checkbox" ng-model="child.value" />{{child.name}}
</span>
</span>
</li>
</ul>
</span>
</div>
</body>
</html>
angular js code will be
var app = angular.module("app",[]);
app.controller('tab-controller',['$scope',function($scope){
$scope.taxonomies = [
{ id: 0, name: 'hello' , children:[{name:"test1",value:true},{name:"test2",value:false}]},
{ id: 1, name: 'how' , children:[{name:"test5", value: false}]},
{ id: 2, name: 'are you',children:[{name:"test4",value: true}]}
];
$scope.onClickTab= function(x){
$scope.selectedIndex= x;
};
}]);
css code will be
.active{
background-color:green;
}
body {
background-color: yellow;
}

Related

Unable to select subitem from dropdown in Vuejs?

Here is an working example https://jsfiddle.net/79epsrmw/
But same in vs code not targeting to item1 sub menu, After passing unique id also.
var app = new Vue({
el: '#app',
data: {
menuItems: [
{
name: 'Item 1',
children: [{name: 'Subitem 1'},{name: 'Subitem 2'},{name: 'Subitem 3'}]
},
{
name: 'Item 2'
}
],
selectedDropdown: 'None'
},
methods: {
setSelectedItem(item) {
this.selectedDropdown = item;
}
},
ready: function() {
$('.dropdown-submenu a.test').on("click", function(e){
$(this).next('ul').toggle();
e.stopPropagation();
e.preventDefault();
});
}
})
.dropdown-submenu {
position: relative;
}
.dropdown-submenu .dropdown-menu {
top: 0;
left: 100%;
margin-top: -1px;
}
<div class="container" id="app">
<h2>Vue.js multi-level dropdown example</h2>
<p>
Selected element: {{ selectedDropdown }}
</p>
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">Dropdown
<span class="caret"></span></button>
<ul class="dropdown-menu">
<li v-for="item in menuItems" v-bind:class="{'dropdown-submenu': item.children}">
<a class="test" tabindex="-1" href="#">{{item.name}}<span class="caret" v-if="item.children"></span></a>
<ul class="dropdown-menu" v-if="item.children">
<li v-for="child in item.children"><a tabindex="-1" href="#" #click="setSelectedItem(child.name)">{{child.name}}</a></li>
</ul>
</li>
</ul>
</div>
</div>
I am getting an error ""Elements in iteration expect to have 'v-bind:key' directives.eslint-plugin-vue
So in order to clear the error i have taken key and passed unique id to it
{{child.name}}
So in order to clear the error i have taken key and passed unique id to it. like :key="child"
Issue is in editor it is working fine, But in vs code after passing unique id, I am unable to select the sub menu in item1.
Check this fiddle
https://jsfiddle.net/negqvmjd/35/
var app = new Vue({
el: '#app',
data: function () {
return {
menuItems: [
{
name: 'Item 1',
children: [{name: 'Subitem 1'},{name: 'Subitem 2'},{name: 'Subitem 3'}]
},
{
name: 'Item 2',
children: []
}
],
selectedDropdown: 'None'
}
},
methods: {
setSelectedItem(item,childLength, bool) {
if(childLength === 0 && bool === false){
this.selectedDropdown = item;
}
else if(childLength !==0 && bool === true){
this.selectedDropdown = item;
}
}
},
ready: function() {
$('.dropdown-submenu a.test').on("click", function(e){
$(this).next('ul').toggle();
e.stopPropagation();
e.preventDefault();
});
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="container" id="app">
<h2>Vue.js multi-level dropdown example</h2>
<p>
Selected element: {{ selectedDropdown }}
</p>
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">Dropdown
<span class="caret"></span></button>
<ul class="dropdown-menu">
<li v-for="item in menuItems" v-bind:class="{'dropdown-submenu': item.children.length}">
<a class="test" tabindex="-1" href="#" #click=setSelectedItem(item.name,item.children.length,false)>{{item.name}}<span class="caret" v-if="item.children.length"></span></a>
<ul class="dropdown-menu" v-if="item.children.length">
<li v-for="child in item.children"><a tabindex="-1" href="#" #click="setSelectedItem(child.name,item.children.length,true)">{{child.name}}</a></li>
</ul>
</li>
</ul>
</div>
</div>

Filter an ng-repeat based on parent ng-repeat

Scratching my head now for 2 hours. Maybe I'm tired or maybe I don't understand what I'm doing. Anyway, I've got an array of blogposts. Which looks like this:
[
{
'title': 'first post',
'tags': [
{ 'name': 'Tag 1', 'slug': 'tag-1' }
]
},
{
'title': 'second post',
'tags': [
{ 'name': 'Tag 1', 'slug': 'tag-1' },
{ 'name': 'Tag 3', 'slug': 'tag-3' }
]
},
{
'title': 'third post',
'tags': [
{ 'name': 'Tag 2', 'slug': 'tag-2' }
]
}
]
And also an array containing my tags like this.
[
{'title': 'Tag 1', 'slug':'tag-1'},
{'title': 'Tag 2', 'slug':'tag-2'},
{'title': 'Tag 3', 'slug':'tag-3'},
{'title': 'Tag 4', 'slug':'tag-4'}
]
And I am doing an regular angular ng-repeat like this to show all my blogpost tags:
<ul>
<li ng-repeat="tag in blog.tags">
<h3>{{ tag.title }}</h3>
</li>
</ul>
Now, I would like to add a nested repeater which only shows blogposts from the variable blog.posts that contains the current tag.
Something like this:
<ul ng-controller="BlogComponent as blog">
<li ng-repeat="tag in blog.tags">
<h3>{{ tag.title }}</h3>
<ul>
<li ng-repeat="post in blog.posts | filter: tag.slug IN post.tags">
<span>{{ post.title }}</span>
</li>
</ul>
</li>
</ul>
But I cannot seem to get it working. I think it SHOULD be easy. Because in my mind it is a quite simple task. to filter out unwanted results based on a string and an array.
Wanted/Exptected output:
<ul>
<li>
<h3>Tag 1</h3>
<ul>
<li>first post</li>
<li>second post</li>
</ul>
</li>
<li>
<h3>Tag 2</h3>
<ul>
<li>third post</li>
</ul>
</li>
<li>
<h3>Tag 3</h3>
<ul>
<li>second post</li>
</ul>
</li>
</ul>
You could make a custom filter instead of using "filter: expression".
What you can do create a filter that takes the tags and posts as arguments and returns the array with filtered items.
myApp.filter('myFilter', function () {
return function (posts, tag) {
var newPosts = [];
for (var i = 0; i < posts.length; i++)
for (var j = 0; j < post.tags.length; j++)
if (posts[i].tags[j].slug === tag.slug)
newPosts.push(posts[i]);
return newPosts;
}
});
And then
<li ng-repeat="post in blog.posts | myFilter: tag">
<span>{{ post.title }}</span>
</li>
Using the built-in functionality, you can do it like this:
<ul ng-controller="BlogComponent as blog">
<li ng-repeat="tag in blog.tags">
<h3>{{ tag.title }}</h3>
<ul>
<li ng-repeat="post in blog.posts | filter: {tags: {slug: tag.slug}}">
<span>{{ post.title }}</span>
</li>
</ul>
</li>
</ul>
See it working here: https://plnkr.co/edit/pQZse1hUnnzyfneIlpMu?p=preview
Documentation for the filter is here: https://docs.angularjs.org/api/ng/filter/filter
Or, if you want Tag 4 to be hidden because it has no matching posts, you could do something like this:
<div ng-controller="BlogComponent as blog">
<div ng-repeat="tag in blog.tags">
<div ng-repeat="post in blog.posts | filter: {tags: {slug: tag.slug}}">
<h3 ng-if="$first">{{ tag.title }}</h3>
<li>{{ post.title }}</li>
</div>
</div>
</div>

Change list item active bg-color with ng-click

I am a little lost with this issue.
When I click on the - a href tag - i want the li active background color to change to a specific color that I have stored in each object - like so obj.color" = #5c6bc0.
<li ng-repeat="obj in tags" ng-class="{active: obj.id == tagStates.activeItemID}">
<a href ng-click="tagStates.activeItemID = obj.id;">
{{obj.name}}
</a>
</li>
How can I do this? I have tried with ng-style - ng-style="{'active':'background-color': obj.color} but that didn't work.
Thanks
You can do something like
<li ng-repeat="obj in tags" ng-class="{active: obj.id == tagStates.activeItemID}" ng-style={backgroundColor: obj.id == tagStates.activeItemID ? obj.color : ''}">
<a href ng-click="tagStates.activeItemID = obj.id;">
{{obj.name}}
</a>
</li>
angular
.module('app', [])
.controller('Ctrl', function() {
this.tags = [
{ name: 'bob' },
{ name: 'rick' },
{ name: 'dave' }
];
})
<body ng-controller="Ctrl as vm">
<ul>
<li ng-repeat="obj in vm.tags" ng-class="{'active': vm.active == obj.name}">
<a ng-click="vm.active = obj.name">{{obj.name}}</a>
</li>
</ul>
{{vm.active}}
</body>
https://plnkr.co/edit/wvKIUtJIb0AE1vpWDtv9?p=preview

Looping through 2 different objects in AngularJS

I have the following controller:
app.controller('MainController', ['$scope', function($scope) {
$scope.taskCategories = {
categories: [
'work',
'chores',
'learning'
]
};
$scope.tasklist = {
tasks: [{
title: 'Email Gregory',
category: 'work'
}, {
title: 'Clean the Kitchen',
category: 'chores'
}, {
title: 'AngularJS',
category: 'learning'
}, {
title: 'Hose Car',
category: 'chores'
}, {
title: 'Email Jethro',
category: 'work'
}
]
};
}]);
And am pulling the information through so far like this:
<div>
<li data-toggle="collapse" data-target="#work" class="nav_head workcat collapsed">
Work <span class="arrow"></span>
</li>
<ul class="sub-menu collapse" id="work">
<li ng-repeat="tasks in tasklist.tasks | orderBy:'title' | filter: {category: 'work'}">
{{ tasks.title }}
</li>
<li class="addwork">
<a href="">
<span class="fa-stack"> <i class="fa fa-2x fa-stack-2x fa-circle"></i><i class="fa fa-2x fa-stack-2x fa-plus-circle"></i>
</a>
</span>
</li>
</ul>
This would work fine doing a few times, one for each category, but I am looking to be able to add categories dynamically, and so I am looking for some way to go through the following steps:
So, I’ll need to loop all in categories.
During that loop, I’ll loop through the tasks and print out any task that matches the string of categories.index(1)
Then add 1 to category index and run again, till category.length runs out
I'm unfamiliar with looping inside a loop, and more unfamiliar again with doing it in angular. Anyone have any suggestions?
You could do an outer loop (ng-repeat) on the categories:
<ul class="sub-menu collapse" id="work" ng-repeat="cat in taskCategories.categories">
<li ng-repeat="tasks in tasklist.tasks | orderBy:'title' | filter: {category: cat}">
{{ tasks.title }}
</li>
<li class="addwork">
<a href="">
<span class="fa-stack"> <i class="fa fa-2x fa-stack-2x fa-circle"></i><i class="fa fa-2x fa-stack-2x fa-plus-circle"></i></span>
</a>
</li>
</ul>
Fiddle
Please refer below code snippet
angular.module('app',[]);
angular.module('app').controller('myController',function($scope){
$scope.taskCategories = {
categories: [
'work',
'chores',
'learning'
]
};
$scope.tasklist = {
tasks: [{
title: 'Email Gregory',
category: 'work'
}, {
title: 'Clean the Kitchen',
category: 'chores'
}, {
title: 'AngularJS',
category: 'learning'
}, {
title: 'Hose Car',
category: 'chores'
}, {
title: 'Email Jethro',
category: 'work'
}
]
};
});
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/js/bootstrap.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<body ng-app="app" ng-controller="myController">
<ul>
<li data-toggle="collapse" data-target=#{{c}} class="nav_head workcat" ng-repeat="c in taskCategories.categories">
<span class="arrow"> {{c}}</span>
<ul class="sub-menu collapse" id={{c}}>
<li ng-repeat="tasks in tasklist.tasks | orderBy:'title' | filter: {category: c}">
{{ tasks.title }}
</li>
</ul>
</li>
</ul>
</body>
Hope this helps!

angular.js ng-repeat division

I have array of categories that has this structure:
{
name: 'something',
main_category: 'A'
}
So each category has it's main category. I would like to display all categories in html like so:
<h1>A</h1>
<ul>
<li>list of categories that has main category A</li>
</ul>
<h1>B</h1>
<ul>
<li>list of categories that has main category B</li>
</ul>
how should I achieve it? The only way I found was doing something like this:
<h1>A</h1>
<ul>
<li ng-repeat="category in categories" ng-if="category.main_category == 'A'">..</li>
</ul>
<h1>B</h1>
<ul>
<li ng-repeat="category in categories" ng-if="category.main_category == 'B'">..</li>
</ul>
It works but I don't think it's a good idea.
You should use groupBy filter provided by https://github.com/a8m/angular-filter, and do something like this
var myApp = angular.module('mcApp', ['angular.filter']);
myApp.controller('mcController', ['$scope', function ($scope) {
console.log('asadsds');
$scope.myArray = [
{
name: 'something 1',
main_category: 'B'
},
{
name: 'something 2',
main_category: 'A'
},
{
name: 'something 3',
main_category: 'A'
},
{
name: 'something 4',
main_category: 'B'
},
{
name: 'something 5',
main_category: 'B'
}
];
}]);
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-example7-production</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.0-beta.5/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.5/angular-filter.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="mcApp" ng-controller="mcController">
<p>{{name}}</p>
<div ng-repeat="object in myArray | orderBy: 'main_category'| groupBy:'main_category' |toArray: true" >
<h1>{{object.$key}}</h1>
<ul>
<li ng-repeat="value in object">
{{value.name}}
</li>
</ul>
</div>
</body>
</html>
See this answer for how to use Angular's filter: { prop: 'value' } syntax to achieve the same functionality as an ng-if in this use-case:
<h1>A</h1>
<ul>
<li ng-repeat="category in categories | filter: {main_category: 'A'}"></li>
</ul>
<h1>B</h1>
<ul>
<li ng-repeat="category in categories | filter: {main_category: 'B'}"></li>
</ul>
You can do it by creating a second main categories array. Using ng-repeat-start and ng-repeat-end you create the basic structure (h1 + ul). In the ul a second repeater of categories filtered by the main category renders the list items (fiddle):
<div ng-app ng-controller="MyCtrl">
<h1 ng-repeat-start="mainCategory in mainCategories | orderBy">{{mainCategory}}</h1>
<!-- main categories ngRepeat starts here -->
<ul ng-repeat-end>
<!-- main categories ngRepeat ends here -->
<li ng-repeat="category in categories | orderBy:'name' | filter:{ 'main_category': mainCategory}">{{category.name}}</li>
<!-- categories are filtered by mainCategory -->
</ul>
</div>
function MyCtrl($scope) {
var categories = [{
name: 'somethingA2',
main_category: 'A'
}, {
name: 'somethingB1',
main_category: 'B'
}, {
name: 'somethingB2',
main_category: 'B'
}, {
name: 'somethingA3',
main_category: 'A'
}, {
name: 'somethingB3',
main_category: 'B'
}, {
name: 'somethingA1',
main_category: 'A'
}];
$scope.categories = categories;
/** main categories - reduce the categories array to a dictionary object, and get the keys of the dictionary to get the unique main category names **/
$scope.mainCategories = Object.keys(categories.reduce(function (main, category) {
main[category['main_category']] = '';
return main;
}, {}));
}

Categories