ng-click not working in bootstrap popover content - javascript

I want to show a clickable table in my popover, and call a function when one of the rows get clicked. My html looks like this:
<a popover id="showDays"
type="button"
class="btn btn-success btn-xs pull-left"
data-toggle="popover"
data-placement="right"
data-html="true"
title="Popover title"
data-content=
'<table class="table table-condensed">
<tbody>
<tr ng-repeat="d in days" ng-click="show(111)">
<td ng-bind="d"></td>
<td ng-bind="x"></td>
</tr>
</tbody>
</table>'>
<i class="fa fa-clock-o fa-lg">Click me</i>
</a>
And my script.js:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.days = [
'Sunday',
'Monday',
];
$scope.show = function(x) {
console.log("show called with " + x);
$scope.x = x;
}
}).directive('popover', function($compile, $timeout){
return {
restrict: 'A',
link:function(scope, el, attrs){
var content = attrs.content;
var elm = angular.element('<div />');
elm.append(attrs.content);
$compile(elm)(scope);
$timeout(function() {
el.removeAttr('popover').attr('data-content',elm.html());
el.popover();
});
}
}
});
Demo here
The code was copied from this question, the answer itself works fine, but my show function is not called. Any idea why?

I've found the problem, for some reason the directive failed to bind function show with scope, but succeeded with days, I have experimented with a few things, if I change the way the link function is written, ng-click will work, but not ng-repeat, which means it had failed to bind days in that case.
The updated DEMO uses templateUrl instead of the data-content attribute
<script type="text/ng-template" id="popover-content">
<table class="table table-condensed">
<tbody>
<tr ng-repeat="d in days" role="button" ng-click="show(111)">
<td ng-bind="d"></td>
<td ng-bind="x"></td>
</tr>
</tbody>
</table>
</script>
now both ng-repeat and ng-click work fine for me.

Related

Passing ng-click value to one controller and use it in another controller

