Hide expanded sub menu when another one selected - javascript

And the I want to expand sub menu when user clicks on it with animation:
angular.module('testApp', ['ngAnimate'])
.controller('testController', ['$scope',
function($scope) {
$scope.workshops = [
{ name: "Workshop audience", id: 'audience' },
{ name: "Workshop catalog", id: 'catalog' },
{ name: "Add a workshop", id: 'add_wk' },
{ name: "Add/Edit categories", id: 'add_ctg' },
{ name: "Add/Edit difficulty level", id: 'add_lvl' },
{ name: "Add/Edit a target group", id: 'add_grp' }
];
}
])
.animation('.slide', function() {
var NG_HIDE_CLASS = 'ng-hide';
return {
beforeAddClass: function(element, className, done) {
if (className === NG_HIDE_CLASS) {
element.slideUp(done);
}
},
removeClass: function(element, className, done) {
if (className === NG_HIDE_CLASS) {
element.hide().slideDown(done);
}
}
}
});
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular-animate.js"></script>
<div ng-app="testApp" ng-controller="testController">
<ul>
<li class="nav-item" ng-class="{ active: isActive('/analysis/recommendation') }">
<a class="nav-link" ng-click="expand = !expand;">Analysis</a>
<ul class="slide" ng-show="expand">
<li>
<a ng-class="{active:isActive('/analysis/recommendation')}" href="#!analysis/recommendation">Recommendation</a>
</li>
</ul>
</li>
<li ng-class="{ active: isActive('/workshop/') }">
<a ng-click="expand2 = !expand2">Workshop </a>
<ul class="nav flex-column sub-menu slide" ng-show="expand2">
<li data-ng-repeat="workshop in workshops">
</li>
</ul>
</li>
</ul>
</div>
It works fine. All sub menus are displayed when I click on them, but how can I hide sub menus which are already expanded when I want to open a new one?
Here is fiddle

Does this work?
angular.module('testApp', ['ngAnimate'])
.controller('testController', ['$scope',
function($scope) {
$scope.workshops = [
{ name: "Workshop audience", id: 'audience' },
{ name: "Workshop catalog", id: 'catalog' },
{ name: "Add a workshop", id: 'add_wk' },
{ name: "Add/Edit categories", id: 'add_ctg' },
{ name: "Add/Edit difficulty level", id: 'add_lvl' },
{ name: "Add/Edit a target group", id: 'add_grp' }
];
$scope.expandToggle= function(subMenu){
if (subMenu == 'expand'){
$scope.expand = !$scope.expand;
if($scope.expand2){$scope.expand2 = false;}
}
if (subMenu == 'expand2'){
$scope.expand2 = !$scope.expand2;
if($scope.expand){$scope.expand = false;}
}
}
}
])
.animation('.slide', function() {
var NG_HIDE_CLASS = 'ng-hide';
return {
beforeAddClass: function(element, className, done) {
if (className === NG_HIDE_CLASS) {
element.slideUp(done);
}
},
removeClass: function(element, className, done) {
if (className === NG_HIDE_CLASS) {
element.hide().slideDown(done);
}
}
}
});
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular-animate.js"></script>
<div ng-app="testApp" ng-controller="testController">
<ul>
<li class="nav-item" ng-class="{ active: isActive('/analysis/recommendation') }">
<a class="nav-link" ng-click="expandToggle('expand')">Analysis</a>
<ul class="slide" ng-show="expand">
<li>
<a ng-class="{active:isActive('/analysis/recommendation')}" href="#!analysis/recommendation">Recommendation</a>
</li>
</ul>
</li>
<li ng-class="{ active: isActive('/workshop/') }">
<a ng-click="expandToggle('expand2')">Workshop </a>
<ul class="nav flex-column sub-menu slide" ng-show="expand2">
<li data-ng-repeat="workshop in workshops">
</li>
</ul>
</li>
</ul>
</div>

Related

