AngularJS Directive - compile is not working - javascript

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);
}
}
});

Related

jQuery doesn't select the element with dynamic ID, how to do that?

I am having a problem when I pass the ID through a directive. I can't obtain the element using jQuery inside the Link function, and the element is using the correct dynamic ID coming as parameter:
The Directive:
(function(angular) {
var app = angular.module('pi.core');
app.directive('piSearch', function() {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
idelement: '#'
},
link: function(scope, element, attrs, controller, transcludeFn) {
var idelement = scope.idelement;
console.log('idElement: ' + idelement);
console.log($('#' + idelement + ' .typeahead'));
},
template: '<div id="{{idelement}}"></div>'
};
});
})(angular);
var myApp = angular.module('piCore', []);
myApp.directive("piSearch", function() {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
idelement: '#'
},
link: function(scope, element, attrs, controller, transcludeFn) {
var idelement = scope.idelement;
scope.elementSelected = $('#' + idelement + ' .typeahead');
console.log('idElement: ' + idelement);
console.log($('#' + idelement + ' .typeahead'));
},
template: '<div id="{{idelement}}"></div>'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body ng-app="piCore">
<pi-search></pi-search>
{{$scope.elementSelected}}
</body>
Any hints? Thanks in advance for your help!
I'll refactor your link function, remember angular has their own life cycle, and you need to make sure that your template compiles when your model has a value, wrap your logic in a watch
app.directive('piSearch', function() {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
idelement: '#'
},
link: function(scope, element, attrs, controller, transcludeFn) {
var idelement = scope.idelement;
scope.$watch('idelement',function(){
scope.elementSelected = $('#' + idelement + ' .typeahead');
console.log('idElement: ' + idelement);
console.log($('#' + idelement + ' .typeahead'));
});
},
template: '<div id="{{idelement}}"></div>'
};
});
try to use angular.element("#"+ idelement);
and also make sure this template: '<div id="{{idelement}}"></div>' is not generated multiple times
One thing you must know regarding dynamic ID's and elements in global:
1) First you must generate your dynamic HTML inside javascript
2) Print it, append or just insert somwhere in the DOM
3) Now to can access to that new element you must use document to read that new element like this:
document.getElementById('new-generic-id');
or
$(document).on('click touchstart select', '#new-generic-id', function(){});
I hope you understand.

ng-click in directive not calling function defined in controller scope

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>

Close multiselect dropdown when user clicks outside (different situation) [duplicate]

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

scope variable isn't reflected in directive

I'm trying to fill the body attribute, so my module will print it as it's body part, yet I have no idea why it's not working, even I tried $scope.$apply() in several places.
var app = angular.module('ClientLogger', ['ngRoute'])
app.controller('global', function($scope, $compile) {
$scope.window = window
$scope.sampletext = "sampleText";
$scope.showModal = false;
$scope.toggleModal = function(text) {
$scope.text = text;
$scope.showModal = !$scope.showModal;
};
});
app.directive('modal', function() {
return {
template: '<div class="modal fade">' +
'<div class="modal-dialog">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>' +
'<h4 class="modal-title">{{ title }}</h4>' +
'</div>' +
'<div class="modal-body" ng-transclude> {{ body }} </div>' +
'</div>' +
'</div>' +
'</div>',
restrict: 'E',
transclude: true,
replace: true,
scope: true,
link: function postLink(scope, element, attrs) {
scope.title = attrs.title;
scope.body = attrs.body;
scope.$watch(attrs.visible, function(value) {
if (value == true)
$(element).modal('show');
else
$(element).modal('hide');
});
$(element).on('shown.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = true;
});
});
$(element).on('hidden.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = false;
});
});
}
};
});
<body ng-app='ClientLogger' ng-controller='global'>
<a class="btn btn-default" ng-click="toggleModal(sampletext)"> sample </a>
<modal title="Message Details" body="{{text}}" visible="showModal"></modal>
</body>
As you can see, after i click the link, it will change the variable $scope.text and it will reflect to the modal. But I can't manage to do it. Since I#m very new about this Angular, I still have troubles about understanding its mechanics, so any specific details will be really good for me.
Any recommendations?
When you retrieve your attrs.body, you are retrieving undefined data.
You can use a Factory to share your data. You have to know that all angular services are singletons, so there is only one instance of a given service.
Thanks to this, you can easily share your data between your controller and your directive.
So when you will trigger an action in your Controller, you will set your data into your factory, then you can retrieve your data into your directive.
Controller
(function(){
function Controller($scope, Service) {
$scope.myText = 'sample';
$scope.showModal = false;
$scope.toggleModal = function() {
//Set the myText value by using our Service.set() method
Service.set($scope.myText);
$scope.showModal = !$scope.showModal;
};
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
Service
(function(){
function Service(){
var data;
function set(value){
data = value;
}
function get(){
return data;
}
return {
get: get,
set: set
};
}
angular
.module('app')
.factory('Service', Service);
})();
Directive
(function(){
function modal(Service) {
return {
template: '<div class="modal fade">' +
'<div class="modal-dialog">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>' +
'<h4 class="modal-title">{{ title }}</h4>' +
'</div>' +
'<div class="modal-body"> {{ body }} </div>' +
'</div>' +
'</div>' +
'</div>',
restrict: 'E',
transclude: true,
replace: true,
scope: true,
link: function (scope, element, attrs) {
scope.title = attrs.title;
scope.$watch(attrs.visible, function(value) {
value
//Retrieve your data by using Service.get() method, and show our modal
? ( scope.body = Service.get(), $(element).modal('show') )
: $(element).modal('hide')
});
$(element).on('shown.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = true;
});
});
$(element).on('hidden.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = false;
});
});
}
};
}
angular
.module('app')
.directive('modal', modal);
})();
Then, you can call your directive into your HTML.
HTML
<body ng-app='app' ng-controller="ctrl">
<input type="text" ng-model="myText">
<a class="btn btn-default" ng-click="toggleModal()"> sample </a>
<modal title="Message Details" visible="showModal"></modal>
</body>
You can see the Working Plunker