I am using a table and a button inside the table pops up a modal. I want to pass the Id value in that row to the modal controller so that I can use it to pass it to the the rest api call and then subsequently load valus in the modal table.
App.js
var app = angular.module("UiApp", ["ServiceApp"]);
app.service('sharedProperties', function () {
var idValue = 'test string value';
return {
getId: function () {
return idValue;
},
setId: function (value) {
idValue = value;
}
}
});
app.controller("PortFolioController", function ($scope, GetPortfolios,sharedProperties) {
$scope.Portfolios = GetPortfolios.query({ pmid: 2 });
console.log($scope.Portfolios);
$scope.addOrder = function (id) {
sharedProperties.setId(id)
};
});
app.controller("OrderController", function ($scope, GetOrders,sharedProperties) {
$scope.item = sharedProperties.getId();
$scope.Orders = GetOrders.query({ id: item});
});
Service.js
var app = angular.module("ServiceApp", ["ngResource"]);
app.factory('GetPortfolios', function ($resource) {
return $resource("http://localhost:61347/api/PortfolioManager/GetPortfolios/");
});
app.factory('GetOrders', function ($resource) {
return $resource("http://localhost:61347/api/PortfolioManager/GetPortfolioOrders/");
});
HTML
<div >
<table class="table table-striped table-hover table-bordered" id="editable-sample" ng-controller="PortFolioController">
<thead>
<tr>
<th>Portfolio ID</th>
<th>Portfolio Name</th>
<th>Portfolio Type</th>
<th>Portfolio Description</th>
<th>Show Stocks</th>
</tr>
</thead>
<tbody>
<tr class="" ng-repeat="portfolio in Portfolios">
<td>{{portfolio.portfolioId}}</td>
<td>{{portfolio.portfolioName}}</td>
<td>{{portfolio.type}}</td>
<td>{{portfolio.description}}</td>
<td> <button type="button" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#myModal" ng-click="addOrder(portfolio.portfolioId)" >Show <i class="fa fa-info-circle"></i></button></td>
</tr>
</tbody>
</table>
</div>
</div>
<!--Modal start-->
<div class="modal fade" id="myModal" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">My Portfolio</h4>
</div>
<div class="modal-body">
<h3>Stock List</h3>
<div class="space15"></div>
<table class="table table-striped table-hover table-bordered" id="editable-sample" ng-controller="OrderController">
<thead>
<tr>
<th>Symbol</th>
<th>Stock Name</th>
<th>Quantity</th>
<th>Market Price</th>
</tr>
</thead>
<tbody>
<tr class="" ng-repeat="order in Orders">
<td>{{order.symbol}}</td>
<td>{{order.executedQuantity}}</td>
<td>{{order.price}}</td>
<td>{{order.createTStamp}}</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">Close <i class="fa fa-times"></i></button>
</div>
In PortFolioController Controller, you can do:
$rootScope.$broadcast('eventName', id);
and listen to the event in OrderController Controller:
$scope.$on('eventName', function (event, id) {...});
Also you can use AngularJS Service Passing Data Between Controllers to see some examples
or the simpleset way without any service is to use $rootScope and $broadcast an event from one Controller and listen to it in the other Controller
thus, No need of writing an extra service sharedProperties
Something like this :-
app.controller("PortFolioController", function ($scope, GetPortfolios,$rootScope) {
//Other Codes
$scope.addOrder = function (id) {
$rootScope.$broadcast('ID-Clicked', { Id: id});
};
}
app.controller("OrderController", function ($scope, GetOrders,$rootScope) {
//Other Codes
//Listen for the event
$rootScope.$on('ID-Clicked', function(event, data){
$scope.item = data.Id;// data.Id has the Clicked Id
});
}
This approach can be done using 2 ways.
First Way is to broadcast some events from the parent rootscope instance and capture that on the $scope elements.This format is not considered as a good way of coding.
Secondly, to communicate data between controllers use a service.Yeah you are currently on the right,clean & widely accepted path.
Remember that services are singletons.so once instansiated it is available to all controllers.But the challenge here is as soon as you click on button element your service sets your idValue in the service.Now how does the second controller know that.The answer would be register a $watch service to watch if the idValue in the service is changed as below.
app.controller("OrderController", function ($scope, GetOrders, sharedProperties) {
$scope.$watch(function () {
return sharedProperties.getId()
}, function (newValue, oldValue) {
if (newValue != oldValue) {
$scope.item = newValue;
$scope.Orders = GetOrders.query({ id: item });
}
});
});

Using jquery with dynamically created elements from angular doesnt work

Im trying to use jquery to manipulate elements created by angular, but I am not able to get it to work. I was wondering if anyone could help me out. Thanks
Here is the HTML
<div class="patients">
<tbody ng-repeat="patient in patients">
<tr>
<td>{{patient.name}}</td>
<td>{{patient.number}}</td>
<td>{{patient.date}}</td>
<td id="item-{{$index}}">{{patient.reminded}}</td>
<div class="sendreminder">
<td>
<a href="" class="btn btn-info btn-sm sendreminder" style=" background-color: #00e699; border-color:#00e699; " ng-click="post($index) " "$parent.selected = $index" id="button-{{$index}}">
<span class="glyphicon glyphicon-send"></span> Request Payment
</a>
</td>
</div>
<td>
<a href="" style="text-decoration:none; color:inherit; scale: 4" class="pe-7s-info">
</a>
</td>
</tr>
</tbody>
</div>
Here is the jquery
$(function() {
$('.patients').on('click', ".sendreminder",function(e){
alert('worked');
});
});
ng-repeat recreates DOM everytime it detects changes(and hence, all the attached events will be gone). So to reattach the events after ng-repeat finishes, you can do
<tbody ng-repeat="patient in patients" ng-init="$last && ngRepeatFinish()">
$last will be set to true if its the last item for ng-repeat
and in you controller, create ngRepeatFinish() function
$scope.ngRepeatFinish = function(){
$('.sendreminder').click(function(e){
alert('worked');
});
}
you can also make custom directives for this which is better than this, but this will suffice for a quick solution.
See this for a solution with custom directives
You should call that code immediately after you dynamically create the new element since that code sets the handler for the actual elements (when you call the function) that have class .patients, not the new ones...
i recommend you to use Angular instead of Jquery
added both methods below
//using Jquery
$('.patients').on('click', ".sendreminder", function(e) {
alert('from JQuery');
});
function TestCtrl($scope) {
$scope.patients = [{
name: 'one',
number: 1,
date: '2016-08-16',
reminded: true
}, {
name: 'two',
number: 2,
date: '2016-08-16',
reminded: true
}, {
name: 'three',
number: 3,
date: '2016-08-16',
reminded: false
}];
//using angular
$scope.post = function(i) {
alert('from Angular');
var selectedPatient = $scope.patients[i];
console.log(selectedPatient);
};
}
<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>
<div ng-app>
<div class="patients" ng-controller="TestCtrl">
<table>
<thead>
<tr>
<th>Name</th>
<th>Number</th>
<th>Date</th>
<th>Reminded</th>
<th>Request</th>
<th>Info</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="patient in patients">
<td>{{patient.name}}</td>
<td>{{patient.number}}</td>
<td>{{patient.date}}</td>
<td id="item-{{$index}}">{{patient.reminded}}</td>
<td>
<a href="" class="btn btn-info btn-sm sendreminder" style="background-color: #00e699; border-color:#00e699;" ng-click="post($index)" id="button-{{$index}}">
<span class="glyphicon glyphicon-send"></span> Request Payment
</a>
</td>
<td>
<a href="" style="text-decoration:none; color:inherit; scale: 4" class="pe-7s-info">test
</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>

How to define a function inside angular ui modal controller

I try to define a function inside angular ui modal controller by default a found the two function $scope.ok and the $scope.cancel and I want to add my function that remove an item form a list of items that a send to that controller
This my angular ui modal controller code :
myapp.controller('ModalInstanceCtrl', function ($scope,$location,$uibModalInstance, items) {
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$uibModalInstance.close($scope.selected.item);
alert("redirection");
$location.path('/questionnaire');
};
$scope.closeListeChoix = function () {
$uibModalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
$scope.deleteChoix=function($index)
{
alert("supp")
$scope.items.splice($index, 1);
};
});
and here where I send the liste of items to modal controller
$scope.ListeChoixOneQuestion=[];
$scope.openListeChoix = function ($index) {
$scope.ListeChoixOneQuestion=$scope.questions[$index].choix ;
console.log("*********** choix de la question **********")
for(var i=0;i<$scope.ListeChoixOneQuestion.length;i++){
console.log("choix : "+$scope.ListeChoixOneQuestion[i].designation);
}
var modalInstance = $uibModal.open({
animation: $scope.animationsEnabled,
templateUrl: 'listeOfChoix.html',
controller: 'ModalInstanceCtrl',
resolve: {
items: function () {
return $scope.ListeChoixOneQuestion;
}
}
});
and this my html code when i call the function deleteChoix in my ng-click nothing happen and the item did not remove from the list of items
any solution
<div class="modal-body">
<div class="row">
<div class="table-responsive">
<table id="Table2" class="table table-bordered table-striped">
<thead>
<tr>
<th>Designation</th>
<th>Image</th>
<th>Aller à la question</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="choix in items track by $index">
<td>{{choix.designation}}</td>
<td>{{choix.imageUrl}}</td>
<td>{{choix.gotoQuestion}}</td>
<td class="text-center" style="width: 50px;">
<span class="btn btn-danger btn-xs fa fa-remove" style="cursor: pointer;" ng-click="deleteChoix($index);"></span>
</td>
<tr>
</tbody>
</table>
</div>
</div>
</div>
As said in comment the short solution is
$parent.deleteChoix($index);
It is a scope problem du to limit of inheritancy in Javascript.
If you don't want to have this problem, always use an inermediary object like :
$scope.context = {};// NEVER forget to initialize it in your controller or it won't work even if you don't put anything in at the start.
$scope.context.deleteChoix = [...];
So you won't need to wonder if you should use $parent or even $parent.$parent.
Check http://zcourts.com/2013/05/31/angularjs-if-you-dont-have-a-dot-youre-doing-it-wrong/ for more information.

Populate angular ui bootstrap popover

How do you populate an angular ui bootstrap popover? I want to populate the popover with a list of actor names. Here is the code:
<div class="container" ng-if="radioModel=='Search for Actor'">
<p>Movies played in:</p>
<table class="table table-bordered">
<tr ng-repeat="name in listOfMovies">
<td>
<p>
<button uib-popover="populateWithListOfData" popover-trigger="mouseenter" type="button" class="btn btn-default"> {{ name }}</button>
</p>
</td>
</tr>
</table>
</div>
I want to be able to populate this popover for each name of the ng-repeat. So I will get a name of a movie and based on that name I want to populate the popover with a list of actors in that movie. Thanks guys.
This is definitely possible.
I have setup a data item called friends in JS
$scope.friends = [
{name:'John'},
{name:'Jessie'},
{name:'Johanna'},
{name:'Joy'}
];
Also , an array was created for the text in the popup
$scope.toolTip =['D','A','C','T'];
If you want to display a pop up for each row.
I've created the ng-repeat and the relevant popover.In order to display all the letters in the first popover.
<div ng-repeat="f in friends">
{{f.name}}
<button uib-tooltip="{{toolTip[$index]}}" type="button" class="btn btn-default">Tooltip {{placement.selected}}</button>
</div>
Here is a working demo
How does it works?
Your tool tip value is set as uib-tooltip="{{toolTip[$index]}}".it accesses each element according to the $index obtained from ng-repeat.
If you want to display all the data in the first pop up
I've created the ng-repeat and the relevant popover.In order to display all the letters in the first popover.
<div ng-repeat="f in friends">
<div ng-if="$index==0">
<button uib-tooltip="{{toolTip}}" type="button" class="btn btn-default">Tooltip {{placement.selected}}</button>
</div>
{{f.name}}
</div>
Here is a working demo
How does it works?
Your tool tip value is set as uib-tooltip="{{toolTip}}".It enters the ng-if , if the condition is met, and thus prints the array.
I couldn't test if it works, but this might give you the idea.
(function ()
{
function moviePopover($compile)
{
return {
restrict: "EA",
scope: true,
templateUrl: '<button uib-popover-template="dynamicPopover.templateUrl" popover-title="{{dynamicPopover.title}}" type="button" class="btn btn-default">Popover With Template</button>',
link: function (scope, element, attrs)
{
scope.dynamicPopover = {
content: "Movies",
templateUrl: "myPopoverTemplate.html",
title: "Title"
};
if (attrs.movieName !== undefined)
{
scope.movieList = getMoviesByName(attrs.movieName);
$compile(element)(scope);
//If 1st leads to infinit loop use this
// $compile(element.contents())(scope);
}
function getMoviesByName(movieName)
{
//return all moviews based on movieName
//here im just returning dummy array(you return your data)
return ["Movie1", "Movie2"];
}
}
}
}
angular.module("myApp").directive("moviePopover", ["$compile", moviePopover]);
}());
<script type="text/ng-template" id="myPopoverTemplate.html">
<ul>
<li ng-repeat="movie in movieList">{{movie}}</li>
</ul>
</script>
<div class="container" ng-if="radioModel=='Search for Actor'">
<p>Movies played in:</p>
<table class="table table-bordered">
<tr ng-repeat="name in listOfMovies">
<td>
<p>
<movie-popover movie-name="{{name}}"></movie-popover>
</p>
</td>
</tr>
</table>
</div>

Angular, share directive template between click functions

I have a directive which, when called, passes in a controller and an array.
In the controller I pass in, there is an object I want to loop over.
my html looks like this:
<div class="row">
<div class="col-md-6">
<div class="jumbotron" ng-controller="protocolCtrl as pctrl">
<button type="button" id="protocol" class="btn btn-primary btn-lg" ng-click="pctrl.getUpdatedList()"
data-toggle="modal" data-target="#modal">Modify Current Protocols</button>
<!--IN THIS MODAL YOU CAN ADD/CHANGE/DELETE DATA-->
<modal-directive list="pctrl" headers="['ID', 'Protocol']"></modal-directive>
</div>
</div>
<div class="col-md-6">
<div class="jumbotron" ng-controller="categoryCtrl as cctrl">
<button type="button" id="category" class="btn btn-primary btn-lg" ng-click="cctrl.getUpdatedList()"
data-toggle="modal" data-target="#modal">Modify Current Categories</button>
<!--IN THIS MODAL YOU CAN ADD/CHANGE/DELETE DATA-->
<modal-directive list="cctrl" headers="['ID', 'Category']"></modal-directive>
</div>
</div>
</div>
My problem is that no matter what I do, it's always the FIRST directive in the html that showes up, no matter what button I press.
My directive looks like this:
.directive('modalDirective', function(){
return {
restrict: 'E',
templateUrl: '/directives/modal-directive.html',
scope: {
list: '=',
headers: '='
},
link: function(scope, element, attrs){
console.log(attrs.list + ' | ' + attrs.headers);
}
};
});
My modal-directive.html looks like this:
<table class="table table-striped">
<thead>
<tr>
<th ng-repeat="h in headers"> {{ h }} </th>
</tr>
</thead>
<tbody>
<!-- Loop through -->
<tr ng-repeat="l in list.list">
<!--Access the actual values inside each of the objects in the array-->
<td ng-repeat="data in l"> {{ data }} </td>
<td>
<button type="button" class="btn btn-primary btn-sm"
data-toggle="modal">Edit</button>
</td>
<td>
<button type="button" class="btn btn-danger btn-sm" ng-click="list.removeData(l)"
data-dismiss="modal">Remove</button>
</td>
</tr>
</tbody>
</table>
Am I using isolated scopes wrong, or is it something else I need to change in order to make this work?
Update
Here is a fiddle, that demonstrates the problem.
No matter which button i click, it displays the same text in the modal body.
You don't really need two controllers and two directives to achieve this. Below is an example of how you can do this. Notice I moved the controller to the row instead of having separate controllers for each column. The controller myCtrl now handles the click functions which are bound to the buttons using the ng-click attribute. This then determines the which text should be placed where by calling there respective functions. IE proto() and cat()
Now this may not be ideal for your situation depending on how you plan on the architecture of your application. But it works for your current problem in terms of what you have provided.
HTML
<body ng-app="TM">
<div class="row" ng-controller="myCtrl as modalControl">
<div class="col-md-6">
<div class="jumbotron" >
<button
ng-click='proto()'
type="button" id="protocol"
class="btn btn-primary btn-lg"
data-toggle="modal"
data-target="#modal">Modify Current Protocols
</button>
</div>
</div>
<div class="col-md-6">
<div class="jumbotron">
<button
ng-click='cat()'
type="button"
id="category"
class="btn btn-primary btn-lg"
data-toggle="modal"
data-target="#modal">Modify Current Categories
</button>
</div>
</div>
<!--IN THIS MODAL YOU CAN ADD/CHANGE/DELETE DATA-->
<modal-directive ctrl="modalControl"></modal-directive>
</div>
</body>
Angular JS
angular.module('TM', [])
.controller('myCtrl', function($scope){
$scope.text ='default';
$scope.proto = function() {
this.text = 'Now looking at the protocol part'
}
$scope.cat = function() {
this.text = 'Now looking at the category part'
}
})
.directive('modalDirective', function(){
return {
restrict: 'E',
scope: true,
template: ['<div id="modal" class="modal fade" role="dialog">',
'<div class="modal-dialog">',
'<div class="modal-content">',
'<div class="modal-header">',
'<h4 class="modal-title">Modal Header</h4>',
'</div>',
'<div class="modal-body">',
'<p> {{ text }} </p>',
'</div>',
'<div class="modal-footer">',
'<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>',
'</div>',
'</div>',
'</div>',
'</div>'].join('')
}
});
Demo:
https://jsfiddle.net/DTcHh/10193/
UPDATE:
Okay, I took another look. And even though the above example works. I noticed that I have a few extra things that I didn't necessarily need. For example myCtrl as modalControl doesn't need the as modalControl part. Below is an updated example. I did this one with some different simplified markup.
HTML:
<body ng-app="TestApp">
<div ng-controller="myCtrl">
<button ng-click="one()">One</button>
<button ng-click="two()">Two</button>
<test-directive></test-directive>
</div>
</body>
Angular Example (without Isolated Scope)
angular.module('TestApp', [])
.controller('myCtrl', function($scope){
$scope.text ='default';
$scope.one = function() {
this.text = 'this is one'
}
$scope.two = function() {
this.text = 'this is two'
}
})
.directive('testDirective', function(){
return {
template: "<div id='test'>{{text}}</div>"
}
});
Demo 2:
https://jsfiddle.net/krishollenbeck/v8tczaea/12/
Note this..
restrict: 'E',
scope: true
Was also not needed because I am not using Isolated scope in this example. More info here https://docs.angularjs.org/guide/directive
Please check this JSFiddle.
The reason is that data-target value points to the DOM element id of the modal. If you fixed this id in the directive template, clicking on the button will always initiate the modal with id modal. So you need to make the modalId as another parameter of the directive.
By the way, you can pass a controller to a directive. Just like this JSFiddle:
angular.module('Joy', [])
.controller('MyCtrl', ['$scope', function ($scope) {
this.value = 'Joy';
}])
.directive('passMeContrller', [function () {
return {
restrict: 'A',
scope: {
ctrl: '=',
},
template: '<div>Value: {{ctrl.value}}</div>'
};
}]);
The HTML:
<div ng-app="Joy" ng-controller="MyCtrl as c">
<div pass-me-contrller ctrl="c"></div>
<hr>
<div ng-bind="c.value"></div>
</div>
Because the controller itself is just a JavaScript object.
Just a reminder: you are using protocolCtrl as pctrl, so you need to specify like this.list=....
If you want to pass in a function to the isolated scope, use &.
However, I suggest not to pass in the whole controller to a directive. A controller is designed to:
Set up the initial state of the $scope object.
Add behavior to the $scope object.
Controllers are not supposed to be reused. Usually there are many properties on the $scope, while some of them passed to the directive will not be used by it.

Categories

Resources