angular hide when !element clicked - angularjs

I'm rendering some td's inside a ng-repeated tr with a directive attribute. For each iteration there are two tr's, the second is initially hidden, until the user clicks the first tr. The revealed tr is correctly hidden when another tr is clicked. However, I need the revealed tr to be hidden when clicking anywhere outside the currently displayed tr's, not just other tr's. This isn't working in my directive because each time a click event is fired the documentClickHandler function is evaluated for each ng-repeat iteration i.e., the first eventOutsideTarget may evaluate to true but the others are false, so the closePreview() function is always called. Any ideas?
<tr ng-repeat-start="campaign in vm.campaignData track by campaign.id" ng-click="vm.showCampaignPreview(campaign)" campaign-item campaign="campaign" campaign-options="vm.campaignOptions" close-preview="vm.hideCampaignPreview()">
</tr>
<tr ng-class-odd="'odd'" ng-class-even="'even'" ng-repeat-end ng-show="vm.selectedCampaign.id == campaign.id">
<td colspan="8">{{ campaign.description }}</td>
</tr>
// directive
(function(){
'use strict';
angular.module('campaignApp')
.directive('campaignItem', ['$document', "$parse", function ($document, $parse) {
return {
restrict: 'A',
scope: {
campaign: '=',
campaignOptions: '=',
closePreview: '&'
},
controller: 'vsmsCampaignsController',
templateUrl: 'campaign.tpl.html',
link: function(scope, element, attr, controller) {
var documentClickHandler = function (event) {
var eventOutsideTarget = (element[0] !== event.target.parentElement) && (0 === element.find(event.target.parentElement).length);
if (eventOutsideTarget) {
scope.$apply(function () {
scope.closePreview();
console.log('close preview');
});
}
};
$document.on("click", documentClickHandler);
scope.$on("$destroy", function () {
$document.off("click", documentClickHandler);
});
}
};
}]);
})();
// campaign.tpl.html
<td>{{ campaign.name }}</td>
<td>{{ campaign.priority }}</td>
<td>{{ campaign.status }}</td>
<td>{{ campaign.createdBy }}</td>
<td>{{ campaign.approvedBy }}</td>
<td>{{ campaign.releaseDate * 1000 | date:'short' }}</td>
<td>{{ campaign.expirationDate * 1000 | date:'short' }}</td>
<td>
<select ng-init="selectedOption = campaignOptions[0]" name="campaignOption" class="form-control" ng-model="selectedOption" ng-options="option.name for option in campaignOptions track by option.value">
</select>
</td>

Related

Pass value to another controller in angularjs

I am new to AngularJS.
I have a div assigned controller to controller1. In this div, I am showing EmployeeList with three links for View, Edit,Delete for each employee.
I have another div assigned controller to controller2. By clicking the view link on any employee, I want to show full details in second div. Both divs are in same page.
How do I do that? Below is what I tried.
<div ng-controller="employeeController" style="border:solid black 3px;float:left;padding:5px">
<h3 style="color:#ff6a00">List Of Employees</h3>
<table>
<tr>
<th hidden>ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Full Name</th>
<th>Actions</th>
</tr>
<tr ng-repeat="employee in employees">
<td hidden><strong>{{ employee.ID }}</strong></td>
<td>{{ employee.FName }}</td>
<td>{{ employee.LName }}</td>
<td><strong>{{ employee.FName + ' ' + employee.LName }}</strong></td>
<td>
<a data-ng-click="GetEmployeeByID({{employee.ID}})" href="javascript:;">View</a>
</td>
</tr>
</table>
</div>
<div ng-controller="employeeByIDController" style="border:solid black 3px;float:right;padding:5px">
<h3 style="color:#ff6a00">Employee Details</h3>
<table>
<tr>
<th hidden>ID</th>
<th>Full Name</th>
<th>Qualification</th>
<th>Gender</th>
<th>Phone</th>
<th>Email</th>
<th>Address</th>
<th>City</th>
</tr>
<tr>
<td hidden><strong>{{ employeeByID.ID }}</strong></td>
<td>{{ employeeByID.FName + ' ' + employeeByID.LName }}</td>
<td>{{ employeeByID.Qualification }}</td>
<td>{{ employeeByID.Gender }}</td>
<td>{{ employeeByID.Phone }}</td>
<td>{{ employeeByID.Email }}</td>
<td>{{ employeeByID.Address }}</td>
<td>{{ employeeByID.City }}</td>
</tr>
</table>
</div>
The code in factory is as below.
mainApp.factory('EmployeeFactory', function ($http) {
return {
GetEmployeeList: function () {
return $http({
method: 'GET',
url: '/AngularEmployee/EmployeeList'
});
}
}
return {
GetEmployeeByID: function ($id) {
return $http({
method: 'GET',
url: '/AngularEmployee/EmployeeByID',
params: { empID : id }
});
}
}
});
I have added controller as follows.
mainApp.controller("employeeController", ['$scope', 'EmployeeFactory', function ($scope, EmployeeFactory) {
$scope.GetEmployeeList = function () {
EmployeeFactory.GetEmployeeList().success(function (data) {
$scope.employees = data;
}).error(function (data) {
$scope.error = "An Error has occured while Loading users! " + data.ExceptionMessage;
});
};$scope.GetEmployeeList();
}]);
Angular has a pubsub mechanism for this. It's well documented. E.g.:
https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
if you want to share data between two controllers in angularJs , using a service or factory is a better option as service/factory in angularJs are injectable.But service/factory is a singleton object so be aware of the fact that same object of the service/factory will be shared by all the controllers.
Taking your example ,
you can create another service :
mainApp.service('sharedService',function(){
this.employee={};
this.getter=function(){
return this.employee;
};
this.setter=function(emp){
this.employee=emp;
};
});
Then you can share this service across your controllers to get or set employee.
mainApp.controller("employeeController", ['$scope', 'EmployeeFactory', 'sharedService', function($scope, EmployeeFactory, sharedService) {
$scope.GetEmployeeList = function() {
EmployeeFactory.GetEmployeeList().success(function(data) {
$scope.employees = data;
sharedService.setter($scope.employees);//set in shared service
}).error(function(data) {
$scope.error = "An Error has occured while Loading users! " + data.ExceptionMessage;
});
};
$scope.GetEmployeeList();
}]);
And then in employeeByIDControlle ,
mainApp.controller("employeeByIDController", ['$scope', 'EmployeeFactory', 'sharedService', function($scope, EmployeeFactory, sharedService) {
$scope.employees = sharedService.getter();//get from shared service
}]);

