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

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>

Related

How to pass Data from one template to another template in AngularJS

at first, I want to say that I am new at programing and also at AngularJS so please be a little forgiving.
My Problem is a little complex but i try to explain it as good as i can. At first i want to tell you what im actully doing.
I have to expand the local "Intrant" from my company and now i try to integrate an overview about the forms which are used by the employeers.
I have differnt groups and childgroups which are already implemented and shown on the first template. My problem is that i can not access to the data after i opend a new template which is made to show the forms which are a part of the childgroups.
Here you can see my first template, this shows all upper groups:
<div class="panel">
<div class="panel-body">
<h1 class="h3 nomargin">Formulare</h1>
</div>
</div>
<div class="row">
<ng-repeat ng-repeat="objFormGroup in arrFormGroups">
<div class="col-xs-12 col-lg-6">
<name-form-group data-group="objFormGroup"></name-form-group>
</div>
</ng-repeat>
</div>
The "objFromGroup" is filled in the "module.js" and the data is comming out of a REST-Service which is hosted local on my computer.
Also there is an dropdown menü which is opened throgh the "< name-from-group >"and this refers to another template:
<nav role="navigation">
<ul>
<li class="menu-item-has-children">
<button id="bdd" class="dropbtn" dropdown-toggle ng-disabled="disabled"><font color="#fff">{{group.name}}<span class="caret"></span></font></button>
<ul class="sub-menu">
<li ng-repeat="childGroup in group.ChildGroups">
{{childGroup.name}}
</li>
</ul>
<br> <br>
</li>
</ul>
</nav>
This is working. But now i want to open the third template (over_view_froms) to show the forms which are part of the childgroup the user clicked on. But when i try to display the forms by using the data which is comming out of the REST-Service it is not working. I also can not access to the data i used befor like {{childGroups.name}} on this template.
Here you can see the last template where the forms should be display:
<html>
<head>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
th, td {
padding: 5px;
text-align: left;
}
</style>
</head>
<body>
<table style="width:100%">
<caption>{{childGroup.name}}</caption>
<tr>
<th>Formular</th>
<th>Format</th>
<th>Größe</th>
<th>Stand</th>
</tr>
<tr>
<td>{{group.childGroup.Formulare.name}}</td>
<td>TEST</td>
<td>Test</td>
<td>Test</td>
</tr>
<tr>
<td>Test</td>
<td>Test</td>
<td>Test</td>
<td>Test</td>
</tr>
</table>
</body>
</html>
module.js:
strehlowIntranet
.config(['$stateProvider', 'strehlowConfigProvider', function ($stateProvider, strehlowConfigProvider) {
$stateProvider
.state('app.formgroups', {
url: '/formgroups',
label: 'Formulare',
template_icon: 'fa fa-file-pdf-o',
resolve: {
objFormGroups: ['FormsService', function (FormsService) {
return FormsService.getFormGroups();
}]
},
views: {
'content#app': {
templateUrl: 'app/templates/wforms/over_view.html',
controller: ['$scope', 'objFormGroups', function ($scope, objFormGroups) {
$scope.arrFormGroups = objFormGroups;
}]
}
}
});
$stateProvider
.state('app.groupdetail',{
url: '/groupdetail',
resolve: {
objSingleGroup: ['$stateParams', function ($stateParams) {
return $stateParams.group;
}]
},
views: {
'content#app': {
templateUrl: 'app/templates/wforms/over_view_forms.html',
controller: ['$scope', 'objSingleGroup', function ($scope, objSingleGroup) {
$scope.objGroup = objSingleGroup;
}]
}
}
});
}]);
So my question is how can i display the data on this template.
Thanks to everyone who trys to help me. (:

Improve Angular Tabs directive

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.

AngularJS: How to refresh parent controller

When the index.html page is rendered my ApplicationController will be called. When I login (login success)I want to refresh my ApplicationController. How to do it?
I have defined below Controllers in my application.
1)Login
angular.module('aclf').controller('LoginController', LoginController);
// The $inject property is an array of service names to inject.
LoginController.$inject = [ '$location', 'AuthenticationService',
'FlashService' ];
function LoginController($location, AuthenticationService, FlashService) {
var loginController = this;
loginController.login = login;
(function initController() {
// reset login status
AuthenticationService.ClearCredentials();
})();
function login() {
loginController.dataLoading = true;
AuthenticationService.Login(loginController.username,
loginController.password, function(response) {
if (response.username === loginController.username) {
console.log(response.authToken);
AuthenticationService.SetCurrentUser(
loginController.username,
response.authToken, true);
$location.path('/home');
} else {
FlashService.Error(response.message);
loginController.dataLoading = false;
}
});
}
;
}
2)ApplicationCotroller
angular.module('aclf').controller('ApplicationController',
function($scope,$rootScope) {
$scope.currentUser = $rootScope.globals.currentUser;
console.log('Inside ApplicationController');
})
3)HomeController- When home controller rendered I want to refresh the portion, since it contains currentUser.
/**
* Home Controller
*/
angular.module('aclf').controller('HomeController', HomeController);
// The $inject property is an array of service names to inject.
HomeController.$inject = [ 'UserService', '$rootScope' ];
function HomeController(UserService, $rootScope) {
var homeController = this;
homeController.user = null;
initController();
function initController() {
loadCurrentUser();
}
function loadCurrentUser() {
UserService.GetByUsername($rootScope.globals.currentUser.username)
.then(function(user) {
homeController.user = user;
});
}
}
4)This is my index.html page
Here I have defined my ApplicationController on body part. This needs to be refreshed at least after login and logout
<body data-ng-controller="ApplicationController">
<!-- TOPBAR START -->
<div id="layout-topbar" data-ng-show="currentUser">
<ul id="top-menu">
<li>
<span class="Fs22 FontRobotoLight">Welcome {{currentUser.username}} </span>
</li>
<li>
Logout
</li>
</ul>
</div>
<!-- TOPBAR END -->
<div id="wrapper">
<div id="wrapperIndent">
<div id="layout-menu-cover" class="Animated05 ps-container">
<div class="ps-scrollbar-x-rail" style="left: 0px; bottom: 3px;">
<div class="ps-scrollbar-x" style="left: 0px; width: 0px;"></div>
</div>
<div class="ps-scrollbar-y-rail" style="top: 0px; right: 3px;">
<div class="ps-scrollbar-y" style="top: 0px; height: 0px;"></div>
</div>
</div>
<div id="layout-portlets-cover">
<div class="Container96 Fnone MarAuto">
<div class="Container100">
<div class="ContainerIndent">
<div class="EmptyBox10"></div>
<div
data-ng-class="{ 'alert': flash, 'alert-success': flash.type === 'success', 'alert-danger': flash.type === 'error' }"
data-ng-if="flash" data-ng-bind="flash.message"></div>
<div data-ng-view></div>
</div>
</div>
<!-- footer -->
<div class="Container100">
<div class="ContainerIndent TexAlCenter Fs14">
Angular | All Rights Reserved.</div>
</div>
</div>
</div>
</div>
</div>
</body>
PS: I am very new to AngularJS
I have tried with reload page stuff. But it was not working.
I removed Application Controller and used
$routeProvider.when('/home', {
controller : 'HomeController',
templateUrl : 'partials/home.html',
controllerAs : 'homeController',
leftNav : 'partials/left-nav-main.html',
topNav: 'partials/top-nav.html'
})
.when('/login', {
controller : 'LoginController',
templateUrl : 'partials/login.html',
controllerAs : 'loginController'
})
Here the templateUrl property in the route definition references the view template that is loaded in the ng-view directive of the div element. I tried to simulate something similar to what Angular does for our left
and top navigation.
The value of the topNav property is used to load the top navigation view in the topnav div element ("id = top-nav") using the ng-include directive. We do the same for left navigation too. The ng-if directive in the left-nav section is used to hide left navigation if the current route configuration does not define the leftNav property.
The last part of this integration is setting up the currentRoute property and
binding it to ng-include. Angular sets up the ng-view template using the route configuration templateUrl property, but it does not know or care about the topNav and leftNav properties that we have added. We need to write some custom code that binds the navigation URLs with the respective ng-includes directives.
$scope.$on('$routeChangeSuccess', function (e, current, previous) {
$scope.currentRoute = current;
});
Here is my index.html
<div class="navbar navbar-default navbar-fixed-top top-navbar">
<!--Existing html-->
<div id="top-nav-container" class="second-top-nav">
<div id="top-nav" ng-include="currentRoute.topNav"></div>
</div>
</div>
<div class="container-fluid">
<div id="content-container" class="row">
<div class="col-sm-2 left-nav-bar"
ng-if="currentRoute.leftNav">
<div id="left-nav" ng-include="currentRoute.leftNav"></div>
</div>
<div class="col-sm-10 col-sm-offset-2">
<div id="page-content" ng-view></div>
</div>
</div>
</div>
Now the left-nav and top-nav appears only in home page and does not appear in login page.
Well, I think the best practice is to reload the entire page:
If you are using angular-ui/ui-router
$state.reload();
If you use another router
$route.reload();

