How to write directive to hide div clicking anywhere on page? - angularjs

This is first time that I am writing directive. I am trying to write directive to hide my div.This is my html:
<div id="loggedIn" close-logged-in class="fade-show-hide" ng-show="loggedInOpened" ng-cloak>
#Html.Partial("~/Views/Shared/_LoggedInPartial.cshtml")
</div>
I find element but when I click anywhere on page I dont get anything.Can someone help me how to write directive so that when shown user click anywhere on page, it will hide that div.Any suggestion?
'use strict';
angular.module("accountModule").directive('closeLoggedIn', function () {
return {
scope: {},
restrict: 'A',
link: function (scope, element, attrs) {
var loggedIn = angular.element(document.getElementById("loggedIn"));
console.log(loggedIn);
var isClosed = false;
loggedIn.on('click', function (e) {
console.log("LOGGED IN ON CLICK ", loggedIn);
});
}
}
I dont get this message "LOGGED IN ON CLICK"

You don't need getElementById("loggedIn") The <div> is already there for you as the element argument in the link function. There should rarely be any need to reference elements by their ID's in Angular.
Is this what you are trying to achieve?
DEMO
html
<div hide-when-click-anywhere default-display='block'>Click anywhere to hide me</div>
js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $window) {
$scope.isHidden = false;
});
app
.directive('hideWhenClickAnywhere', function ($window) {
return {
// bind a local scope (i.e. on link function scope) property
// to the value of default-display attribute in our target <div>.
scope: {
defaultDisplay: '#'
},
restrict: 'A',
link: function (scope, element, attrs) {
var el = element[0];
// set default css display value. Use 'block' if
// the default-display attribute is undefined
el.style.display = scope.defaultDisplay || 'block';
angular.element($window).bind('click', function(){
// Toggle display value.
// If you just want to hide the element and
// that's it then remove this if block
if(el.style.display === 'none'){
el.style.display = scope.defaultDisplay || 'block';
return;
}
el.style.display = 'none';
});
}
};
});
Update
Ok after reading you comments I think this might be more what you're trying to achieve;
Take note of this line in the <button> element defined in template.html:
ng-click="contentHidden = !contentHidden; $event.stopPropagation();"
$event.stopPropagation() stops the event from bubbling up and triggering the 'click' listener we have defined on $window in our directive.
DEMO2
app.js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $window) {
$scope.hideOnStart = true;
});
app
.directive('panel', function ($window) {
return {
scope: {
// this creates a new 'isolate' scope
// '=' sets two-way binding between the directive
// scope and the parent scope
// read more here https://docs.angularjs.org/api/ng/service/$compile
hideOnStart: '=',
panelTitle: '#'
},
transclude: true,
templateUrl: 'template.html',
restrict: 'E',
link: function (scope, element, attrs) {
console.log(scope.panelTitle)
// div content is hidden on start
scope.contentHidden = scope.hideOnStart || false;
angular.element($window).bind('click', function(e){
// check if the content is already hidden
// if true then ignore
// if false hide the content
if(!scope.contentHidden){
scope.contentHidden = true;
// we have to manually update the scope
// because Angular does not know about this event
scope.$digest();
}
});
}
};
});
template.html
<div class="panel panel-default">
<div class="panel-heading">
<div class="panel-title">{{panelTitle}}
<button
type="button"
class="close"
ng-click="contentHidden = !contentHidden; $event.stopPropagation();"
aria-label="Close"
>
<span ng-hide="contentHidden">close</span>
<span ng-hide="!contentHidden">open</span>
</button>
</div>
</div>
<div class="panel-body" ng-hide="contentHidden" ng-transclude></div>
</div>
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.3/angular.js" data-semver="1.4.3"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div class="container">
<h1>Demo</h1>
<div class="row">
<div class="col-sm-6">
<panel panel-title="Title" hide-on-start="hideOnStart">
<h4>Content...</h4>
<p>foo bar baz</p>
</panel>
</div>
</div>
</div>
</body>
</html>