Angular directive with no template - assign ng-click function from directive

How can I set the ng-click function on the element where my directive attribute is applied? After having issues with my directive template, because it spans multiple tr's, I resulted to using ng-repeat-start/end i.e., no template. So, my question is: what is the proper way to get the contents of the directive element into the isolate scope of the directive, so I can assign values/functions to it, as if it were the template?
// controller scope
<tr campaign-item ng-repeat-start="campaign in vm.allCampaigns.results | orderBy:vm.params.sort:vm.sortReverse track by $index" ng-if="vm.allCampaigns.totalCount > 0" campaign="campaign" close-preview="vm.hideCampaignPreview()" class="campaign-tr" id="campaign-{{campaign.id}}" ng-class="{'selected': showCampaignDetails}" user-role="{{vm.user.role}}" campaign-options="" campaign-detail="" show-campaign-details="">
<td>{{ campaign.name }}</td>
<td>{{ campaign.priority }}</td>
<td>{{ campaign.status }}</td>
<td>{{ campaign.createdBy }}</td>
<td>{{ campaign.approvedBy }}</td>
<td ng-show="item.releaseDate">{{ campaign.releaseDate * 1000 | date:'short' || ''}}</td>
<td ng-show="!item.releaseDate"></td>
<td ng-show="item.expirationDate">{{ campaign.expirationDate * 1000 | date:'short' }}</td>
<td ng-show="!item.expirationDate"></td>
<td>
<select id="campaign-options" name="campaignOptions" class="form-control" ng-model="selectedCampaignOption" ng-options="option for option in campaignOptions track by $index">
</select>
</td>
</tr>
<tr class="campaign-description campaign-{{campaign.id}}" ng-show="showCampaignDetails" ng-class="{'selected': showCampaignDetails}">
<td colspan="8">
<p> {{ campaignDetails.description }}</p>
</td>
</tr>
// directive
angular.module('fotaAdminPortal')
.directive('campaignItem', ['vsmsCampaignFactory', function (vsmsCampaignFactory) {
return {
restrict: 'A',
transclude: true,
scope: {
campaign: '=',
closePreview: '&',
userRole: '#',
campaignOptions: '=?',
showPreview: '=?',
showCampaignDetails: '=?',
campaignDetail: '=?'
},
// templateUrl: 'app/vsms/admin/campaign/campaign.tpl.html',
link: function(scope, element, attr, ctrl, transclude) {
scope.showCampaignDetails = false;
transclude(scope, function(clone) {
element.append(clone);
element[0].addEventListener('click', function(e) {
if(e.target.id == 'campaign-options') {
return;
} else {
vsmsCampaignFactory.getCampaign(scope.campaign.id)
.then(function(response) {
scope.showCampaignDetails = true;
scope.campaignDetails = response;
console.log(scope);
});
}
})
});
...
You might add ng-click from directive like so: (If you'd provide a fiddle or plnkr would be easier for us to test)
element.attr("ng-click", "onClick()");
If you have the onClick() function on controller add controllerName.onClick()
element.attr("ng-click", "vm.onClick()");
Then at the end
$compile(element.contents())(scope);
or
$compile(element)(scope);
Another option is
element.bind('click', function(e) {
// Handle onclick event here.
});

AngularJS pass function with dynamic parameter to directive

TL;DR version: I'm trying to write a directive to show various objects in a table, and add an edit / delete button at the end of the table rows.
The example entity will be ProjectType, so:
project-type.controller.js
// determines which attributes we want to show in the table
$scope.attr = [
'computerName',
'shortName',
'longName'
];
// puts the current projectType object to the form
$scope.edit = function(projectType) {
$scope.projectType = projectType;
$scope.computerName = projectType.computerName;
$scope.isEdit = true;
};
// shows a delete modal window
$scope.deleteConfirm = function (projectType) {
Modal.confirm.deleteModal('Project type', projectType.computerName, function () {
Message.createMessage('project-type.message.delete.success', { projectType: projectType.computerName }, { type: 'warning', timeout: 4000 });
$scope.remove(projectType);
})();
};
project-type.html
<!-- show table with directive -->
<entity-table object="projectTypes" entity="'project-type'" attr="attr"></entity-table>
<!-- show table without directive -->
<table class="table table-responsive">
<thead>
<tr>
<th>{{ 'project-type.create.label.computerName' | translate }}</th>
<th>{{ 'project-type.create.label.shortName' | translate }}</th>
<th>{{ 'project-type.create.label.longName' | translate }}</th>
<th>{{ 'common.operations' | translate }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="projectType in projectTypes">
<td><strong>{{ projectType.computerName }}</strong></td>
<td>{{ projectType.shortName }}</td>
<td>{{ projectType.longName }}</td>
<td>
<a ng-click="edit(projectType)" class="pencil"><span class="glyphicon glyphicon-pencil"></span></a>
<a ng-click="deleteConfirm(projectType)" class="trash"><span class="glyphicon glyphicon-trash"></span></a>
</td>
</tr>
</tbody>
</table>
entity-table.directive.js
angular.module('unioffice-centralApp')
.directive('entityTable', function () {
return {
restrict: 'E',
replace: true,
transclude : true,
templateUrl: '/components/directives/entity-table/entity-table.html',
scope: {
object: '=',
entity: '=',
attr: '='
},
link: function (scope, element, attrs, controllers, transclude) {
console.log(scope);
}
};
});
entity-table.html
<div>
<table class="table table-responsive">
<thead>
<tr>
<th ng-repeat="att in attr">{{ (entity + ".table.label." + att) | translate }}</th>
<th ng-repeat-end translate>common.operations</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="obj in object">
<td ng-repeat="att in attr">{{ obj[att] }}</td>
<td ng-repeat-end>
<a ng-click="edit(obj)" class="pencil"><span class="glyphicon glyphicon-pencil"></span></a>
<a ng-click="deleteConfirm(obj)" class="trash"><span class="glyphicon glyphicon-trash"></span></a>
</td>
</tr>
</tbody>
</table>
(there is a /div at the end of the entity-table.html, stackoverflow seems to kill it)
So the question is: how do I pass the edit() and deleteConfirm() functions to the directive to make them work?
In this picture you can see the two tables are the same, but the buttons in the first one obviously won't do anything at this point: (also I know the second one has bold first column, it's ok, that's not the point now)
Passing/binding the functions from the controller to the directive is done in the same manner as binding an object = or a string # with the difference being how it's referenced in the directive
Pass in the functions as attributes on the directive as shown below ..
<entity-table object="projectTypes" entity="'project-type'" on-delete="deleteConfirm" on-edit="edit" attr="attr"></entity-table>
In your directive do this ..
angular.module('unioffice-centralApp')
.directive('entityTable', function () {
return {
restrict: 'E',
replace: true,
transclude : true,
templateUrl: '/components/directives/entity-table/entity-table.html',
scope: {
object: '=',
entity: '=',
attr: '=',
onDelete: '&', // function referencing
onEdit: '&' // function referencing
},
link: function (scope, element, attrs, controllers, transclude) {
scope.deleteFn = function (obj) {
scope.onDelete()(obj); // this invokes the deleteConfirm function in the controller
}
scope.editFn = function (obj) {
scope.onEdit()(obj); // this invokes the deleteConfirm function in the controller
}
}
};
});
In your controller ...
$scope.edit = function(projectType) {
$scope.projectType = projectType;
$scope.computerName = projectType.computerName;
$scope.isEdit = true;
};
// shows a delete modal window
$scope.deleteConfirm = function (projectType) {
Modal.confirm.deleteModal('Project type', projectType.computerName, function () {
Message.createMessage('project-type.message.delete.success', { projectType: projectType.computerName }, { type: 'warning', timeout: 4000 });
$scope.remove(projectType);
})();
};
PLUNKR

Angular custom directive execute after inner ng-repeat finished

My angular template is:
<div style="min-width: 500px;">
<table sticky-table>
<thead>
<tr>
<th>Size</th>
<th>Vertical</th>
<th>Choker</th>
<th>Basket</th>
<th>Basket at 60</th>
<th>Basket at 45</th>
<th>Basket at 30</th>
</tr>
</thead>
<tbody ng-repeat="row in getWireRopeSlingCapacityData(metricUnit)">
<tr>
<th>{{ row.size }}</th>
<td>{{ row.vertical }}</td>
<td>{{ row.choker }}</td>
<td>{{ row.basket }}</td>
<td>{{ row.basket_60 }}</td>
<td>{{ row.basket_45 }}</td>
<td>{{ row.basket_30 }}</td>
</tr>
</tbody>
</table>
</div>
I need a custom directive 'sticky-table' that will apply some jquery on the table. My issue is that the body of table element is not populated when directive executes. My directive code is:
app.directive("stickyTable", function($parse, $timeout) {
var stickyTable = function(element){
... aplly jquery code on table element
... table element inner html is: "
<thead>
<tr>
<th>Size</th>
<th>Vertical</th>
<th>Choker</th>
<th>Basket</th>
<th>Basket at 60</th>
<th>Basket at 45</th>
<th>Basket at 30</th>
</tr>
</thead>
<!-- ngRepeat: row in getWireRopeSlingCapacityData(metricUnit) -->
"
};
return {
restrict: 'A',
scope: true,
link: function(scope, element, attrs) {
var result = stickyTable(element);
scope.$on('scroll1Position', function(event, position) {
//console.log('Got scrolled to: ' + position.top + 'x' + position.left);
if (!result)
return;
result.repositionHead(position);
result.repositionColumn(position);
});
}
}
});
Be sure to specify the priority of your directive. Ng-repeat gets set to a default priority of 1000, so you need to set a higher priority than that for this directive to be compiled first. See the example below:
app.directive("stickyTable", function($parse, $timeout) {
...
return {
prioriy: 1001
restrict: 'A',
scope: true,
link: function(scope, element, attrs) {
var result = stickyTable(element);
scope.$on('scroll1Position', function(event, position) {
//console.log('Got scrolled to: ' + position.top + 'x' + position.left);
if (!result)
return;
result.repositionHead(position);
result.repositionColumn(position);
});
}
}});
You can view the directive documentation for more details.

Angularjs adding directive on click

i have table that has this body
<tbody >
<tr ng-repeat="con in mycon | array | filter:search | orderBy:predicate:reverse"
ng-click="showhistory($event,con.conid)">
<td >{{ con.fname }}</td>
<td >{{ con.lname }}</td>
</tr>
</tbody>
When the row is click i call this function my my controller
$scope.showhistory = function ($event,cid) {
$event.stopPropagation();
var contentTr = angular.element('<con_history ></con_history>');
contentTr.insertAfter(angular.element($event.target).parent());
$compile(contentTr)($scope);
$scope.$apply
};
And i can see that the con_history tags get added. But it doesn't render the directive. When I add the directive i do not see BLAH BLAH , I only see the con_history tag
this is my directive
app.directive('con_history', function() {
return {
restrict: 'AE',
replace: 'true',
template: '<tr><td colspan="11">BLAH BLAH</td></tr>'
};
});
thanks for any help
You are not doing it in angular way, I think this is what you want to do:
<tr ng-repeat="con in mycon | array | filter:search | orderBy:predicate:reverse"
ng-click="showhistory($event,con.conid)">
<td >{{ con.fname }}</td>
<td >{{ con.lname }}</td>
</tr>
<con_history ng-if="historyVisible[con.conid]"></con_history>
in your controller
$scope.historyVisible = {};
$scope.showhistory = function ($event,cid) {
.
.
$scope.historyVisible[cid] = true;
.
.
};

Resources