Unit test with jasmine - angularjs

I'm working in an angular application and i want to implement a table sort system
Here is my ui-table-sort.js
(function() {
'use strict';
var dependencies = [];
angular.module('uiTable', dependencies)
.config(configFn)
.run(runFn)
.directive('uiTableSorted',[uiTableSortedDirective])
function uiTableSortedDirective() {
return {
restrict: 'A',
transclude: true,
template :
'<a ng-click="onClick()">'+
'<span ng-transclude></span>'+
'<i class="glyphicon" ng-class="{\'glyphicon-sort-by-alphabet\' : order === by && !reverse, \'glyphicon-sort-by-alphabet-alt\' : order===by && reverse}"></i>'+
'</a>',
scope: {
order: '=',
by: '=',
reverse : '='
},
link: function(scope, el, attr, ctrl) {
scope.onClick = function () {
if( scope.order === scope.by ) {
scope.reverse = !scope.reverse
} else {
scope.by = scope.order ;
scope.reverse = false;
}
}
}
}
}
})();
This file is used an other controller 'entity-list.js :
(function() {
'use strict';
var dependencies = ['uiTable','core'];
angular.module('entityList', dependencies)
.config(configFn)
.run(runFn)
.directive('entityList', ['BASE_PATH', entityListDirective])
.controller('EntityListCtrl', ['$scope','Entity', EntityListCtrl])
function EntityListCtrl($scope , Entity ) {
$scope.order = 'id';
$scope.reverse = false;
$scope.eltsPerPage = 50
$scope.entityList = [];
$scope.offset = 0;
$scope.totalCount = 0;
var self = $scope;
$scope.edit = editFn;
$scope.delete = deleteFn
$scope.view = viewFn
and here is the view
<div ng-controller="EntityListCtrl">
<table class="table o-responsive-table table-hover">
<caption></caption>
<thead class="cf">
<tr>
<th scope="col" class="header" uiTableSorted order="id" by="order" reverse="reverse" >#</th>
</tr>
The problem is that my directive uiTableSorted is not added to the element in the view , is there any dependencies issue
Ps i am follow this link plunker

Related

Select Always Returning First Item of List (AngularJS)

No matter what I select, this function only returns the first item of the list. As requested, I have included the complete JS code. I have looked through this code four hours and everything seems right to me. This is my first contact with Angular and any help will be greatly appreciated!
Thank you!
HTML
<div class="form-horizontal input-append">
<select name="selectedSharedTenantId" data-ng-model="selectedSharedTenantId">
<option data-ng-repeat="item in getFilteredTenants()" value="{{item.id}}">{{item.displayName}}</option>
</select>
<button type="button" class="btn" ng-click="addSharedTenant();">
<i class="fas fa-plus"></i>
</button>
</div>
JS
$scope.addSharedTenant = function () {
alert($scope.selectedSharedTenantId)
}
COMPLETE JS CODE
angular.module('directives.datasetEditor', [
'services.dataset',
'ui.bootstrap'
])
.directive('datasetEditor', ['$modal', 'DatasetServices', function ($modal, DatasetServices) {
return {
restrict: 'A',
scope: {
tenantId: '=',
tenants: '=',
originalModel: '=model',
callback: '&callback'
},
link: function(scope, element, attrs) {
scope.model = scope.originalModel ? angular.copy(scope.originalModel) : {
entryType: 'Activity',
displayName: ''
};
var ModalInstanceCtrl = ['$scope', '$modalInstance',
function($scope, $modalInstance) {
var setSelectedSharedTenantId = function () {
var selectedTenant = $scope.getFilteredTenants()[0];
$scope.selectedSharedTenantId = selectedTenant ? selectedTenant.id : null;
};
$scope.getFilteredTenants = function () {
return _.filter($scope.tenants, function (o) {
alert(o.id)
return _.indexOf($scope.model.sharedTenantIds, o.id) == -1 && o.id != $scope.tenantId;
});
};
$scope.getTenantById = function (id) {
return _.findWhere($scope.tenants, {
id: id
});
};
$scope.removedSharedTenant = function (id) {
$scope.model.sharedTenantIds = _.without($scope.model.sharedTenantIds, id);
var selectedTenant = $scope.getFilteredTenants()[0];
setSelectedSharedTenantId();
};
$scope.addSharedTenant = function () {
//alert($scope.selectedSharedTenantId)
//alert($scope.model.sharedTenantIds)
if ($scope.selectedSharedTenantId) {
if ($scope.model.sharedTenantIds == null) {
$scope.model.sharedTenantIds = [];
}
$scope.model.sharedTenantIds.push($scope.selectedSharedTenantId);
setSelectedSharedTenantId();
}
};
$scope.submit = function(isValid) {
if (isValid) {
DatasetServices.save($scope.model).success(function(data, status, headers, config) {
$modalInstance.close(data);
});
} else {
$scope.submitted = true;
}
};
$scope.cancel = function() {
$modalInstance.dismiss();
};
$scope.submitted = false;
setSelectedSharedTenantId();
}];
function open() {
var modalInstance = $modal.open({
templateUrl: '../pkg/wisdom/common/angular/directives/dataset-editor/index.html',
controller: ModalInstanceCtrl,
scope: scope
});
modalInstance.result.then(
function(model) {
scope.callback({
model: model
});
},
function() {
}
);
};
element.on('click', open);
}
};
}]);

AngularJs app not show uib tooltip

I am trying to implement in angular js that when in li ellipsis come display tooltip for that my code is below
Dependency Injection:
angular.module('spt', ['ui.router', 'ngStorage', 'ngAnimate', 'ui.bootstrap', 'ui.slimscroll', 'angular-google-analytics', 'jmdobry.angular-cache',
'stpa.morris', 'angularReverseGeocode', 'chart.js', 'ui.calendar', 'ui.date',
'me-lazyload', 'angularUtils.directives.dirPagination', 'angular-loading-bar', 'base64',
'nemLogging', 'ui-leaflet', 'angular-google-adsense', 'dropstore-ng', 'ngVideo', 'angular-google-adsense', 'cgBusy', 'duScroll', 'angularGrid', 'infinite-scroll'
]);
In HTML:
<p class="contactEmail">
<ul style="max-width: 200px;">
<li uib-tooltip="{{email}}" tooltip-enable="flag" show-tooltip-on-text-overflow="flag" style="overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;">
<i class="ion-email"></i>
Email : {{email}}
</li>
</ul>
</p>
JS:
angular.module('spt').directive('showTooltipOnTextOverflow',
function ($timeout) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var el = element[0];
scope.$watch(function(){
return el.scrollWidth;
}, function() {
var el = element[0];
if (el.offsetWidth < el.scrollWidth) {
//console.log('ellipsis is active for element', element);
attrs.tooltipEnable = "true";
} else {
//console.log('ellipsis is NOT active for element', element);
}
});
}
};
});
Controller:
function ContactController(
$scope,
$rootScope,
$modal,
$log,
$sce,
ContactService,
Utility,
SettingsService,
Session,
APPLICATION,
RESPONSE,
CONSTANTS) {
$log.debug('in ContactController');
//function Declaration
$scope.closeModelInstance = closeModelInstance;
$scope.showDropdown = showDropdown;
$scope.selectItem = selectItem;
$scope.showBlock = false;
$scope.showBlockMessage = false;
$scope.blockMessage = '';
$scope.syncContacts = syncContacts;
$scope.flag = true;
$scope.email = "sdajkdsjsadklsdajkasldjsdakljsadklsdadsa#adsjsdsadkjadsjk.it";
if (Utility.redirectToDashboard(Session.getValue(APPLICATION.currentDeviceId)) === true) {
return true;
}
$rootScope.isChildSelected1 = Session.getValue('isChild');
var params = {};
params.id = Session.getValue(APPLICATION.currentDeviceId);
$log.debug('contacts');
$scope.items = {};
$scope.blockType = {
message: false,
Contacts: false
};
You can't use a static attribute to do that!
Use a variable from your scope e.g.
$scope.myVar = false;
HTML
<p class="contactEmail">
<ul>
<li uib-tooltip="{{item.email}}"
tooltip-enable="myVar"
show-tooltip-on-text-overflow="myVar">
<i class="ion-email"></i> Email : {{item.email}}
</li>
</ul>
</p>
JS
angular.module('spt').directive('showTooltipOnTextOverflow', function () {
return {
restrict: 'A',
scope: {
showTooltipOnTextOverflow: "="
},
link: function(scope, element, attrs) {
var el = element[0];
scope.$watch(function(){
return el.scrollWidth;
}, function() {
var el = element[0];
if (el.offsetWidth < el.scrollWidth) {
scope.showTooltipOnTextOverflow = true;
}
else {
scope.showTooltipOnTextOverflow = false;
}
});
}
};
});
Working fiddle ==> http://plnkr.co/edit/EGHPncgOVvubU9iBlJdx?p=preview

AngularJS: Cloning element containing ng-repeat

I am creating a directive with Angular 1.6 which creates a fixed header for a table. I am trying to achieve this by clone the table header and fixing this. This all works fine in most of my tables. I am using scope: false to keep parent scope, as some header elements references e.g. sorting functions. This also works. My problem is with one table I have which creates columns based on an array, because I want to be able to change columns. The columns are added with ng-repeat. When I clone this header, the ng-repeat is not cloned.
What can I do to clone an element containing an ng-repeat?
HTML of table:
<table class="proloen-table no-last-border" table-fix-header>
<thead class="light-blue-background">
<tr>
<th>{{vm.testString}}</th>
<th ng-repeat="head in vm.tableHeaders">
<span>{{ head.label | translate }}</span>
<sorting sortkey="head.sort" color="'white'" filter="vm.state.sorting"></sorting>
</th>
</tr>
</thead>
...
</table>
Controller (with controllerAs: 'vm') has (among other things):
vm.testString = 'Test';
vm.tableHeaders = [{label: 'Column1', sort: 'prop1'}, {label: 'Column2', sort: 'prop2'}];
The directive is as follows:
.directive('tableFixHeader', function ($window, $compile) {
return {
restrict: 'A',
scope: false,
link: function (scope, element) {
var clone;
function init() {
element.wrap('<div class="fix-table-container"></div>');
clone = element.clone(true);
clone.find('tbody').remove().end().addClass('table-header-fixed');
clone.removeAttr('table-fix-header');
$compile(clone)(scope);
element.before(clone);
resizeFixed();
}
function resizeFixed() {
clone.find('th').each(function (index) {
$(this).css('width', element.find('th').eq(index).outerWidth() + 'px');
});
}
function scrollFixed() {
var offset = $($window).scrollTop(),
tableOffsetTop = element.offset().top,
tableOffsetBottom = tableOffsetTop + element.height() - element.find('thead').height();
if (offset < tableOffsetTop || offset > tableOffsetBottom){
clone.hide();
}
else if (offset >= tableOffsetTop && offset <= tableOffsetBottom && clone.is(':hidden')) {
clone.show();
}
}
$window.addEventListener('scroll', scrollFixed);
$window.addEventListener('resize', resizeFixed);
scope.$on('$destroy', function() {
$window.removeEventListener('scroll', scrollFixed);
$window.removeEventListener('resize', resizeFixed);
});
init();
}
};
});
The directive works fine for tables columns are fixed and the above example clones the first "hardcoded" column just fine, along the variable from controller. The problem arises when cloning the ng-repeat. I just can't seem to figure out how to clone the ng-repeat, so that it to will work and update when I update the list of columns.
You could try to send event with $scope.$emit when ng-repeat finished rendering. Or create own event emitter and connect your directives;
app.directive('onFinishRepeat', function(){
return {
restrict: 'A',
link: function($scope) {
if($scope.$last == true) {
$scope.$emit('ng-repeat', 'finish');
}
}
}
})
app.directive('tableFixHeader', function ($window, $compile) {
return {
restrict: 'A',
scope: false,
link: function (scope, element) {
var clone;
function init() {
element.wrap('<div class="fix-table-container"></div>');
clone = element.clone(true);
clone.find('tbody').remove().end().addClass('table-header-fixed');
clone.removeAttr('table-fix-header');
$compile(clone)(scope);
element.before(clone);
resizeFixed();
}
function resizeFixed() {
clone.find('th').each(function (index) {
$(this).css('width', element.find('th').eq(index).outerWidth() + 'px');
});
}
function scrollFixed() {
var offset = $($window).scrollTop(),
tableOffsetTop = element.offset().top,
tableOffsetBottom = tableOffsetTop + element.height() - element.find('thead').height();
if (offset < tableOffsetTop || offset > tableOffsetBottom){
clone.hide();
}
else if (offset >= tableOffsetTop && offset <= tableOffsetBottom && clone.is(':hidden')) {
clone.show();
}
}
$window.addEventListener('scroll', scrollFixed);
$window.addEventListener('resize', resizeFixed);
scope.$on('$destroy', function() {
$window.removeEventListener('scroll', scrollFixed);
$window.removeEventListener('resize', resizeFixed);
});
$scope.on('ng-repeat', function(event, data){
if(data == 'finish') {
init();
}
})
}
};
});
HTML
<table class="proloen-table no-last-border" table-fix-header>
<thead class="light-blue-background">
<tr>
<th>{{vm.testString}}</th>
<th ng-repeat="head in vm.tableHeaders" on-finish-repeat>
<span>{{ head.label | translate }}</span>
<sorting sortkey="head.sort" color="'white'" filter="vm.state.sorting"></sorting>
</th>
</tr>
</thead>
...
</table>

How to call controller method from custom directive in AngularJs

UI
<div class="row" ng-app="myModule" ng-controller="multiselect">
<div class="col-md-2">
<div id="lob" ng-dropdown-multiselect="" extra-settings="dropdownSetting" options="lobs" attrfilterprojectsonlob="getProjects"
selected-model="lobsSelected" checkboxes="true">
</div>
</div>
<div class="col-md-2">
<div id="projects" ng-dropdown-multiselect="" extra-settings="dropdownSetting" options="projects"
selected-model="projectsSelected" checkboxes="true">
</div>
</div>
</div>
Angular Controller
var myApp = angular.module('myModule', ['angularjs-dropdown-multiselect']);
myApp.controller('multiselect', ['$scope', '$http', function ($scope, $http) {
$scope.lobsSelected = [];
$scope.lobs = [];
$scope.projectsSelected = [];
$scope.projects = [];
$scope.dropdownSetting = {
scrollable: true,
scrollableHeight: '200px'
}
$http.get('Home/GetALLLOB').then(function (data) {
angular.forEach(data.data, function (value, index) {
$scope.lobs.push({ id: value.N_LevelID, label: value.T_LevelName }
);
});
})
$http.get('Home/GetAllProjects').then(function (data) {
debugger;
angular.forEach(data.data, function (value, index) {
$scope.projects.push({ id: value.N_LevelID, label: value.T_LevelName }
);
});
})
$scope.getProjects = function (selectedId) {
debugger;
var parentId = id;
$http.get('Home/GetAllProjects', { params: { "parentId": selectedId } })
.then(function (data) {
angular.forEach(data.data, function (value, index) {
debugger;
$scope.projects.push({ id: value.N_LevelID, label: value.T_LevelName }
);
});
})
.catch(function (data) {
console.error('Gists error', data.status, data.data);
})
}
}])
Directive : - Please ignore fn_template()
'use strict';
var directiveModule = angular.module('angularjs-dropdown-multiselect', []);
directiveModule.directive('ngDropdownMultiselect', ['$filter', '$document', '$compile', '$parse',
function ($filter, $document, $compile, $parse) {
return {
//restrict: 'AE',
restrict: 'AEC',
scope: {
selectedModel: '=',
options: '=',
extraSettings: '=',
events: '=',
searchFilter: '=?',
translationTexts: '=',
groupBy: '#',
fn_selectAll: '&',
selectedId :'=',
attrfilterProjectsonLob: '&'
},
template: fn_Template,
link: function ($scope, $element, $attrs) {
var $dropdownTrigger = $element.children()[0];
$scope.toggleDropdown = function () {
$scope.open = !$scope.open;
};
$scope.checkboxClick = function ($event, id) {
$scope.setSelectedItem(id);
$event.stopImmediatePropagation();
};
$scope.filterProjectsonLob = function ($event, id) {
debugger;
$scope.selectedId = id;
$scope.attrfilterProjectsonLob({value : selectedId});
}
$scope.externalEvents = {
onItemSelect: angular.noop,
onItemDeselect: angular.noop,
onSelectAll: angular.noop,
onDeselectAll: angular.noop,
onInitDone: angular.noop,
onMaxSelectionReached: angular.noop
};
$scope.settings = {
dynamicTitle: true,
scrollable: false,
scrollableHeight: '300px',
closeOnBlur: true,
displayProp: 'label',
idProp: 'id',
externalIdProp: 'id',
enableSearch: false,
selectionLimit: 0,
showCheckAll: true,
showUncheckAll: true,
closeOnSelect: false,
buttonClasses: 'btn btn-default',
closeOnDeselect: false,
groupBy: $attrs.groupBy || undefined,
groupByTextProvider: null,
smartButtonMaxItems: 0,
smartButtonTextConverter: angular.noop
};
$scope.texts = {
checkAll: 'Check All',
uncheckAll: 'Uncheck All',
selectionCount: 'checked',
selectionOf: '/',
searchPlaceholder: 'Search...',
buttonDefaultText: 'LOB',
dynamicButtonTextSuffix: 'Selected',
LOBSelected: ''
};
$scope.searchFilter = $scope.searchFilter || '';
if (angular.isDefined($scope.settings.groupBy)) {
$scope.$watch('options', function (newValue) {
if (angular.isDefined(newValue)) {
$scope.orderedItems = $filter('orderBy')(newValue, $scope.settings.groupBy);
}
});
}
angular.extend($scope.settings, $scope.extraSettings || []);
angular.extend($scope.externalEvents, $scope.events || []);
angular.extend($scope.texts, $scope.translationTexts);
$scope.singleSelection = $scope.settings.selectionLimit === 1;
function getFindObj(id) {
var findObj = {};
if ($scope.settings.externalIdProp === '') {
findObj[$scope.settings.idProp] = id;
} else {
findObj[$scope.settings.externalIdProp] = id;
}
return findObj;
}
function clearObject(object) {
for (var prop in object) {
delete object[prop];
}
}
if ($scope.singleSelection) {
if (angular.isArray($scope.selectedModel) && $scope.selectedModel.length === 0) {
clearObject($scope.selectedModel);
}
}
if ($scope.settings.closeOnBlur) {
$document.on('click', function (e) {
var target = e.target.parentElement;
var parentFound = false;
while (angular.isDefined(target) && target !== null && !parentFound) {
if (_.contains(target.className.split(' '), 'multiselect-parent') && !parentFound) {
if (target === $dropdownTrigger) {
parentFound = true;
}
}
target = target.parentElement;
}
if (!parentFound) {
$scope.$apply(function () {
$scope.open = false;
});
}
});
}
$scope.getGroupTitle = function (groupValue) {
if ($scope.settings.groupByTextProvider !== null) {
return $scope.settings.groupByTextProvider(groupValue);
}
return groupValue;
};
$scope.getButtonText = function () {
if ($scope.settings.dynamicTitle && ($scope.selectedModel.length > 0 || (angular.isObject($scope.selectedModel) && _.keys($scope.selectedModel).length > 0))) {
//if ($scope.settings.smartButtonMaxItems > 0) {
if ($scope.settings.smartButtonMaxItems >= 0) {
var itemsText = [];
var SelectedTexts = [];
angular.forEach($scope.options, function (optionItem) {
if ($scope.isChecked($scope.getPropertyForObject(optionItem, $scope.settings.idProp))) {
var displayText = $scope.getPropertyForObject(optionItem, $scope.settings.displayProp);
$scope.SelectedTexts = displayText;
var converterResponse = $scope.settings.smartButtonTextConverter(displayText, optionItem);
itemsText.push(converterResponse ? converterResponse : displayText);
}
});
//if ($scope.selectedModel.length > $scope.settings.smartButtonMaxItems) {
// itemsText = itemsText.slice(0, $scope.settings.smartButtonMaxItems);
// itemsText.push('...');
//}
if (itemsText.length <= 2) {
return itemsText.join(', ');
}
else {
var totalSelected;
if ($scope.singleSelection) {
totalSelected = ($scope.selectedModel !== null && angular.isDefined($scope.selectedModel[$scope.settings.idProp])) ? 1 : 0;
} else {
totalSelected = angular.isDefined($scope.selectedModel) ? $scope.selectedModel.length : 0;
}
if (totalSelected === 0) {
return $scope.texts.buttonDefaultText;
} else {
return totalSelected + ' ' + $scope.texts.buttonDefaultText + ' ' + $scope.texts.dynamicButtonTextSuffix;
}
}
}
} else {
return $scope.texts.buttonDefaultText;
}
};
$scope.getPropertyForObject = function (object, property) {
if (angular.isDefined(object) && object.hasOwnProperty(property)) {
return object[property];
}
return '';
};
$scope.selectAll = function () {
$scope.deselectAll(false);
$scope.externalEvents.onSelectAll();
angular.forEach($scope.options, function (value) {
$scope.setSelectedItem(value[$scope.settings.idProp], true);
});
};
$scope.deselectAll = function (sendEvent) {
sendEvent = sendEvent || true;
if (sendEvent) {
$scope.externalEvents.onDeselectAll();
}
if ($scope.singleSelection) {
clearObject($scope.selectedModel);
} else {
$scope.selectedModel.splice(0, $scope.selectedModel.length);
}
};
$scope.setSelectedItem = function (id, dontRemove) {
debugger;
var findObj = getFindObj(id);
var finalObj = null;
if ($scope.settings.externalIdProp === '') {
finalObj = _.find($scope.options, findObj);
} else {
finalObj = findObj;
}
if ($scope.singleSelection) {
clearObject($scope.selectedModel);
angular.extend($scope.selectedModel, finalObj);
$scope.externalEvents.onItemSelect(finalObj);
if ($scope.settings.closeOnSelect) $scope.open = false;
return;
}
dontRemove = dontRemove || false;
var exists = _.findIndex($scope.selectedModel, findObj) !== -1;
if (!dontRemove && exists) {
$scope.selectedModel.splice(_.findIndex($scope.selectedModel, findObj), 1);
$scope.externalEvents.onItemDeselect(findObj);
} else if (!exists && ($scope.settings.selectionLimit === 0 || $scope.selectedModel.length < $scope.settings.selectionLimit)) {
$scope.selectedModel.push(finalObj);
$scope.externalEvents.onItemSelect(finalObj);
}
if ($scope.settings.closeOnSelect) $scope.open = false;
};
$scope.isChecked = function (id) {
if ($scope.singleSelection) {
return $scope.selectedModel !== null && angular.isDefined($scope.selectedModel[$scope.settings.idProp]) && $scope.selectedModel[$scope.settings.idProp] === getFindObj(id)[$scope.settings.idProp];
}
return _.findIndex($scope.selectedModel, getFindObj(id)) !== -1;
};
$scope.externalEvents.onInitDone();
}
};
}]);
function fn_Template(element, attrs) {
var checkboxes = attrs.checkboxes ? true : false;
var groups = attrs.groupBy ? true : false;
var template = '<div class="multiselect-parent btn-group dropdown-multiselect">';
template += '<button type="button" class="dropdown-toggle" ng-class="settings.buttonClasses" ng-click="toggleDropdown()">{{getButtonText()}} <span class="caret"></span></button>';
template += '<ul class="dropdown-menu dropdown-menu-form" ng-style="{display: open ? \'block\' : \'none\', height : settings.scrollable ? settings.scrollableHeight : \'auto\' }" style="overflow: scroll" >';
template += '<li ng-hide="!settings.showCheckAll || settings.selectionLimit > 0"><a data-ng-click="selectAll()"><span class="glyphicon glyphicon-ok"></span> {{texts.checkAll}}</a>';
template += '<li ng-show="settings.showUncheckAll"><a data-ng-click="deselectAll();"><span class="glyphicon glyphicon-remove"></span> {{texts.uncheckAll}}</a></li>';
template += '<li ng-hide="(!settings.showCheckAll || settings.selectionLimit > 0) && !settings.showUncheckAll" class="divider"></li>';
template += '<li ng-show="settings.enableSearch"><div class="dropdown-header"><input type="text" class="form-control" style="width: 100%;" ng-model="searchFilter" placeholder="{{texts.searchPlaceholder}}" /></li>';
template += '<li ng-show="settings.enableSearch" class="divider"></li>';
if (groups) {
template += '<li ng-repeat-start="option in orderedItems | filter: searchFilter" ng-show="getPropertyForObject(option, settings.groupBy) !== getPropertyForObject(orderedItems[$index - 1], settings.groupBy)" role="presentation" class="dropdown-header">{{ getGroupTitle(getPropertyForObject(option, settings.groupBy)) }}</li>';
template += '<li ng-repeat-end role="presentation">';
} else {
template += '<li role="presentation" ng-repeat="option in options | filter: searchFilter">';
}
template += '<a role="menuitem" tabindex="-1" ng-click="setSelectedItem(getPropertyForObject(option,settings.idProp))">';
if (checkboxes) {
template += '<div class="checkbox"><label><input class="checkboxInput" type="checkbox" ng-click="checkboxClick($event, getPropertyForObject(option,settings.idProp)); filterProjectsonLob($event, getPropertyForObject(option,settings.idProp)) "ng-checked="isChecked(getPropertyForObject(option,settings.idProp))" /> {{getPropertyForObject(option, settings.displayProp)}}</label></div></a>';
} else {
template += '<span data-ng-class="{\'glyphicon glyphicon-ok\': isChecked(getPropertyForObject(option,settings.idProp))}"></span> {{getPropertyForObject(option, settings.displayProp)}}</a>';
}
template += '</li>';
template += '<li class="divider" ng-show="settings.selectionLimit > 1"></li>';
template += '<li role="presentation" ng-show="settings.selectionLimit > 1"><a role="menuitem">{{selectedModel.length}} {{texts.selectionOf}} {{settings.selectionLimit}} {{texts.selectionCount}}</a></li>';
template += '</ul>';
template += '</div>';
element.html(template);
}
Comments
---------- .
Please guide me what is wrong i am doing, as controllers method is not getting invoked.
I need to call $scope.getProjects method present in controller from $scope.filterProjectsonLob in directive. Please guide me as i am new to angularJs.
Your directive can have it's own controller:
angular.directive(function() { //blah blah
return {
//restrict: 'AE',
restrict: 'AEC',
scope: {
},
controller: multiselect,
controllerAs: 'vm'
}
});
Then it's a matter of vm.methodNameCall(arguments) as you would normally do.
What's happening here is that your controller is existing on the parent scope of your directive and the directive has an isolate scope so it doesn't see its parent scope's data.
The simplest solution (but not necessarily most correct) is to use $scope.$parent ($scope.$parent.getProjects) from inside your directive to access the controller's scope.
A more correct solution is to refactor your multiselect controller to be a directive. With multiselect as a directive (ex. ngMultiselect), you would be able to inject its controller into other directives. You won't need to change your controller code that much, just make a directive with it as a controller rather than use ng-controller.
Step 1: replace ng-controller with ngMultiselect directive.
directiveModule.directive('ngMultiselect', function(){
return {
restrict: 'AEC',
controller: 'multiselect'
};
});
<div class="row" ng-app="myModule" ng-multiselect> ... </div>
Step 2: update multiselect controller to expose getProjects function
// change:
$scope.getProjects = function (selectedId) {...};
// to:
this.getProjects = function (selectedId) {...};
// and if you still need getProjects on multiselect's scope, you can add $scope.getProjects = this.getProjects;
Step 3: Inject controller into ngDropdownMultiselect by requiring ngMultiselect
directiveModule.directive('ngDropdownMultiselect', ['$filter', '$document', '$compile', '$parse',
function ($filter, $document, $compile, $parse) {
return {
//restrict: 'AE',
restrict: 'AEC',
require: 'ngMultiselect',
scope: {
selectedModel: '=',
options: '=',
// etc ...
},
template: fn_Template,
link: function ($scope, $element, $attrs, MultiselectCtrl) {
$scope.getProjects = MultiselectCtrl.getProjects;
// and the rest of your stuff here...
}
};
}
]);
Learning how to set up and differentiate controllers + directive link functions is certainly one of the finer points of learning angular. I would say it is necessary for building anything beyond simple components. There are lots of tutorials about it, but it can be tricky to really grok until you come across it in your own codings.
Edit:
Just wanted to note that you will need to take a look at which modules you are defining your directives and controller in and set your module dependencies accordingly.

Calling parent directive method from child directive through attrs

This is the scenario :
<export-team>
<ul>
<li>
<button buy-ticket="{{data}}" buy-callback="onBuyTicket()">buy</button>
</li>
<li>
<button buy-ticket="{{data}}" buy-callback="onBuyTicket()">buy</button>
</li>
</ul>
</export-team>
The buyTicket directive
(function() {
'use strict';
angular
.module('myApp')
.directive('buyTicket', buyTicket);
/** #ngInject */
function buyTicket($parse, ngDialog, authService, APPCONFIG, $rootScope, shareToken, contestsFactory, shareCurrentTicket, shareIdSession, shareSessionAAMS, $location) {
var vm = this;
var directive = {
restrict: 'A',
link : function(scope, element, attributes) {
var buyCompatible = attributes['buyCompatible'];
function addZero(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
var buyTicket = function(contest) {
var d = new Date();
var y = d.getFullYear();
var m = addZero(d.getMonth()+1);
var day = addZero(d.getDate());
var h = addZero(d.getHours());
var min = addZero(d.getMinutes());
var s = addZero(d.getSeconds());
var date = ''+y+m+day+h+min+s+'';
var transactionId = $rootScope.TRANSACTIONID;
var currentTOKEN = shareToken.get();
var data = {
idSessione:currentTOKEN, // ->TOKEN
userAgent:navigator.userAgent,
sessioneAAMS:contest.aams_session_id,
gameId:APPCONFIG.GAME_ID,
transactionId:transactionId,
dateTime:date,
buyIn:contest.buy_in
}
var dialogLoading = ngDialog.open({
closeByDocument : false,
closeByEscape : false,
showClose : false,
id : 'ft-modal-loading',
controller: ['$scope', function($scope){
$scope.bodyUrl = 'app/components/modals/body/loading.html';
$scope.title = 'Acquisto Ticket';
$scope.error = 'Il sistema sta procedendo all\'acquisto del ticket';
}]
});
contestsFactory.buyTicket(data).success(function(response){
dialogLoading.close();
if (response.esito == "0") {
if (!buyCompatible) {
shareCurrentTicket.set(response.ticketSogei);
shareSessionAAMS.set(contest.aams_session_id);
shareIdSession.set(contest.id_session);
$location.path('my-contests/'+contest.id_contest+'/'+contest.contest_status);
}
} else {
var message = response.descrizione;
var ids = ngDialog.getOpenDialogs();
var dialogError = ngDialog.open({
id : "ft-modal-error-2",
controller: ['$scope', function($scope){
$scope.bodyUrl = 'app/components/modals/body/error.html';
$scope.title = 'Errore';
$scope.error = message;
}]
});
}
})
.error(function(){
var dialogErrorNotEndled = ngDialog.close('ft-modal-loading');
ngDialog.open({
id : 'ft-modal-error',
controller: ['$scope', function($scope){
$scope.bodyUrl = 'app/components/modals/body/error.html';
$scope.title = 'Errore';
$scope.error = 'Il servizio non รจ attualmente disponibile';
}]
});
})
}
var openConfirmBuyTicket = function(contest) {
contest = JSON.parse(contest);
if (ngDialog.isOpen('ft-modal-contest-detail')) {
ngDialog.close('ft-modal-contest-detail');
};
if (!authService.isLogged()) {
ngDialog.open({
controller: ['$scope', function($scope){
$scope.bodyUrl = 'app/components/modals/body/not_logged.html';
$scope.title = 'Spiacenti';
$scope.error = 'Devi essere loggato per poter partecipare ad un contest';
}]
});
} else {
ngDialog.openConfirm({
controller: ['$scope', function($scope){
$scope.title = 'CONFERMA';
$scope.bodyUrl = 'app/components/modals/body/confirm_buy.html';
$scope.contest_name = contest.name_contest;
$scope.buy_in = contest.buy_in;
$scope.currency = APPCONFIG.CURRENCY_SYMBOL;
}],
}).then(function (confirm) {
buyTicket(contest);
}, function(reject) {
});
}
}
element.on('click', function(e){
var contest = attributes['buyTicket'];
openConfirmBuyTicket(contest);
})
}
};
return directive;
}
})();
The export directive
(function() {
'use strict';
angular
.module('myApp')
.directive('exportTeam', exportTeam);
/** #ngInject */
function exportTeam(contestsFactory, ngDialog, APPCONFIG, formatDateFactory) {
var vm = this;
var directive = {
restrict: 'AE',
transclude: true,
controller : function($scope) {
$scope.test = function() {
alert('hey');
}
},
link : function(scope, element, attributes) {
element.on('click', function(e){
var ticket = attributes['exportTeam'];
var id_session = attributes['idsession'];
scope.openExportTeamDialog(ticket, id_session, false);
})
scope.openExportTeamDialog = function(ticket, aams_session_id, afterSave) {
ngDialog.open({
id : 'ft-modal-exportTeam-detail',
className : 'ngdialog ngdialog-theme-default ft-dialog-exportTeam',
controller: ['$scope', 'contestsFactory', 'APPCONFIG', function($scope, contestsFactory, APPCONFIG){
$scope.title = "Aggiungi contest compatibili";
$scope.bodyUrl = 'app/components/modals/body/exportTeam.html';
$scope.contentLoading = true;
$scope.currency = APPCONFIG.CURRENCY_SYMBOL;
$scope.afterSave = afterSave;
$scope.CompatibleContests = [];
contestsFactory.getCompatibleContests(ticket).then(function(response){
angular.forEach(response.data[0], function(item, i){
var multientryOptions = [];
if(item.multientry > 1) {
item.isMultientry = false;
var n = parseInt(item.multientry);
for (i = 1; i <= n; i++) {
multientryOptions.push({
text : i+" team",
value : i
})
}
item.multientryOptions = multientryOptions;
item.multientryOptionSelected = multientryOptions[0];
}else{
item.isMultientry = true;
};
})
$scope.CompatibleContests = response.data[0];
$scope.contentLoading = false;
})
}]
});
}
scope.openExportTeamDialog('N3E94100A725F9QG', 'M3E921013C6DCFCT', false);
}
};
return directive;
}
})();
The buy-ticket directive makes an http call, on the response i want to be able to call the onBuyTicket method of the <export> directive.
I'm trying to understand the best way to do that.
Thanks everyone
This sample show to you how can call an function from your directive
In this sample you can see we just insert data in our directive, and then we handle the data and other action in the directive.
var app = angular.module("app", []);
app.controller("ctrl", function ($scope) {
$scope.dataFromYourController = [
{ name: "Concert Jennifer", value: 200 },
{ name: "007", value: 100 }
];
})
.directive("export", function () {
var template = "<div>" +
"<ul>" +
"<li ng-repeat=\"array in arrays\">" +
"<button ng-click=\"onBuyTicket()\">buy Ticket {{array.name}}</button><hr>" +
"</li>" +
"</ul>" +
"</div>";
return {
restrict: "E",
template: template,
scope: {
data: "="
},
link: function (scope, elem, attrs, ngModel) {
scope.arrays = scope.data;
scope.onBuyTicket = function () {
alert("calling function from directive");
}
}
};
})
<!doctype html>
<html ng-app="app" ng-controller="ctrl">
<head>
</head>
<body>
<h1>call action from your directive</h1>
<export data="dataFromYourController"></export>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</body>
</html>
#eesdil
var directive = {
restrict: 'AE',
transclude: true,
controller : function($scope) {
$scope.onBuyTicket = function() {
alert('hey');
}
}
}
Ho can I call that from the buy-ticket directive ?
Use the $parent
<button buy-ticket="{{data}}" buy-callback="$parent.onBuyTicket()">buy</button>
So the expor directive something like:
var directive = {
restrict: 'AE',
template: '<ng-tansclude></ng-transclude>',
transclude: true,
controller : function($scope) {
$scope.onBuyTicket = function() {
alert('hey');
}
}
}
UPDATED:
see the plunker:
https://plnkr.co/edit/fmyJ4oPLvTiI0TzO7h1b?p=preview
It really depends what you can call and what you cannot based on the scopes... here if you would remove the scope from the export directive would work without the $parent also as export would share the same scope as the parent (main view)
The best way to communicate events from a child directive to a parent directive (or controller) is to use the $emit method of the scope.
What you want to do is take an ng-click event, get additional information with an $http call, and $emit an event with the additional information to be used by your parent directive (or controller).
HTML
<button buy-ticket="data" ng-click="onBuyTicket()">buy</button>
The directive:
angular.module("myApp").directive("buyTicket", function($http) {
function linkFn(scope,elem,attrs) {
scope.onBuyTicket = function() {
var buyData = scope.$eval(attrs.buyTicket);
var url = someFunction(buyData);
$http.get(url).then (function (response) {
var httpData = response.data;
scope.$emit("buyTicket.click", buyData, httpData);
});
};
};
return {
restrict: "AE",
link: linkFn
};
});
In the parent controller:
$scope.$on("buyTicket.click", function (buyData, httpData) {
console.log(buyData);
console.log(httpData);
});
Notice that I used the $eval method to get the data from the variable named by the buy-ticket attribute.
When choosing a name for the event, I recommend including the name of the directive in the event's name. It makes it clear the source of the event and is unlikely to be duplicated elsewhere.

Resources