Related

How to control invoking of directive

In this plnkr :
https://plnkr.co/edit/F0XsOPZKq5HArFo9vtFs?p=preview
I'm attempting to prevent a custom directive being invoked by the use of ng-show. But if check console output when the directive is invoked 4 times : console.log('invoked') But ng-show shows/hides html elements it does not control what is rendered within the custom directive itself.
Is there a mechanism to pass the ng-show to the custom directive and if it's false then do call the directive ? I think could pass a new variable to the directive which contains same value as ng-show and then wrap the body of the directive in a conditional ?
src :
goob.html :
goob
http-hello2.html:
2. http-hello2.html
index.html :
<!doctype html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="FetchCtrl">
<label>Filter: <input ng-model="search"></label>
<div ng-show="false">
<div ng-repeat="sourceUrl in sourceUrls | filter:search track by $index ">
<status-viewer url="sourceUrl"> </status-viewer>
</div>
</div>
</div>
</body>
</html>
mytemplate.html :
<!--<h1>{{url}}</h1>-->
<div>
<p>{{model}}</p>
</div>
script.js :
var myapp = angular.module('app', []).controller('FetchCtrl', FetchCtrl)
myapp.directive('statusViewer', function ($http , $interval) {
return {
restrict: 'E',
templateUrl: 'mytemplate.html',
scope: {
url: '='
},
link: function (scope, elem, attrs, ctrl) {
console.log('invoked')
scope.isFinishedLoading = false;
$http.get(scope.url).success(function (data) {
scope.model = data;
});
}
};
});
function FetchCtrl($scope, $http, $q , $parse) {
$scope.sourceUrls = [
'http-hello2.html'
,'http-hello2.html'
,'test.html'
,'goob.html'];
}
test.html :
test
Instead of ng-show you should use ng-if directive to avoid directive linking before show
Forked plunker example

How to apped a `directive` by click

Say, I have multiple directives, on click how can i add the "directive" what i want to "popup". i have number of scenario, but using only one pop-up for all. whenever the appropiate element is clicked, using the same pop-up, i would like to update the other 'directives'
for sample, i created only one. click on the red bordered title
here is my code :
<div ng-controller="main">
{{name}}
<div class="info" ng-click="popIt()">
<info-board></info-board>
</div>
<div class="popup" ng-if="show" ng-click="popIt()">
//add directive dynamically - how?
</div>
</div>
</body>
<script type="text/ng-template" id="modalPopup.html">
<div class='ng-modal'>
some information here.
</div>
</script>
js:
var myApp = angular.module("myApp", []);
myApp.controller("main", function($scope) {
$scope.name = "Mo!";
$scope.show = false;
$scope.popIt = function (show) {
$scope.show = !$scope.show;
}
})
myApp.directive("infoBoard", function() {
return {
replace : true,
templateUrl: "modalPopup.html",
scope : {},
link : function (scope, element, attrs) {
element.append("Hi there!")
}
}
})
Demo Onlie

Angular accordion doesn't update UI when data source has been changed

