This question already has answers here:
AngularJS dropdown directive hide when clicking outside
(9 answers)
Closed 7 years ago.
Please refer to this jsfiddle. http://jsfiddle.net/jaredwilli/vUSPu/
This is for multi select dropdown. User selects options available in the drop and clicks outside the dropdown menu. The dropdown list doesnt close unless you click on the dropdown menu itself.
Is there any way to hide the dropdown when user clicks anywhere else away from dropdown list.
<div ng-app="myApp" ng-controller="AppCtrl">
<dropdown-multiselect pre-selected="member.roles" model="selected_items" options="roles"></dropdown-multiselect>
<pre>selected roles = {{selected_items | json}}</pre>
'use strict';
var app = angular.module('myApp', ['app.directives']);
app.controller('AppCtrl', function($scope){
$scope.roles = [
{"id": 1, "name": "Manager", "assignable": true},
{"id": 2, "name": "Developer", "assignable": true},
{"id": 3, "name": "Reporter", "assignable": true}
];
$scope.member = {roles: []};
$scope.selected_items = [];
});
var app_directives = angular.module('app.directives', []);
app_directives.directive('dropdownMultiselect', function(){
return {
restrict: 'E',
scope:{
model: '=',
options: '=',
pre_selected: '=preSelected'
},
template: "<div class='btn-group' data-ng-class='{open: open}'>"+
"<button class='btn btn-small'>Select</button>"+
"<button class='btn btn-small dropdown-toggle' data-ng-click='open=!open;openDropdown()'><span class='caret'></span></button>"+
"<ul class='dropdown-menu' aria-labelledby='dropdownMenu'>" +
"<li><a data-ng-click='selectAll()'><i class='icon-ok-sign'></i> Check All</a></li>" +
"<li><a data-ng-click='deselectAll();'><i class='icon-remove-sign'></i> Uncheck All</a></li>" +
"<li class='divider'></li>" +
"<li data-ng-repeat='option in options'> <a data-ng-click='setSelectedItem()'>{{option.name}}<span data-ng-class='isChecked(option.id)'></span></a></li>" +
"</ul>" +
"</div>" ,
controller: function($scope){
$scope.openDropdown = function(){
$scope.selected_items = [];
for(var i=0; i<$scope.pre_selected.length; i++){ $scope.selected_items.push($scope.pre_selected[i].id);
}
};
$scope.selectAll = function () {
$scope.model = _.pluck($scope.options, 'id');
console.log($scope.model);
};
$scope.deselectAll = function() {
$scope.model=[];
console.log($scope.model);
};
$scope.setSelectedItem = function(){
var id = this.option.id;
if (_.contains($scope.model, id)) {
$scope.model = _.without($scope.model, id);
} else {
$scope.model.push(id);
}
console.log($scope.model);
return false;
};
$scope.isChecked = function (id) {
if (_.contains($scope.model, id)) {
return 'icon-ok pull-right';
}
return false;
};
}
}
});
I've tried your jsFiddle i see what you mean. I think this would help you.
With a little bit of searching alot can be found :)
AngularJS dropdown directive hide when clicking outside
Related
Everything seems to render properly but my ng-click="check($event)" is not firing when I click on one of the four buttons. I even added an alert to the check($event) function but it's not coming up. Heres the codepen http://codepen.io/theMugician/pen/MKOzzV?editors=101
simonApp.directive('pads', ['lightIt', 'soundIt', function(lightIt,soundIt) {
var template = '';
template += '<button ng-click="check($event)" id=' + '{{index}} ' + 'class="pad pad-' + '{{color}}' + '">';
template += '<div class="ripple js-ripple">';
template += '<span class="ripple-circle ' + 'ripple-' + '{{color}}' + '"></span></div>';
template += '</button>';
return {
restrict: 'E',
replace: true,
scope: {
color: '#color',
index: '#index'
},
link: function(scope, element, attrs) {
scope.createRipple = function(e){
lightIt.createRipple(e, this);
}
//scope.$emit('index', scope.index);
/*
var index = parseInt(scope.index);
scope.playSound = function() {
soundIt(1);
}*/
console.log("scope.index: " + scope.index);
//console.log("scope.playSound: " + scope.playSound);
element.on('click', scope.createRipple);
//element.on('animationend', scope.endRipple);
},
template: template
};
}]);
Here is the check() function in the controller
$scope.check = function(e) {
alert("check works");
var id = e.currentTarget.id;
if($scope.init){
if(sequence[turn] === id){
turn++;
if(turn === sequence.length){
$scope.step++;
setTimeout(function(){
newRound();
}, 1000);
}
}else{
if($scope.isStrict){
setTimeout(function(){
alert('game over');
$scope.reset();
}, 300);
}else{
$scope.displaySeq(sequence);
}
}
}
}
You cannot call controller method from directive like that.
In case you need scope isolation you have to use expression binding ( '&' scope parameters) or turn off scope isolation all together ( remove scope key from directive definition )
See excellent video from John Lindquist for example of using scope expression binding.
var app = angular.module('app', [])
app.directive("pads", function() {
return {
restrict: "E",
scope: {
check: "&"
},
template: '<div class="button" ng-click="check({message: value})">click</div></div>',
link: function(scope) {
scope.value = 'message to controller'
}
};
});
app.controller('myCtrl', function() {
this.doCheck = function(message) {
alert(message) // alerts "message to controller"
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app' ng-controller='myCtrl as ctrl'>
<pads check="ctrl.doCheck(message)"></pads>
</div>
I have been playing around with Angular Material and I wanted to create a $mdDialog that allows a user to enter in information that when saved, will update an object tied to a ng-repeat.
While trying to get this to work and trying out different parameters for mdDialog.show() I was confused about what scope is being used when/why.
This is the first implementation:
(function () {'use strict';
angular.
module('myApp', ['ngMaterial']).
controller('AppCtrl', AppCtrl);
function AppCtrl($mdDialog, $scope) {
$scope.lister = [{name:'Matt'},{name:'Steve'}];
$scope.showDialog = showDialog;
function showDialog(evt) {
$scope.obj = {name:'default'};
$mdDialog.show({
targetEvent: evt,
scope: $scope.$new(),
template:
'<md-dialog>' +
' <md-content><md-input-container>'+
' <label>Name</label>'+
' <input ng-model="obj.name">' +
' </md-input-container></md-content>' +
' <div class="md-actions">' +
' <md-button ng-click="close(obj)">' +
' Save' +
' </md-button>' +
' </div>' +
'</md-dialog>'
}).then(function(objs){$scope.lister.unshift(objs)});
}
$scope.close = function(objs){
$mdDialog.hide(objs);
}
}
}());
The behavior of the above code is mdDialog will open with "default" in the Name input field, but if I change the show() parameters to below (key difference is swapping out "scope:" for "controller:"):
function showDialog(evt) {
$scope.obj = {name:'default'};
$mdDialog.show({
targetEvent: evt,
controller: 'AppCtrl',
template:
'<md-dialog>' +
' <md-content><md-input-container>'+
' <label>Name</label>'+
' <input ng-model="obj.name">' +
' </md-input-container></md-content>' +
' <div class="md-actions">' +
' <md-button ng-click="close(obj)">' +
' Save' +
' </md-button>' +
' </div>' +
'</md-dialog>'
}).then(function(objs){$scope.lister.unshift(objs)});
}
The behavior of the second implementation is that the mdDialog will open with a blank for the Name input field.
This is a long setup for this question: Why does the dialog template recognize $scope.obj when "scope: $scope.$new()", but it is not recognized when "controller: 'AppCtrl'"? I thought both implementations are providing AppCtrl's scope to the dialog.
Dialog is always given an isolated scope
You can pass data to dialog from parent controller using dependency injection.
function AppController($scope, $mdDialog) {
var message='message from parent';
$scope.showDialog = showDialog;
$scope.items = [1, 2, 3];
function showDialog($event) {
var parentEl = angular.element(document.body);
$mdDialog.show({
parent: parentEl,
targetEvent: $event,
templateUrl:'templateFile.html',
locals: {
items: $scope.items
},
message:message
controller: DialogController
});
function DialogController($scope, $mdDialog, items,message) {
$scope.items = items;
$scope.message = message;
$scope.closeDialog = function() {
$mdDialog.hide();
}
}
}
In your first case, you are adding an object to isolated scope of your dialog using
$scope.obj = {name:'default'} and its available as obj.name on yr view.
In your second case, you are declaring controller for your dialog as 'AppCtrl', but you have not defined it anywhere inside your parent controller, so you are not getting anything on view. AppCtrl is not defined.
if you want to use calling scope; you can pass 'isolateScope' parameter like this:
$mdDialog.show({
....,
isolateScope: false
});
How to retrieve the element class and display its name?
I read that I can use sth like:
var elementResult = element[0].getElementsByClassName('menu-item-active');
console.log('element[0].getElementsByClassName: ', elementResult);
JSON :
{
"title": "Fruits",
"mainmenu": [
{
"id": "apples",
"title": "Apples",
"href": "#/apples",
"act": "menu-item-active"
},
{
"id": "bananas",
"title": "Bananas",
"submenu": [
{
"id": "banana-box",
"title": "Banana Box",
"href": "#/banana-box",
"act": "menu-item-active"
}
]
}
]
}
I have a directive
.directive('dir', function () {
return {
restrict: 'E',
template: "<nav class=\"navbar navbar-inverse navbar-fixed-top\" role=\"navigation\" id=\"nav-bar\">"
+ "<div class=\"container-fluid\">"
+ "<div class=\"navbar-header\">"
+ "<span class=\"navbar-brand\" >{{title}} </span>"
+ "</div>"
+ "<ul class=\"nav navbar-nav\" ng-repeat=\"item in mainmenu\">"
+ "<li>"
+ "{{item.title}}"
+ "</li>"
+ "</ul>"
+ "</div>"
+ "</nav>"
}
});
And the controller:
.module('menu').controller('menuCtrl', ['$scope', '$http', function ($scope, $http) {
$http.get('MenuItems.json').success(function (data) {
$scope.mainmenu = data.mainmenu;
$scope.title = data.title;
});
}]);
What I want to achieve:
when I will be on the each site/subsite and I want to display its name as {{title}} in my site
I tried to use the elementResult but I don't know how to check if the element is menu-item-active. I thought about using the ng-if statement, but I don't know how to handle it.
For.ex.
ng-if="elementResult has class 'menu-item-active' then display its 'title' as {{title}} in menu bar
In dom, getElementByClassName returns NodeList.
var nodeList = element[0].getElementsByClassName('menu-item-active');
So then you have to iterate over nodes. You can use it's length property.
for ( var i = 0 ; i < nodeList.length ; i++ )
if ( nodeList[i].className == 'menu-item-active' )
var elem = $scope.findByClassName('menu-item-active');
and in your controller write scope method:
$scope.findByClassName = function(id){
for ( var i = 0 ; i < jsonArray.length ; i++ )
if ( jsonArray[i].mainmenu.act == id )
return jsonArray[i].title;
}
Also prepare proper JSON file - key value pairs and arrays of objects ( these starting with { and ending with } ).
Also, in Angular you can pass to param to a page/view, so you can use routes and $routeParam service must be dependency injected in your controller. Then for page "new/3" you ask for "$routeParams.id" and you will get 3.
ng-click is not working. Can someone help me, how to use the $compile?
I have created a plunker http://plnkr.co/edit/AhxGYnOsJ7rPqcQquMfq?p=preview
// main.js
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function($scope) {
})
.directive('myDir', function($compile){
return {
restrict: 'CAE',
link: function(scope, element, attrs){
var dropdown = '';
dropdown += '<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="'+ attrs.id +'">';
dropdown += '<li role="presentation">Actions</li>';
dropdown += '</ul>';
//dropdown = $compile(dropdown)(scope);
element.after(dropdown);
}
}
});
I would pull out the click function into your controller. There is no alert on your controller's scope so it does nothing. Also, I try to avoid lots of nested quotes if possible. A simple and clean refactor.
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function($scope) {
$scope.actionClick = function() {
alert("a");
}
})
.directive('myDir', function($compile){
return {
restrict: 'CAE',
link: function(scope, element, attrs){
var dropdown = '';
dropdown += '<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="'+ attrs.id +'">';
dropdown += '<li role="presentation">Actions</li>';
dropdown += '</ul>';
dropdown = $compile(dropdown)(scope);
element.after(dropdown);
}
}
});
see plunkr
You were very close. Compile template with $compile:
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function($scope) {
$scope.doSomething = function(){
alert('d');
};
})
.directive('myDir', function($compile){
return {
restrict: 'CAE',
link: function(scope, element, attrs){
var dropdown = '';
dropdown += '<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="'+ attrs.id +'">';
dropdown += '<li role="presentation">Actions</li>';
dropdown += '</ul>';
var a_input = angular.element($compile(dropdown)(scope));
element.append(a_input);
}
}
});
Demo Plunker
comment
AngularJS doesn't know about alert. If you want to call it from HTML, override it like #udidu showed.
When you're using ng-click the ng-click directive searche for the expression in the current scope. Since their is no alert function on the directive's scope (or it's parent scope) then nothing happen.
You can add the alert function on your scope like so:
.directive('myDir', function($compile){
return {
restrict: 'CAE',
link: function(scope, element, attrs){
// add the alert function on the scope
scope.alert = function(param) {
alert(param);
}
var dropdown = '';
dropdown += '<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="'+ attrs.id +'">';
dropdown += '<li role="presentation">Actions</li>';
dropdown += '</ul>';
dropdown = $compile(dropdown)(scope);
element.after(dropdown);
}
}
});
I stumbled upon this article on how to build a click to edit feature for a form. The author states:
What about if you wanted input type="date" or even a select? This
is where you could add some extra attribute names to the directive’s
scope, like fieldType, and then change some elements in the template
based on that value. Or for full customisation, you could even turn
off replace: true and add a compile function that wraps the necessary
click to edit markup around any existing content in the page.
While looking through the code I cannot seem to wrap my head around how I could manipulate the template in such a way that I could make it apply to any angular component, let alone how I can make it apply to a drop down list. Code from article below:
app.directive("clickToEdit", function() {
var editorTemplate = '<div class="click-to-edit">' +
'<div ng-hide="view.editorEnabled">' +
'{{value}} ' +
'<a ng-click="enableEditor()">Edit</a>' +
'</div>' +
'<div ng-show="view.editorEnabled">' +
'<input ng-model="view.editableValue">' +
'Save' +
' or ' +
'<a ng-click="disableEditor()">cancel</a>.' +
'</div>' +
'</div>';
return {
restrict: "A",
replace: true,
template: editorTemplate,
scope: {
value: "=clickToEdit",
},
controller: function($scope) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editorEnabled = true;
$scope.view.editableValue = $scope.value;
};
$scope.disableEditor = function() {
$scope.view.editorEnabled = false;
};
$scope.save = function() {
$scope.value = $scope.view.editableValue;
$scope.disableEditor();
};
}
};
});
My question is, how can we extend the above code to allow for drop down edits? That is being able to change to the values that get selected.
One approach you might consider is using template: function(tElement,tAttrs ).
This would allow you to return appropriate template based on attributes.
app.directive("clickToEdit", function() {
return {
/* pseudo example*/
template: function(tElement,tAttrs ){
switch( tAttrs.type){
case 'text':
return '<input type="text"/>';
break;
}
},....
This is outlined in the $compile docs