Is there any way to just use a child directive as a placeholder for holding content?

I am new to angularjs. Want to see if there is any way to just use a child directive as a placeholder for holding content but for really rendering?
I don't want to do rendering in the child directive because I want to let the parent to do everything. So I can have some other special logic in the parent directive.
angular.module('components', []).
directive('tabs', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: function($scope, $element) {
var panes = $scope.panes = [];
$scope.select = function(pane) {
angular.forEach(panes, function(pane) {
pane.selected = false;
});
pane.selected = true;
}
$scope.createPane = function() {
var pane = panes[panes.length - 1];
var clonedPane = Angular.copy(pane);
panes.push(clonedPane);
}
this.addPane = function(pane) {
if (panes.length == 0) $scope.select(pane);
panes.push(pane);
}
},
template: '<div class="tabbable tabs-left">' +
'<ul class="nav nav-tabs">' +
'<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">' +
'{{pane.title}}' +
'</li>' +
'<li ng-class="addLink"><a ng-click="createPane()"><i class="icon-plus"></i> tab</a></li>' +
'</ul>' +
'<div class="tab-content">' +
'<div ng-repeat="pane in panes" class="tab-pane" ng-class="{active: selected},{{pane.pclass}}" id="{{pane.id}}">' +
'{{ pane.content }}' +
'</div>' +
'</div>' +
'</div>',
replace: true
};
}).
directive('pane', function() {
return {
require: '^tabs',
restrict: 'E',
scope: {
title: 'bind',
pclass: 'bind',
id: 'bind'
},
link: function(scope, element, attrs, tabsCtrl) {
var text = element.text();
tabsCtrl.addPane({
title: scope.title,
pclass: scope.pclass,
id: scope.id,
content: text
});
}
};
})
Corresponding html code:
<tabs>
<pane title="tab 1" id="tab1" pclass="tab">
hello
</pane>
<pane title="tab 2" id="tab2" pclass="tab">
world
</pane>
</tabs>
I tried something like the above but nothing is pushed into panes. Seems like the link function of the child directive (pane) is never called. So as a result, only the link for adding tab is displayed.
Any ideas?
The easiest (though sort of annoying) way to approach this would be to have a template for each pane.
In html:
<tabs>
<pane title="tab 1" id="tab1" pclass="tab" template="pane1-templ">
</pane>
<pane title="tab 2" id="tab2" pclass="tab" template="pane2-templ">
</pane>
</tabs>
<script type="text/ng-template" src="pane1-template">hello</script>
<script type="text/ng-template" src="pane2-template>wolrd</script>
In your directive:
'<div class="tab-content">' +
'<div ng-repeat="pane in panes" class="tab-pane" ng-class="{active: selected},{{pane.pclass}}" id="{{pane.id}}">' +
'<div ng-include src="pane.template"></div>' +
'</div>' +
'</div>'
Or.. have you tried doing this to fix it?
In pane:
var html = elm.html();
//...
pane.html = html;
In tabs:
<div ng-bind-html-unsafe="pane.html"></div>
Perhaps the reason why it is not working is because in your child directive you have no template. Without a template, the directive does not know where and how to render the directive.
What you could do is have something like:
template : '<div style="display:none;" ng-transclude></div>'
in your child template..
and your element.text() should still work!
Without a template the linking function would never get the element so your child directive will fail right there.
With the template above it should work.

Categories

Resources