I start learning Angular and faced with some strange behaviour.
I want to add a new header to accordion dynamically but I accordion doesn't reflect it on UI till I explicitly click on some of his items. By some reason he doesn't react on items changes before it starts load itserlf aggain durnig DOM rendering.
var mainApp = angular.module('ui.bootstrap.demo', ['ui.bootstrap', 'ngResource']);
mainApp.factory('teamSharedObj',['$rootScope', function($rootScope) {
return {
teams: [],
peopleInTeam: [],
addNewTeam: function(item) {
console.log("add new team: " + item);
this.teams.push(item);
$rootScope.$broadcast('team.new');
},
addTeamMembers: function(team, teamMembers) {
for (var i = 0; i < teamMembers.length; i++) {
var temp;
// put in team as key-value pair
temp[team] = teamMembers[i]
console.log("add new team member: " + temp);
peopleInTeam.push(temp);
}
if (teamMembers.length != 0) {
$rootScope.$broadcast('teamMember.new');
}
}
}
}]);
mainApp.directive("addNewTeam", ['teamSharedObj', 'teamSharedObj', function (teamSharedObj) {
return {
restrict: 'A',
link: function( scope, element, attrs ) {
element.bind('click', function() {
console.log(scope.teamName)
teamSharedObj.addNewTeam(scope.teamName)
});
}
}
}])
mainApp.controller('teamListCtrl', ['$scope', 'teamSharedObj', function($scope, teamSharedObj) {
$scope.$on('team.new', function(event) {
console.log('new team ' + event);
$scope.items = teamSharedObj.teams;
}
);
$scope.oneAtATime = true;
$scope.items = ['new', 'another one'];//teamSharedObj.teams;
}]);
<!DOCTYPE html>
<html>
<head lang="en">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-resource.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.2.js"></script>
<script src="js/test.team.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body ng-app="ui.bootstrap.demo">
<div id="teamBlock">
<input type="text" ng-model="teamName" >
<input add-new-team type="submit" value="Add new team" >
<!--<button add-book-button>Add data</button>-->
</div>
<div>
{{teamName}}
</div>
<div ng-controller="teamListCtrl">
<accordion close-others="oneAtATime" >
<accordion-group heading="{{d}}" ng-repeat="d in items">
This content is straight in the template.
</accordion-group>
</accordion>
<div ng-repeat="item in items">{{item}}</div>
</div>
</body>
</html>
Can you suggest me please a right way to notifu component about changes in its datasource?
bind is jqLite/jQuery method and does not automatically trigger the digest loop for you. This means no dirty checking will take place and the UI will not be updated to reflect the model changes.
To trigger it manually wrap the code in a call to $apply:
element.bind('click', function() {
scope.$apply(function () {
teamSharedObj.addNewTeam(scope.teamName);
});
});
And since teamSharedObj contains a reference to the array the controller can reference it directly. Then you do not need to use $broadcast:
addNewTeam: function(item) {
this.teams.push(item);
},
And:
mainApp.controller('teamListCtrl', ['$scope', 'teamSharedObj',
function($scope, teamSharedObj) {
$scope.oneAtATime = true;
$scope.items = teamSharedObj.teams;
}
]);
Demo: http://plnkr.co/edit/ZzZN7wlT10MD0rneYUBM?p=preview

Call jQuery function from AngularJS Controller

I have a below button when on clicked shows a small popup like notification
<button id="element" type="button" onclick="ShowNotifications()" class="btn btn-default" data-container="body" data-toggle="popover" data-placement="bottom" data-content="Text inside popup">Notifications</button>
<script type="text/javascript">
function ShowNotifications() {
$('#element').popover('open');
}
</script>
My Intention is to Show this popup every few seconds without clicking the button, but from the AngularJS Controller.
var showPop = function () {
//how can i call that jQuery function here ??
$timeout(showPop, 1000);
}
$timeout(showPop, 1000);
Tried with the below solution
app.directive("showNotifications", ["$interval", function ($interval) {
return {
restrict: "A",
link: function(scope, elem, attrs) {
$interval(function () {
$(elem).popover("open");
alert('hi');
}, 1000);
}
};
}]);
Also included the scripts
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<script src="js/app.js"></script>
<script src="js/postsService.js"></script>
<script src="js/directive.js"></script>
<script src="js/controllers.js"></script>
using the directive like this
<button id="element" type="button" class="btn btn-default" data-container="body" data-toggle="popover" data-placement="bottom" data-content="Friend request 1" **show-notifications**>Live Notifications</button>
I see an error "the object has no method popover"
Directives are used for DOM manipulation:
<button show-notifications>
And the directive
.directive("showNotifications", ["$interval", function($interval) {
return {
restrict: "A",
link: function(scope, elem, attrs) {
//On click
$(elem).click(function() {
$(this).popover("open");
});
//On interval
$interval(function() {
$(elem).popover("open");
}, 1000);
}
}
}]);
The following steps can be followed,
var jq = $.noConflict();
then create any regular angular module and controller and create a function inside the controller which we can use it for the calling any jquery function, e.g. I want to add a class to a div element.
angular.module('myApp',[]).controller('hello',function($scope){
$scope.name = 'Vikash';
$scope.cities = ['Delhi','Bokaro','Bangalore'];
$scope.hide = function(){
jq('#hideme').addClass('hidden');
}
});
and we will create some regular html to utilize that method with the controller.
<body ng-controller="hello">
<div class="container" id="hideme">
Hello Dear
</div>
<button ng-click="hide()">Hide Hello</button>
</body>
Now here you can see that we are about call addClass method from the jQuery inside the function declared in the controller and part of the $scpe.
Instead of a $ just place the key word angular
angular.element("#id").val()