Angular toggle class to parent element

For angular I'm using a Directive to Toggle Class:
app.directive('toggleClass', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.bind('click', function() {
element.toggleClass(attrs.toggleClass);
});
}
};
});
And the HTML I have multiple times:
<div class="tab" toggle-class="open">
<div class="title">
<p>Tab title</p>
</div>
<div class="content">
<!-- This element hidden by default, needs toggle -->
<p>Foobar</p>
</div>
</div>
It works, but when I click somewhere in the .content-element, it also toggles. But I want it to stay active. Not toggle-ing back to the close-state.
Is there an easy way to let it work like this:
- click on .title
- toggle .content (by adding .open to the .tab-element.
Added .parent()
app.directive('toggleClass', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.bind('click', function() {
element.parent().toggleClass(attrs.toggleClass);
});
}
};
});
And toggle-class to the title element.
<div class="tab">
<div class="title" toggle-class="open">
<p>Tab title</p>
</div>
<div class="content">
<!-- This element hidden by default, needs toggle -->
<p>Foobar</p>
</div>
</div>
I just consider that you don't need a custom directive to do this kind of things.
Here is two exemples, first one with the angular way to toggleClass, second one in a global angular way.
Both will meet your needs covered by this directive.
First One :
<div ng-repeat="aTab in [{},{},{},{},{}]" class="tab" ng-class="{'open':isOpen}">
<div class="title" ng-click="isOpen = !isOpen">
<p>Tab title</p>
</div>
<div class="content">
<!-- This element hidden by default, needs toggle -->
<p>Foobar</p>
</div>
</div>
with this CSS
.content {
display:none;
}
.open .content {
display:block;
}
Second one :
<div ng-repeat="aTab in [{},{},{},{},{}]" class="tab">
<div class="title" ng-click="aTab.show = !aTab.show">
<p>Tab title</p>
</div>
<div class="content2" ng-show="aTab.show">
<!-- This element hidden by default, needs toggle -->
<p>Foobar</p>
</div>
</div>
You can see theses exemples in this plunker
Hope it helped
EDIT : Updated the exemples and plunker to show how it works with an ng-repeat

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.

Resources