Improve Angular Tabs directive - angularjs

I created an angular tab directive width JQuery.
I can't find the problem with it because all seems right.
Could someone check what am I doing wrong?
I think my Angular directive is to dependent of jQuery.
What steps should I take to make it more "Angular"?
angular.module('app', []);
angular.module("app").directive("tabs", function() {
return {
restrict: "A",
link: function(scope, element, attributes) {
var tabs = element.find("a");
var sections = links.map(function() {
return $("#" + this.href.split("#")[1])
});
$(tabs[0]).show();
sections[0].show();
tabs.bind("click", function(event) {
event.preventDefault();
tabs.not(this).hide();
$(this).siblings("a").hide();
$.each(sections, function() {
$(this).hide()
});
$("#" + this.href.split("#")[1]).show();
});
}
};
});
ul.tabs {
list-style: none;
margin: 0;
padding: 0;
}
ul.tabs li {
display: inline-block;
margin: 0;
padding: 0;
}
ul.tabs li a {
text-decoration: none;
color: white;
padding: 8px;
display: block;
background-color: indigo;
}
div.section {
background-color: #f0f0f0;
padding: 8px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<ul class="tabs">
<li>Tab 1
</li>
<li>Tab 2
</li>
</ul>
<div id="section1" class="section">Section 1</div>
<div id="section2" class="section">Section 2</div>

I'm not going to try fixing your code. Just explain how you would do this the Angular way.
jQuery is all about DOM manipulation. In Angular, DOM manipulation is bad. The DOM should automatically adapt itself based on the model. Tabs can simply be implemented the following way:
<ul class="tabs">
<li><a href ng-click="selectedTab = 1">Tab 1</a>
</li>
<li><a href ng-click="selectedTab = 2">Tab 2</a>
</li>
</ul>
<div class="section" ng-show="selectedTab === 1">Section 1</div>
<div class="section" ng-show="selectedTab === 2">Section 2</div>
This is much simpler: clicking on a tab changes the value of the selectedTab model value. The sections hide/show themselves based on the value of selectedTab.

Can be something like:
angular.module("app").directive("tabs", function() {
return {
restrict: "A",
scope: {
tabs: '='
},
template : '<ul><li ng-repeat="section in tabs" ng-click="handleClick(section)" ng-class="{selected : section.selected}">{{section.name}}</li></ul>',
link: function(scope, element, attributes) {
scope.handleClick = function(section) {
//....
}
}
};
});
where tabs is array like [{name : 'N1', href : '#smth', selected: false}, ...] in html:
<div tabs="tabs"></div>
You can add selected property, disabled, etc. Also notice - that if you change tabs array - template will redraw itself automatically.

Related

I have the following code for a popover however when I resize the page, i want to close the popover

My html
<div id="deviceInputContainer">
<div class="row noMarg">
<div class="form col-xs-12" style='padding-left:0px;margin-right:15px;'>
<div class="form col-xs-12 noPad left">
<h2 class="page-title">Certification Projects
<span class='icon-settings-big' style='cursor:pointer;float:right;margin-top:-10px;' title='settings' uib-popover-template="dynamicPopoverPageSettings.templateUrl"
popover-placement="bottom-right" popover-trigger="click outsideClick" popover-class="settingsClass" ></span>
</h2>
</div>
<div class="helpMessage" style="margin-left:-15px;margin-right:-15px;" ng-show="dashboardData.userPreferences.showHelpTextEnabled">
<p class="help-text-content col-sm-12 helpText-color helpText-size" style='margin-bottom:15px;'>Your open projects are listed below- but you can search for other projects if you want. Just
set the search criteria below.</p>
</div>
</div>
</div>
</div>
My controller code to toggle popover and on window resize close popover.
But popover hide is not working on window rezise, can somebody please help me where am i doing wrong
$scope.dynamicPopoverPageSettings = {
templateUrl: 'myPopoverTemplatePageSetting.html',
title: 'Page Settings',
isPopOpen: false,
setIsPopOpen: function() {
$scope.dynamicPopoverPageSettings.isPopOpen = !$scope.dynamicPopoverPageSettings.isPopOpen;
console.log("$scope.dynamicPopoverPageSettings.isPopOpen == " + $scope.dynamicPopoverPageSettings.isPopOpen);
},
setIsPopFalse: function() {
$scope.dynamicPopoverPageSettings.isPopOpen = false;
console.log("$scope.dynamicPopoverPageSettings.isPopOpen == " + $scope.dynamicPopoverPageSettings.isPopOpen);
}
};
var w = angular.element($window);
w.bind('resize', function () {
$('.settingsClass ').popover('hide');
});
When using angular if not using it already id recommend using Angular-Bootstrap-native AngularJS directives based on Bootstrap's markup and CSS. I've included this link to their site, it's the 0.14.3 docs. Also including this Codepen with an example of what I think you're trying to accomplish. Hope it helps, I can always help and modify it further.
function exampleController($scope, $window) {
$scope.popoverVisible = false;
function onResize() {
$scope.popoverVisible = false;
// manuall $digest required as resize event
// is outside of angular
$scope.$digest();
}
function cleanUp() {
angular.element($window).off("resize", onResize);
}
angular.element($window).on("resize", onResize);
$scope.$on("$destroy", cleanUp);
}
angular
.module("example", ["ui.bootstrap"])
.controller("exampleController", exampleController);
html,
.container,
.container-fluid {
width: 100%;
height: 100%;
background-color: #333;
color: #fff;
text-align: center;
button {
margin-top: 10%;
font-weight: bold;
}
button:focus {
outline: 0;
}
.popover {
.popover-title,
.popover-content {
color: #333;
}
}
}
<div class="container-fluid" ng-app="example">
<div class="container" ng-controller="exampleController">
<button uib-popover="I have a title!" popover-title="The title." popover-placement="bottom-right" popover-is-open="popoverVisible" type="button" class="btn btn-primary">
CLICK ME
</button>
</div>
</div>

angular same height directive not working

I have created a directive so that the columns in a bootstrap row are of same height.
Here is the code - http://jsbin.com/waxoboloqo/edit
But it is not working. Can you please fix it ?
Html code:
<h1>Height: {{ hei }}</h1>
<div class="row">
<div class="col-lg-6 col-1">
<h1 ng-repeat="x in y" match-height>{{x}}</h1>
</div>
<div class="col-lg-6 col-2">
</div>
</div>
CSS:
.col-lg-6 {
border: 1px solid;
min-height: 50px;
}
Js:
angular.module('myApp', [])
.controller('myController', function($scope){
$scope.y = [1,2,3];
$scope.hei = "initial"
})
.directive('matchHeight', function(){
return function(scope, element) {
if(scope.$last){
scope.hei = $(element).height();
$(element).closest(".row").find(
".col-2").height(scope.hei);
}
}
});
you can try this.
app.directive('getHeight',function () {
return {
restrict: 'AE',
link: function (scope, element, attrs) {
scope.$watch(function(){
scope.style = {
height:element[0].offsetHeight+'px',
// width:element[0].offsetWidth+'px'
};
});
}
};
});
<div class="col-lg-6 col-1" get-height>
<h1 ng-repeat="x in y">{{x}}</h1>
</div>
<div class="col-lg-6 col-2" ng-style="style">
</div>

AngularJS - Scroll div container when clicking on item in different div

Context: I have a left-menu (navigator) and right-panel (content) divs. Every item from the navigator is linked to the ID of its section in the right-panel.
Problem: I'm trying to scroll the right panel to the corresponding section when clicking on the menu item from the navigation panel.
I am doing it with anchorScroll() and locator.hash() functions. It works, but it scrolls the whole page (navigation and content).
I have created two controllers: One for the parent (including the navigator) and other for the container (the one I want to scroll). When I click in the item from the menu, I am changing a variable within the scope.
Then, from the child scope, I am watching that variable, performing the scroll with
html:
html:
<!-- Navigation Panel -->
<div id="helpNavigatorPanel">
<ul>
<li ng-repeat="(value, item) in list" ng-click="setCurrentItem (item.title)">
<a style="helpNavigationItems" href="#">{{item.title}}</a>
</li>
</ul>
</div>
<div id="helpContentPanel"ng-controller="contentHelpController" >
<div id="scrollablePanel">
<!-- List of help elements -->
<ul>
<li ng-repeat="some in list">
<div id="{{some.title}}" style="margin-top: 3px; padding: 10px; border: 1px solid lightGrey;">
...
</div>
</div>
</li>
</ul>
</div>
</div>
js:
Scope parent:
$scope.setCurrentItem = function (currentItemID){
$scope.currentItemID = currentItemID;
}
Scope child:
$scope.$watch('currentItemID', function (divDestinyID) {
$location.hash(divDestinyID);
$anchorScroll();
});
The problem is that the whole page is scrolled.
Any idea ?
Thank you!
A possible solution is to make your "child" into a directive. That way, you can scroll on the element, rather than the entire page
content-help.directive.js
angular
.module('appModule')
.directive('contentHelp', contentHelp);
function contentHelp() {
var directive = {
bindToController: true,
controller: ContentHelpController,
controllerAs: 'vm',
restrict: 'EA',
scope: {
list: '=',
currentItemId: '='
},
templateUrl: 'contentPanel.html'
};
return directive;
}
ContentHelpController.$inject = ['$scope', '$element'];
/* #ngInject */
function ContentHelpController ($scope, $element) {
var vm = this;
$scope.$watch(function() {
return vm.currentItemId;
}, onIdChange);
////
function onIdChange(newId) {
if(newId) {
var idElement = $element.find('#' + newId);
$element.animate({
scrollTop: idElement.offset().top
});
}
}
}
contentPanel.html
<div id="scrollablePanel">
<!-- List of help elements -->
<ul>
<li ng-repeat="some in vm.list">
<div id="{{::some.title}}" style="margin-top: 3px; padding: 10px; border: 1px solid lightGrey;">
...
</div>
</li>
</ul>
</div>
html
<!-- Navigation Panel -->
<div id="helpNavigatorPanel">
<ul>
<li ng-repeat="(value, item) in list" ng-click="setCurrentItem (item.title)">
<a style="helpNavigationItems" href="#">{{item.title}}</a>
</li>
</ul>
</div>
<div id="helpContentPanel" ng-controller="contentHelpController">
<content-help list="list" current-item-id="currentItemId"></content-help>
</div>

Want to make the li elements of ul list visible but not clickable using angularjs

I have a small issue regarding how to make the 'li' elements of 'ul' list visible but not-clickable. I tried using ng-disabled directive to disable it i.e., not-clickable but it doesn't seem to work. Could please help me out in this issue which would be highly appreciable.
Code Example (Plunker Link)
If you just want to prevent click events from registering on your li's, you could implement this CSS rule:
li { pointer-events: none; }
If you need this to be dynamically set from Angular, consider using the core ngClass directive:
CSS:
.no-click { pointer-events: none; }
HTML:
<li ng-class="{'no-click': clickable}">No Touching!</li>
Sources:
ngClass: https://docs.angularjs.org/api/ng/directive/ngClass
More info on pointer-events here: https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
Reasonably supported in everything but IE: http://caniuse.com/#feat=pointer-events
You can use this driective:
(function () {
'use strict';
angular
.module('app.widgets')
.directive('zzDisableClickIf', disableClickIf);
function disableClickIf($parse, $rootScope) {
// Usage:
// <li>
// <a ui-sref="somestate" zz-disable-click-if="!model.isValid()">Go Somestate</a>
// </li>
return {
// this ensure zzDisableClickIf be compiled before ngClick
priority: 100,
restrict: 'A',
compile: function ($element, attr) {
var fn = $parse(attr.zzDisableClickIf);
return {
pre: function link(scope, element) {
var eventName = 'click';
element.on(eventName, function (event) {
var callback = function () {
if (fn(scope, { $event: event })) {
// prevents ng-click to be executed
event.stopImmediatePropagation();
// prevents href
event.preventDefault();
return false;
}
};
if ($rootScope.$$phase) {
scope.$evalAsync(callback);
} else {
scope.$apply(callback);
}
});
},
post: function () { }
}
}
}
}
} ());
This directives stops the action of if zz-disable-click-if='false'
var app=angular.module("app",[])
app.controller("itemController",function($scope){
$scope.condition=false;
$scope.disableLink=function(){
$scope.condition=!$scope.condition;
}
});
<html>
<style>
.not-active {
pointer-events: none;
cursor: default;
color: #ddd;
}
</style>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.16/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="app">
<div ng-controller="itemController">
<div >
<ul class="list-group" >
<li class="list-group-item"><a href='https://www.google.co.uk/webhp?hl=en'>Item1</a></li>
<li class="list-group-item"><a ng-class={'not-active':condition} href='https://www.google.co.uk/webhp?hl=en'>Item2</a></li>
<li class="list-group-item"><a href='https://www.google.co.uk/webhp?hl=en'>Item3</a></li>
</ul>
</div>
<input type='button' value='Enable/Disable Link' ng-click='disableLink()'>
</div>
</body>
</html>
What about just using ng-show / ng-hide to show an <a>or a <span> depending on the boolean holding the fact that the item should be clickable or not ?
That's not the sexiest, but it's still clean.
Here is a plunker (i assumed your list is dynamically created but that's not something really important)
The HTML :
<ul class="list-group" >
<li ng-repeat="item in items" class="list-group-item">
<span ng-hide="item.clickable">{{item.name}}</span>
<a ng-show="item.clickable" href=''>{{item.name}}</a>
<button ng-click="item.clickable = !item.clickable">Disable/Enable</button>
</li>
</ul>
I also added for the demo a button to disable/enable the link.
Tell me if what do you think about this solution.

On click adding and removing classes in angularJs

I want to remove active class from an li and add it to another li.
I know how to do it with jQuery but I don't know how to make it work with AngularJS.
Here is my code :
$(document).ready(function() {
// event to nav bar header
$("ul.nav li").click(function() {
$("ul.nav li").removeClass("active");
$(this).addClass("active");
});
});
Demo Here
You can build up an array of your nav items in your scope and call a function which sets a variable to true if the respective string matches.
Furthermore, I suggest building your nav with ng-repeat based on this very array.
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
var navArr = [
'home',
'help',
'admin'
];
$scope.setNavActive = function(str) {
console.log(str);
angular.forEach(navArr, function(item) {
console.log(item);
$scope[item] = (str == item);
});
};
$scope.home = true;
}
.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus {
color: #fff;
background-color: #6da54d;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<ul class="nav nav-pills pull-right mynavbar">
<li ng-class="{active: home}" ng-click="setNavActive('home')"><a ng-href="#/">Home</a></li>
<li ng-class="{active: help}" ng-click="setNavActive('help')"><a ng-href="#/help">Help</a></li>
<li ng-class="{active: admin}" ng-click="setNavActive('admin')"><a ng-href="#/administration">Administration</a></li>
</ul>
</div>
I like to use the ternary operator
ng-class="(clickedBoolean) ? 'active' : ''"
Just change clickedBoolean to whatever your click event is changing and your ng-class directive will act accordingly.

Resources