I am learning AngularJs and was trying to run a custom directive. I have a button and on click of the button, I need to handle to event inside my controller. I am not getting the function call on ng-click directive. I am attaching the plnkr link: Link to plnkr
// Code goes here
angular.module("app", []);
angular.module('app').controller('mainCtrl', function($scope){
$scope.developer={
name: "Pradeep Kumar L",
age: 32,
city: "Bengaluru",
friends:[
"Praveen",
"Kori",
"Kiran"
]
}
$scope.handleClick = function(){
developer.rank="friend";
console.log("button clicked..");
}
});
angular.module('app').directive('mySimpleDirective', function(){
return{
restrict: "E",
templateUrl: 'userInfoCard.html'
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="panel panel-primary">
<div class="panel-heading">
<h4>{{developer.name}}</h4>
</div>
<div class="panel-body">
<span ng-show='!!developer.age'><h4>User age: {{developer.age}}</h4></span>
<h4>User city: {{developer.city}}</h4>
<h4>Friends</h4>
<ul>
<li ng-repeat="friend in developer.friends">
{{friend}}
</li>
</ul>
<div ng-show="!developer.rank">
Rank: {{developer.rank}}
</div>
<div ng-show="!developer.rank">
<button class="btn btn-success" ng-click="handleClick(developer)">Click Me</button>
</div>
</div>
</div>
The function is triggered but you have a syntax error inside it :
$scope.handleClick = function(){
$scope.developer.rank="friend";
console.log("button clicked..");
};
or
Usually, people use the following way when they have to deal with a list of developers, and the function would have been inside a ng-repeat.
$scope.handleClick = function(developer){ //as far as you pass it as argument
developer.rank="friend";
console.log("button clicked..");
};
http://plnkr.co/edit/vsRr9CfXxK0HQ7XS5GFe?p=preview
if you switch on console in your plunker you will see an error:
ReferenceError: developer is not defined
at Scope.$scope.handleClick (script.js:18)
at $parseFunctionCall (angular.js:12456)
at callback (angular.js:21692)
at Scope.$eval (angular.js:14555)
at Scope.$apply (angular.js:14654)
at HTMLButtonElement.<anonymous> (angular.js:21697)
at HTMLButtonElement.n.event.dispatch (jquery-2.1.4.min.js:3)
at HTMLButtonElement.r.handle (jquery-2.1.4.min.js:3)
you should add or argument on function or $scope to line script.js:18
When clicking the button, you are passing in developer to the handleClick function:
<button class="btn btn-success" ng-click="handleClick(developer)">Click Me</button>
However, in your javascript, your handleClick function does not have developer as an argument:
$scope.handleClick = function () { // developer is not passed in...
developer.rank="friend"; // ...so it will be undefined here
console.log("button clicked..");
}
To fix this, your function should be:
$scope.handleClick = function (developer) { // developer can be passed in...
developer.rank="friend"; // ...so this WILL work!!
console.log("button clicked..");
}
Related
I need to display a small popover which should open on click and goaway on clicking anywhere on the page.
I found a plunker (http://plnkr.co/edit/K7cYQSDEBS3cHvDfJNLI?p=preview) which matches this requirement however, unable to get it to work inside ng-repeat.
I saw several answers and Plunker examples but not able to get this to work.
Here is my html
<div ng-controller="TestController">
<div class="row" style="background-color: #ebebeb !Important; ">
<div style="text-align:center">
<table style="width:100% !important;">
<tr ng-repeat="member in TeamMembers" style="font-size:18px !important; height: 108px;">
<td style="display:block;margin-top:30px;text-align:left;"> {{member.FirstName}} {{member.LastName}} <i class="fa fa-info-circle" aria-hidden="true" ng-show="member.Description != null" popover-template="dynamicPopover.templateUrl" popover-placement="bottom" popover-elem descr="{{member.Description}}"></i></td>
</tr>
</table>
</div>
</div>
...
<script type="text/ng-template" id="descriptionModal.html">
<div class="adp-info-dialog">
<div class="modal-body">
<div class="row">
<div class="col-md-8 col-md-offset-1">
<div class="form-group">
<label class="fieldset-label">Test {{ dynamicPopover.descr }}</label>
</div>
</div>
</div>
</div>
</div>
</script>
Here is the JS
testApp.controller('TestController', function ($scope, $rootScope, $log, $modal, SiebelAccountTeamService, $filter, $element) {
$scope.dynamicPopover = {
templateUrl: 'descriptionModal.html',
descr: null
};
var result = TestService.GetTeamMembers();
result.then(function (data) {
$scope.TeamMembers = data.data;
}, function (e) {
console.log(e);
}).finally(function () {
$scope.CompleteLoading();
});
});
testApp.directive('popoverClose', function ($timeout) {
return {
scope: {
excludeClass: '#'
},
link: function (scope, element, attrs) {
var trigger = document.getElementsByClassName('trigger');
function closeTrigger(i) {
$timeout(function () {
angular.element(trigger[0]).triggerHandler('click').removeClass('trigger');
});
}
element.on('click', function (event) {
var etarget = angular.element(event.target);
var tlength = trigger.length;
if (!etarget.hasClass('trigger') && !etarget.hasClass(scope.excludeClass)) {
for (var i = 0; i < tlength; i++) {
closeTrigger(i)
}
}
});
}
};
});
testApp.directive('popoverElem', function () {
return {
scope: {
descr: '#'
},
link: function (scope, element, attrs) {
$scope.dynamicPopover.descr = scope.descr,
alert($scope.dynamicPopover.descr),
element.on('click', function () {
element.addClass('trigger');
});
}
};
});
Appreciate your help.
Update:
To show the data of the ng-repeat inside the popover content, we need to access the individual objects through the $index of the ng-repeat. Refer the below example.
Plunkr Demo
The problem here is that you are using ng-repeat which creates a new scope read more here.
Since replicating the issue with your code is tedious, I tried replicating the issue with the plunkr!
Solution:
Plunkr Demo
You can simply define a new controller inside the descriptionModal.html like so
HTML:
<script type="text/ng-template" id="myPopoverTemplate.html">
<div class="adp-info-dialog" ng-controller="tester">
<div class="modal-body">
<div class="row">
<div class="col-md-8 col-md-offset-1">
<div class="form-group">
<label class="fieldset-label">Test {{ $parent.$parent.dynamicPopover.content }}</label>
</div>
</div>
</div>
</div>
</div>
</script>
JS:
app.controller('tester', function ($rootScope, $scope) {
console.log($scope.$parent.$parent.dynamicPopover.title);
});
Then, we will be able to access the parent scope, using $parent, the html inside the above script uses the $parent to get the variable!
Please note: It took me two $parent to reach the required $scope to access the scope variable. In your scenario it will also require two, the way to check how many is needed is use console.log($scope), then open the console(F12), then traverse through the objects $parent property till you find the correct $scope. Then count the number of $parent traversed, that will be your required number of $parent to traverse!
P.S:
There is another method you can do this, since this method will require a significant rewrite of your code, I will provide the GIST, you can use the controller as syntax and access the correct scope.
Here is the SO Answer giving the method to do it
SO Answer
I hope this fixes you issue.
I have html which looks like one below, I have 2x ng-click in whole code in both situation I call same function. Both functions are in same controller.
<div class="tagselect tagselect--frameless">
<div class="combobox__body combobox__body--open combobox__body--frameless" ng-show="focus">
<ul class="list-unstyled">
<li class="combobox__item" ng-repeat="pos in listCtrl.positions | filter:query as results"
ng-click="listCtrl.choosePosition(pos)">{{pos.name}}
</li>
</ul>
</div>
</div>
<div class="col-md-2 no-padding">
<button type="button" class="btn btn-success" ng-click="listCtrl.chosenPositions(789456)">Add</button>
</div>
controller looks like:
myApp.controller('ListCtrl', ['$scope', '$cookies', '$http', function ($scope, $cookies, $http) {
var listCtrl = {
candidates: [],
positions: [],
chosenPositions: [],
init: function () {
listCtrl.getCandidates();
listCtrl.getPositions();
},
getCandidates: function () {
$http.get('candidates.json').then(function (res) {
listCtrl.candidates = res.data;
});
},
getPositions: function () {
$http.get('positions.json').then(function (res) {
listCtrl.positions = res.data;
});
},
choosePosition: function (position) {
console.log(position);
}
};
listCtrl.init();
$scope.listCtrl = listCtrl;
}]);
I double check for missspells and make sure its not because of function (I create a new one with simple console log).
Problem is that button click correctly call function but ng-repeat <li ng-click=""> doesnt do anything. I read in angular documentation that ng-repeat create new scope but this should be still okey in my opinion as soon as I use reference to object listCtrlchoosePosition()
Can someone tell me what I am doing wrong?
Thanks
EDIT: Plunker example:
http://plnkr.co/edit/ooUQA2n1Vyj8RZtsQ1Pj?p=preview
ng-blur is doing something weird, so I'm going to suggest you to change the $scope.focus value from the ListCtrl instead of using the ng-blur.
html file
<!-- more html code -->
<!-- input without ng-blur directive -->
<input class="tagselect__input" placeholder="Position" ng-focus="focus=true" ng-model="query">
<!-- more html code -->
<li class="combobox__item" ng-repeat="pos in listCtrl.positions | filter:query as results" ng-click="listCtrl.choosePosition(pos)">{{pos.name}}
<!-- more html code -->
js file
// more code goes here.
choosePosition: function (position) {
//alert('Going to choosen position');
//$scope.query = position.name;
$scope.focus = false; // Hide div options from here.
// rest of your code.
},
// more code goes here.
Working in this plunkr
I am working on a modal for a client. I have created a directive that works great, the problem is that one modal is made ahead each time it is used like..
What I have
<div ng-repeat="item in items">
<a data-toggle="modal" data-target="{{item.id}}">Click</a>
<my-dialog element-id="item.id">
<h1>This is the body of the modal</h1>
</my-dialog>
</div>
This works great for a small amount of modals but we are using a very large number of modals. So I would like to add the directive at runtime, something closer to...
What I want...
<div id="warning"></div>
<div ng-repeat="item in items">
<a data-toggle="modal" data-target="{{item.id}}" ng-click="showModal(item)">Click</a>
</div>
...
// inside controller
$scope.showModal = function(item){
$http.get('/someUrl').success(function(data){
var result = $compile('<my-dialog element-id="'+item.id+'">'+data+'</my-dialog>').($scope);
$("#warning").append(result);
});
}
$scope.hideModal = function(){
$( "#warning" ).empty();
}
This of course isn't working yet. Also it doesn't feel like the best way. This would allow me to remove the directive once it has been closed.
Please include a plunker or equivalent for the check.
One way you could do this is to use ng-repeat with your items, then call $scope.$apply() after you push a new item to the list. The HTML could look like this ...
<div ng-repeat="item in items">
<span dialog>
<a class="dialog-anchor">{{item.name}}</a>
<div class="dialog-body">{{item.id}}</div>
</span>
</div>
... and the directive like this
.directive('dialog', [function () {
return {
scope: {
id: '#elementId',
}
, link: function (scope, el, attrs) {
var body = $(el).find('.dialog-body').detach();
$(el).find('.dialog-anchor').on('click', function () {
$('body').append(body);
});
}};
}])
... and the controller like this
.controller('app', ['$scope', function ($scope) {
$scope.items = [
{name: 'first', id: 001},
{name: 'second', id: 002}
];
setTimeout(function () {
$scope.items.push({name: 'three', id: 003});
if (!$scope.$$phase) $scope.$apply();
}, 2000);
}])
Here's the plunker... http://plnkr.co/edit/2ETbeCKGcHW3CJCfD9d7?p=preview. You can see the $scope.$apply call in the setTimeout where I push a new item to the array.
Try this:
var result = $compile('<my-dialog element-id="'+item.id+'">'+data+'</my-dialog>')($scope);
i have a directive, and in its link function i want to access methods from a service. My code for directive is
AppDirectives.directive('feed',['FeedService',function() {
return {
restrict : 'AE',
scope : {
feedLike: '&',
feedItem : '=',
feedDislike :'&',
feedsArray :'=',
},
templateUrl :'resources/views/templates/feedTemplate.html',
link : function(scope,element,feedService){
console.debug("linking now");
scope.likeComment = function(commentUid){
console.debug("comment liked :"+commentUid);
};
scope.addComment = function(referenceFeedUid){
console.debug("commentText : "+scope.commentText);
var comment = {
user : "guest",
feedText : scope.commentText
};
feedService.addComment(comment,referenceFeedUid).then(function(response){
console.debug("response ; "+response);
// $scope.feeds.unshift(response);
});
};
},
replace : true,
};
}]);
and my service code is
.factory('FeedService',function($http){
return {
postFeed : function (feed){
/*$http.post('/feed/add',feed).success(function(response){
console.debug("added "+response);
}).error(function(){
console.debug("error adding feed");
});*/
return $http.post('/feed/add',feed).then(function(response){
return response.data;
});
},
getFeeds : function(){
return $http.get('/feed/get');
},
likeFeed : function(feedUid){
return $http.get('/feed/'.concat(feedUid).concat('/like')).then(function(response){
return response.data;
});
},
dislikeFeed : function(feedUid){
return $http.get('/feed/'.concat(feedUid).concat('/dislike')).then(function(response){
return response.data;
});
},
addComment : function (comment,referenceUid){
var targetUrl = '/feed/'.concat(referenceUid).concat('/comment');
return $http.post(targetUrl,comment).then(function(response){
return response.data;
});
},
};
});
when i call the add comment from directive's link, i am getting following error on firebug console.
TypeError: Object #<Object> has no method 'addComment'
at Object.scope.addComment (http://localhost:8080/feed/resources/js/directives.js:53:21)
at http://localhost:8080/feed/resources/js/lib/angular/angular.js:6193:19
at http://localhost:8080/feed/resources/js/lib/angular/angular.js:12684:13
at Object.Scope.$eval (http://localhost:8080/feed/resources/js/lib/angular/angular.js:7840:28)
at Object.Scope.$apply (http://localhost:8080/feed/resources/js/lib/angular/angular.js:7920:23)
at HTMLButtonElement.<anonymous> (http://localhost:8080/feed/resources/js/lib/angular/angular.js:12683:17)
at http://localhost:8080/feed/resources/js/lib/angular/angular.js:1926:10
at Array.forEach (native)
at forEach (http://localhost:8080/feed/resources/js/lib/angular/angular.js:110:11)
at HTMLButtonElement.eventHandler (http://localhost:8080/feed/resources/js/lib/angular/angular.js:1925:5)
here is my directive template
<ul class="media-list">
<li class="media">
<a class="pull-left" href="#"><img class="media-object" src="resources/images/holder.png" style="height:64px; width:64px;" alt="img"></a>
<div class="media-body">
<span><h4 class="media-heading">{{feedItem.userUid}}</h4>{{ feedItem.time | date:medium }}</span>
<h5>{{feedItem.feedText}}</h5><h3></h3>
<p> <a ng-click="feedLike(feedItem.feedLike)">Like </a> {{feedItem.like}}
<a ng-click="feedDislike(feedItem.feeddisLike)">Dislike</a> {{feedItem.dislike}}
</p>
<div ng-repeat = "comment in (feedsArray | filter:{referenceGroupId:feedItem.uid})">
<div class="media">
<a class="pull-left" href="#"><img class="media-object" src="resources/images/holder.png" style="height:64px; width:64px;" alt="img"></a>
<div class="media-body">
<span><h4 class="media-heading">{{comment.userUid}}</h4>{{ comment.time | date:medium }}</span>
<h5>{{comment.feedText}}</h5><h3></h3>
<p> <a ng-click="likeComment(comment.uid)">Like </a> {{comment.like}}
<a ng-click="commentDislike(comment.uid)">Dislike</a>{{comment.dislike}}
</p>
</div>
</div><br/>
</div>
<div>
<input type="text" id="commentBox" ng-model="commentText"/>
<button class="btn btn-success" ng-click="addComment(feedUid)">Comment</button>
</div>
</div>
</li>
</ul>
what i am trying to do is that i want to access the addCommennt method from service. how can i fix it or is there any way that i can acess the service methods from inside the directive link function.
Thanks in advance.
regards,
Instead of declaring the service in the link function, declare it when you are defining the directive:
So here:
AppDirectives.directive('feed',['FeedService',function(feedService) {
Then the feedService will be available to call inside the link function. The link function parameters are specifically defined as scope, element, attrs, ctrl so there is no straight dependency injection happening there (AFAIK).
Using the example mentioned here, how can I invoke the modal window using JavaScript instead of clicking a button?
I am new to AngularJS and tried searching the documentation here and here without luck.
Thanks
OK, so first of all the http://angular-ui.github.io/bootstrap/ has a <modal> directive and the $dialog service and both of those can be used to open modal windows.
The difference is that with the <modal> directive content of a modal is embedded in a hosting template (one that triggers modal window opening). The $dialog service is far more flexible and allow you to load modal's content from a separate file as well as trigger modal windows from any place in AngularJS code (this being a controller, a service or another directive).
Not sure what you mean exactly by "using JavaScript code" but assuming that you mean any place in AngularJS code the $dialog service is probably a way to go.
It is very easy to use and in its simplest form you could just write:
$dialog.dialog({}).open('modalContent.html');
To illustrate that it can be really triggered by any JavaScript code here is a version that triggers modal with a timer, 3 seconds after a controller was instantiated:
function DialogDemoCtrl($scope, $timeout, $dialog){
$timeout(function(){
$dialog.dialog({}).open('modalContent.html');
}, 3000);
}
This can be seen in action in this plunk: http://plnkr.co/edit/u9HHaRlHnko492WDtmRU?p=preview
Finally, here is the full reference documentation to the $dialog service described here:
https://github.com/angular-ui/bootstrap/blob/master/src/dialog/README.md
To make angular ui $modal work with bootstrap 3 you need to overwrite the styles
.modal {
display: block;
}
.modal-body:before,
.modal-body:after {
display: table;
content: " ";
}
.modal-header:before,
.modal-header:after {
display: table;
content: " ";
}
(The last ones are necessary if you use custom directives) and encapsulate the html with
<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">Modal title</h4>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
Open modal windows with passing data to dialog
In case if someone interests to pass data to dialog:
app.controller('ModalCtrl', function($scope, $modal) {
$scope.name = 'theNameHasBeenPassed';
$scope.showModal = function() {
$scope.opts = {
backdrop: true,
backdropClick: true,
dialogFade: false,
keyboard: true,
templateUrl : 'modalContent.html',
controller : ModalInstanceCtrl,
resolve: {} // empty storage
};
$scope.opts.resolve.item = function() {
return angular.copy(
{name: $scope.name}
); // pass name to resolve storage
}
var modalInstance = $modal.open($scope.opts);
modalInstance.result.then(function(){
//on ok button press
},function(){
//on cancel button press
console.log("Modal Closed");
});
};
})
var ModalInstanceCtrl = function($scope, $modalInstance, $modal, item) {
$scope.item = item;
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
Demo Plunker
The AngularJS Bootstrap website hasn't been updated with the latest documentation. About 3 months ago pkozlowski-opensource authored a change to separate out $modal from $dialog commit is below:
https://github.com/angular-ui/bootstrap/commit/d7a48523e437b0a94615350a59be1588dbdd86bd
In that commit he added new documentation for $modal, which can be found below:
https://github.com/angular-ui/bootstrap/blob/d7a48523e437b0a94615350a59be1588dbdd86bd/src/modal/docs/readme.md.
Hope this helps!
Quick and Dirty Way!
It's not a good way, but for me it seems the most simplest.
Add an anchor tag which contains the modal data-target and data-toggle, have an id associated with it. (Can be added mostly anywhere in the html view)
Now,
Inside the angular controller, from where you want to trigger the modal just use
angular.element('#myModalShower').trigger('click');
This will mimic a click to the button based on the angular code and the modal will appear.
Different version similar to the one offered by Maxim Shoustin
I liked the answer but the part that bothered me was the use of <script id="..."> as a container for the modal's template.
I wanted to place the modal's template in a hidden <div> and bind the inner html with a scope variable called modal_html_template
mainly because i think it more correct (and more comfortable to process in WebStorm/PyCharm) to place the template's html inside a <div> instead of <script id="...">
this variable will be used when calling $modal({... 'template': $scope.modal_html_template, ...})
in order to bind the inner html, i created inner-html-bind which is a simple directive
check out the example plunker
<div ng-controller="ModalDemoCtrl">
<div inner-html-bind inner-html="modal_html_template" class="hidden">
<div class="modal-header">
<h3>I'm a modal!</h3>
</div>
<div class="modal-body">
<ul>
<li ng-repeat="item in items">
<a ng-click="selected.item = item">{{ item }}</a>
</li>
</ul>
Selected: <b>{{ selected.item }}</b>
</div>
<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>
</div>
<button class="btn" ng-click="open()">Open me!</button>
<div ng-show="selected">Selection from a modal: {{ selected }}</div>
</div>
inner-html-bind directive:
app.directive('innerHtmlBind', function() {
return {
restrict: 'A',
scope: {
inner_html: '=innerHtml'
},
link: function(scope, element, attrs) {
scope.inner_html = element.html();
}
}
});