Scope issue in AngularJS using AngularUI Bootstrap Modal

plunker: http://plnkr.co/edit/wURNg8ByPYbEuQSL4xwg
example.js:
angular.module('plunker', ['ui.bootstrap']);
var ModalDemoCtrl = function ($scope, $modal) {
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'modal.html',
controller: 'ModalInstanceCtrl'
});
};
};
var ModalInstanceCtrl = function ($scope, $modalInstance) {
$scope.ok = function () {
alert($scope.text);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
index.html:
<!doctype html>
<html ng-app="plunker">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js"></script>
<script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.6.0.js"></script>
<script src="example.js"></script>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
</head>
<body>
<div ng-controller="ModalDemoCtrl">
<button class="btn" ng-click="open()">Open me!</button>
<div ng-show="selected">Selection from a modal: {{ selected }}</div>
</div>
</body>
</html>
modal.html:
<div class="modal-header">
<h3>I'm a modal!</h3>
</div>
<textarea ng-model="text"></textarea>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
Why I can't get the $scope.text defined in ModalInstanceCtrl, even though I can use $scope.ok and $scope.cancel?
Looks like a scope issue. I got it to work like this:
var ModalInstanceCtrl = function ($scope, $modalInstance) {
$scope.input = {};
$scope.ok = function () {
alert($scope.input.abc);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
HTML:
<textarea ng-model="input.abc"></textarea>
Update Nov 2014: the issue is fixed with angular-ui-bootstrap 0.12.0 - the transclusion scope is merged with the controller's scope. There is no need to do anything. Just stay with:
<textarea ng-model="text"></textarea>
Before 0.12.0:
Angular-UI modals are using transclusion to attach modal content, which means any new scope entries made within modal are created in child scope.
You should use inheritance and initialize empty text entry in parent $scope
or you can explicitly attach the input to parent scope:
<textarea ng-model="$parent.text"></textarea>
Let'me try to explain the reason. ui-bootstrap modal sourcecode:
.directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
return {
restrict: 'EA',
scope: {
index: '#',
animate: '='
},
replace: true,
transclude: true,
templateUrl: function(tElement, tAttrs) {
return tAttrs.templateUrl || 'template/modal/window.html';
},
and the template sourcecode - window.html:
<div tabindex="-1" role="dialog" class="modal fade" ng-class="{in: animate}" ng-style="{'z-index': 1050 + index*10, display: 'block'}" ng-click="close($event)">
<div class="modal-dialog" ng-class="{'modal-sm': size == 'sm', 'modal-lg': size == 'lg'}"><div class="modal-content" modal-transclude></div></div>
there is a directive modal-transclude,your dialog content will insert into it, it's sourcecode:
.directive('modalTransclude', function () {
return {
link: function($scope, $element, $attrs, controller, $transclude) {
$transclude($scope.$parent, function(clone) {
$element.empty();
$element.append(clone);
});
}
};
})
now take a look at offical doc of $compile:
Transclusion Functions
When a directive requests transclusion, the compiler extracts its contents and provides
a transclusion function to the directive's link function and controller.
This transclusion function is a special linking function that will return the compiled
contents linked to a **new transclusion scope.**
transclude will create a new scope of controller scope

Resources