Issue when trying to add popup menu with multi level sub menu in Vuejs?

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;
}
<ul>
<li :class="{ current : item === value }" v-for="item in list" #click="select(item)">{{ item }}</li>
</ul>
<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>
CodePen link https://codepen.io/santoshch/pen/mdWwepg
Tried adding sub menu in the dropdown, But unable to do that, i mean multi level dropdown. Taken one more ul and li tag to display items, But not sure how to proceed.
In the data i have already passed items and value, But for sub multi level i need to add few items.
For the above codepen, Tried to add code for sub multi level items, Then i am getting error
You could extend your array list (from your codepen example) with objects:
list: [
'Orange',
'Apple',
'Kiwi',
'Lemon',
'Pineapple',
{
name: 'Others',
children: [
'Strawberries',
'Raspberries',
'Blueberries'
]
},
'Last Fruit'
]
And when you loop over the list you check for these objects and output them in a nested <ul> element:
<ul>
<template v-for="(item, index) in list">
<li v-if="item.name" :key="index" :class="{ current : item === value }">
{{ item.name }}
<ul>
<li v-for="(childItem, childIndex) in item.children" :class="{ current : childItem === value }" :key="childIndex" #click="select(childItem)">
{{ childItem }}
</li>
</ul>
</li>
<li v-else :key="index" :class="{ current : item === value }" #click="select(item)">{{ item }}</li>
</template>
</ul>
Here is a working example (without styling the submenu)
https://jsbin.com/caroguwori/edit?html,js,output

Pass custom <li> attribute to Javascript function

So I have a basic dropdown and each dropdown option has specific attributes. I'd like to pass those attributes (data name, description, and value) to a javascript function. code is below the console says "undefined " for everything I'm trying to pass. I've already tried changing .data() to .attr(). I really need something clean and simple. thanks!
HTML
<label class="dropdown" style="float: left" >
<div class="dd-button">Select a Plan</div>
<input type="checkbox" class="dd-input" id="test">
<ul id="dropdown" class="dd-menu">
<li data-description="description." data-name="Web" data-value="mmk">2</li>
<li data-description="description." data-name="Basic" data-value="basic">3</li>
<li data-description="description." data-name="Plus" data-value="plus">4</li>
</ul>
</label>
JS
var PLAN_CONFIG = {
id: '',
billing: '',
name: '',
description: '',
payment: true,
panelLabel: 'Confirm',
};
$('#dropdown').click(function () {
PLAN_CONFIG.id = $(this).data('value');
PLAN_CONFIG.name = $(this).data('name');
PLAN_CONFIG.description = $(this).data('description');
console.log(PLAN_CONFIG);
});
If you assign the click event handler to #dropdown, this in the callback function will be #dropdown too. You should assign the event handler to li elements instead:
$('#dropdown li').click(function () {
Demo:
var PLAN_CONFIG = {
id: '',
billing: '',
name: '',
description: '',
payment: true,
panelLabel: 'Confirm',
};
$('#dropdown li').click(function () {
PLAN_CONFIG.id = $(this).data('value');
PLAN_CONFIG.name = $(this).data('name');
PLAN_CONFIG.description = $(this).data('description');
console.log(PLAN_CONFIG);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label class="dropdown" style = "float: left" >
<div class="dd-button"> Select a Plan </div>
<input type="checkbox" class="dd-input" id="test">
<ul id = "dropdown" class="dd-menu">
<li data-description = "description." data-name = "Web" data-value="mmk" >2 </li>
<li data-description = "description." data-name = "Basic" data-value="basic" >3 </li>
<li data-description = "description." data-name = "Plus" data-value="plus" >4</li>
</ul>
</label>
Use this selector '#dropdown li'.
Your current code is binding the click event to your element dropdown rather than your li elements.
var PLAN_CONFIG = {
id: '',
billing: '',
name: '',
description: '',
payment: true,
panelLabel: 'Confirm',
};
$('#dropdown li').click(function() {
PLAN_CONFIG.id = $(this).data('value');
PLAN_CONFIG.name = $(this).data('name');
PLAN_CONFIG.description = $(this).data('description');
console.log(PLAN_CONFIG);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label class="dropdown" style="float: left">
<div class="dd-button"> Select a Plan </div>
<input type="checkbox" class="dd-input" id="test">
<ul id = "dropdown" class="dd-menu">
<li data-description = "description." data-name = "Web" data-value="mmk" >2 </li>
<li data-description = "description." data-name = "Basic" data-value="basic" >3 </li>
<li data-description = "description." data-name = "Plus" data-value="plus" >4</li>
</ul>
</label>

jquery click event is not working with angular.js ng-repeat

I have a nested (tertiary) menu list with a jquery click event on the back. jQuery click does not fire when the menu item is clicked. The jQuery event works well if the values inside the HTML are static.
HTML:
<div>
<ul class="collapsible-list" ng-controller="ViewCtrl">
<li class="collapsible-list-subnav" ng-repeat="view in views">
<a class="collapsible-list-parent">{{view.name}}</a>
<ul class="collapsible-list secondary">
<li class="collapsible-list-subnav">
<a class="collapsible-list-parent">Public Views</a>
<ul class="collapsible-list tertiary">
<li ng-repeat="publicview in view.publicviews">
<a>{{publicview.title}}</a>
</li>
</ul>
</li>
<li class="collapsible-list-subnav">
<a class="collapsible-list-parent">Private Views</a>
<ul class="collapsible-list tertiary">
<li ng-repeat="privateview in view.privateviews">
<a>{{privateview.title}}</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
Javascript:
define([ 'angular', 'controllers-module'], function(angular,
controllers) {
controllers.controller("ViewCtrl", [
"$scope",
"$rootScope",
"directiveBinder",
'$timeout',
'$stateParams',
'$resource',
'$state',
function($scope, $rootScope,directiveBinder, $timeout, $stateParams, $resource, $state) {
$scope.engines = [ {
name : 'name1',
publicviews : [ {
title : 'First public View'
} ],
privateviews : [ {
title : 'First private View'
} ]
}, {
name : 'name2',
publicviews : [ {
title : 'Second public View'
} ],
privateviews : [ {
title : 'Second private View'
} ]
} ];
$('.collapsible-list-parent').click(function(e) {
e.preventDefault();
$(this).next().slideToggle('fast');
if ($(this).parent().hasClass('open')) {
$(this).parent().removeClass('open');
} else {
$(this).parent().addClass('open');
}
});
});
Because the elements are added dynamically by ng-repeat the .click event is not binded to them. Try to use .delegate
$( "ul" ).delegate( ".collapsible-list-parent", "click", function() {
// code here
});
When we use ng-repeat and need to trigger a jquery click event just try this it worked for me.
$(document).on("click", ".className", function() {
//your code here...
});
I don't think using jQuery code in an angularjs controller is the right way to do this, a sample to do the same without the animation will be like
var app = angular.module('my-app', [], function() {
})
app.controller('ViewCtrl', function($scope) {
$scope.views = [{
name: 'name1',
publicviews: [{
title: 'First public View'
}],
privateviews: [{
title: 'First private View'
}]
}, {
name: 'name2',
publicviews: [{
title: 'Second public View'
}],
privateviews: [{
title: 'Second private View'
}]
}];
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="my-app">
<ul class="collapsible-list" ng-controller="ViewCtrl">
<li class="collapsible-list-subnav" ng-repeat="view in views">
<a class="collapsible-list-parent" ng-click="open = !open">{{view.name}}</a>
<ul class="collapsible-list secondary" ng-show="open">
<li class="collapsible-list-subnav">
<a class="collapsible-list-parent" ng-click="popen = !popen">Public Views</a>
<ul class="collapsible-list tertiary" ng-show="popen">
<li ng-repeat="publicview in view.publicviews">
<a>{{publicview.title}}</a>
</li>
</ul>
</li>
<li class="collapsible-list-subnav">
<a class="collapsible-list-parent" ng-click="ropen = !ropen">Private Views</a>
<ul class="collapsible-list tertiary" ng-show="ropen">
<li ng-repeat="privateview in view.privateviews">
<a>{{privateview.title}}</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
If you want to use animations you can make use of angularjs animation which uses css3 animations.

Run JS on appended html with angular

Hi I want to recreate the following using angularJS.
<div>
<fieldset class="regulated">
<legend>This list was created with html</legend>
<ul>
<li>
<h4>Category1</h4>
<ul>
<li>
<h5>Element1</h5>
<p>Description of Category 1 Element 1</p>
</li>
<li>
<h5>Element2</h5>
<p>Description of Category 1 Element 2</p>
</li>
<li>
<h5>Element1</h5>
<p>Description of Category 1 Element 3</p>
</li>
</ul>
</li>
<li>
<h4>Element2</h4>
<p>Description of element 2</p>
</li>
<li>
<h4>Category2</h4>
<ul>
<li>
<h5>Element1</h5>
<p>Description of Category 2 Element 1</p>
</li>
<li>
<h5>Element2</h5>
<p>Description of Category 2 Element 2</p>
</li>
</ul>
</li>
</ul>
</fieldset>
</div>
Here is how I tried and what I have achieved till now.
This is the html on which my controller is called.
<div>
<fieldset class="regulated" ng-controller="UlController2">
<legend>{{information.header}}</legend>
<ul>
<li ng-repeat="item in items">
<h4>{{item.name}}</h4>
<div ng-bind-html="element(item.followUp)"></div>
</li>
</ul>
</fieldset>
</div>
And this is the controller I am using.
app.controller('UlController2', function($scope,$sce) {
$scope.information = {
header : "This list was created with angular!"
};
$scope.items = [
{'name' : 'Category1', 'followUp' : '<ul></ul>'},
{'name' : 'Element2', 'followUp' : '<p>Description of element 2</p>'},
{'name' : 'Category2', 'followUp' : '<ul></ul>'}
];
$scope.elEments = [
{'name':'Element1','description':'Description of Category 1 Wlement 1'},
{'name':'Element2','description':'Description of Category 1 Wlement 2'},
{'name':'Element3','description':'Description of Category 1 Wlement 3'},
];
$scope.element = function(input){
return $sce.trustAsHtml(input);
}
});
I want to make this without any JQuery or native JavaScript, only AngularJS native methods.
It's not possible with the data you have there because you have no information about the category. It's also difficult for us because your variables names in the controller do not match the ones in the view. Other than that, given you have the information about the category you can do it with two nested ng-repeat.
angular.module('testApp', [])
.controller('testCtrl', ['$scope',
function($scope) {
$scope.categories = [
{
name: 'Category 1',
items: [
{
name: 'Element1',
description: 'Description of Category 1 Element 1'
},
{
name: 'Element2',
description: 'Description of Category 1 Element 2'
},
{
name: 'Element3',
description: 'Description of Category 1 Element 3'
}
]
},
{
name: 'Category 2',
items: [
{
name: 'Element1',
description: 'Description of Category 2 Element 1'
},
{
name: 'Element2',
description: 'Description of Category 2 Element 2'
},
{
name: 'Element3',
description: 'Description of Category 2 Element 3'
}
]
}
]
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="testApp" ng-controller="testCtrl">
<fieldset class="regulated">
<legend>This list was created with angular</legend>
<ul>
<li ng-repeat="category in categories">
<h4>{{category.name}}</h4>
<ul>
<li ng-repeat="item in category.items">
<h5>{{item.name}}</h5>
<p>{{item.description}}</p>
</li>
</ul>
</li>
</ul>
</fieldset>
</div>
I found 2 solutions, both written in angular 1.3.8.
The first is based on Marcel's answer and the other is mine.
This is based on Marcel:
app.controller('UlController2', function($scope, $sce) {
$scope.information = {
header: "This 2nd list was created with angular!"
};
$scope.items = [{
'name': 'Category1',
'innerList': [{
'name': 'Element1',
'description': 'Description of Category 1 Element 1'
}, {
'name': 'Element2',
'description': 'Description of Category 1 Element 2'
}, {
'name': 'Element3',
'description': 'Description of Category 1 Element 3'
}, ]
}, {
'name': 'Element2',
'followUp': 'Description of element 2'
}, {
'name': 'Category2',
'innerList': [{
'name': 'Element1',
'description': 'Description of Category 2 Element 1'
}, {
'name': 'Element2',
'description': 'Description of Category 2 Element 2'
}]
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
<div>
<fieldset class="regulated long">
<legend>This 2nd list was created with html</legend>
<ul>
<li>
<h4>Category1</h4>
<ul>
<li>
<h5>Element1</h5>
<p>Description of Category 1 Element 1</p>
</li>
<li>
<h5>Element2</h5>
<p>Description of Category 1 Element 2</p>
</li>
<li>
<h5>Element1</h5>
<p>Description of Category 1 Element 3</p>
</li>
</ul>
</li>
<li>
<h4>Element2</h4>
<p>Description of element 2</p>
</li>
<li>
<h4>Category2</h4>
<ul>
<li>
<h5>Element1</h5>
<p>Description of Category 2 Element 1</p>
</li>
<li>
<h5>Element2</h5>
<p>Description of Category 2 Element 2</p>
</li>
</ul>
</li>
</ul>
</fieldset>
</div>
<div>
<fieldset class="regulated long" ng-controller="UlController2">
<legend>{{information.header}}</legend>
<ul>
<li ng-repeat="item in items">
<h4>{{item.name}}</h4>
<p>{{item.followUp}}</p>
<ul>
<li ng-repeat="Li in item.innerList">
<h5>{{Li.name}}</h5>
<p>{{Li.description}}</p>
</li>
</ul>
</li>
</ul>
</fieldset>
</div>
And this is mine, simulating the reading of a JSON response.
app.controller('UlController3', function($scope, $sce) {
$scope.information = {
header: "This 3rd list was created with angular!"
};
var items = [{
'name': 'Category1',
'innerList': [{
'name': 'Element1',
'description': 'Description of Category 1 Element 1'
}, {
'name': 'Element2',
'description': 'Description of Category 1 Element 2'
}]
}, {
'name': 'Element2',
'followUp': 'Description of element 2'
}];
$scope.innerUl = function() {
var toAdd = '';
var inner = '';
angular.forEach(items, function(value, key) {
angular.forEach(value, function(firstLiVal, key) {
if (key == 'name') {
toAdd += '<li><h4>' + firstLiVal + '</h4>';
} else if (key == 'followUp') {
toAdd += '<p>' + firstLiVal + '</p></li>';
} else if (key == 'innerList') {
angular.forEach(firstLiVal, function(SecondUlVal, key) {
angular.forEach(SecondUlVal, function(SecondLiVal, key) {
if (key == 'name') {
inner += '<li><h5>' + SecondLiVal + '</h5>';
} else {
inner += '<p>' + SecondLiVal + '</p></li>';
}
});
});
toAdd += '<ul>' + inner + '</ul></li>';
}
});
});
return $sce.trustAsHtml(toAdd);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
<div>
<fieldset class="regulated long">
<legend>This 3rd list was created with html</legend>
<ul>
<li>
<h4>Category1</h4>
<ul>
<li>
<h5>Element1</h5>
<p>Description of Category 1 Element 1</p>
</li>
<li>
<h5>Element2</h5>
<p>Description of Category 1 Element 2</p>
</li>
</ul>
</li>
<li>
<h4>Element2</h4>
<p>Description of element 2</p>
</li>
</ul>
</fieldset>
</div>
<div>
<fieldset class="regulated long" ng-controller="UlController3">
<legend>{{information.header}}</legend>
<ul ng-bind-html='innerUl()'></ul>
</fieldset>
</div>

Angular Bootstrap 3 nav-bar submenu will not fire

I have Angular 1.2.1 and Bootstrap 3.0.2
Plain vanilla nav menu with a drop down works fine but when I try to generate my menu using ng-repeat the submenu will not fire.
The HTML:
<ul class="nav navbar-nav" ng-repeat="data in main_menu">
<li ng-class="{'dropdown' : data.nodes}">
<a href="{{data.link}}" ng-class="{'dropdown-toggle' : data.nodes}">{{data.name}} <b class="caret" ng-if="data.nodes"></b>
<ul ng-if="data.nodes" ng-repeat="items in data.nodes" class="dropdown-menu">
<li>{{items.name}}</li>
</ul>
</li>
</ul>
In the Controller:
$scope.main_menu = [
{
name: 'Home',
class: '',
link: '/',
nodes: false
},
{
name: "DropDown",
class: 'dropdown-toggle',
link: '#',
nodes: [
{
name: "Node2",
class: '',
link: 'link'
},
{
name: "Node2",
class: '',
link: 'link'
},
{
name: "Node2",
class: '',
link: 'link'
},
{
name: "Node2",
class: '',
link: 'link'
}
]
}
];
The normal bootstrap html submenu function works fine ..
Any suggestion ?
After checking ..
The Angular html Block is not correct, with this adjustment it renders the correct html - the drop down still does not work
<ul class="nav navbar-nav" >
<li ng-repeat="data in main_menu" ng-class="{'dropdown' : data.nodes}">
{{data.name}} <b class="caret" ng-if="data.nodes"></b>
<ul ng-if="data.nodes" class="dropdown-menu">
<li ng-repeat="items in data.nodes">{{items.name}}</li>
</ul>
</li>
</ul>
Two steps:
Add attr data-toggle="dropdown" for link.
Update controller, change link: '#' to link: ''.

Categories

Resources