Angular ng-valid-email "blocked" with intervening custom directive - angularjs

I am implementing a custom directive in Angular 1.4 which creates a inner input element within a form. I am having problems with the validation.
1) When make the email invalid (add a '.' to the end of the string in the input box), the ng-invalid-email class does not get propagated upwards to the form (possibly because the vc-input directive is in the way?). So it is reporting it as "required invalid" as opposed to "email invalid".
2) If I change vc-input to just input (and remove closing vc-input element), it works as expected (try in snippet).
Is there some way I can make sure this validity passes through the way it's supposed to?
'use strict';
var myApp = angular.module('myApp', []);
myApp
.directive('vcInput', ['$compile',
function ($compile) {
return {
restrict: 'E',
link: function ($scope, elem, attrs) {
var toDash = function(str){
return str.replace(/([A-Z])/g, function($1){return '-'+$1.toLowerCase();});
};
// returned glyph string could be glyphicon or fontawesome
var mapIcon = function(iconName) {
var glyphName = '';
if (iconName === 'search') {
glyphName = 'glyphicon glyphicon-search';
}
else if (iconName === 'user') {
glyphName = 'glyphicon glyphicon-user';
}
else if (iconName === 'password') {
glyphName = 'glyphicon glyphicon-lock';
}
else if (iconName === 'token') {
glyphName = 'glyphicon glyphicon-tags';
glyphName = 'fa fa-tags';
}
return glyphName;
};
var leadIcon = elem.attr('lead-icon');
elem.removeAttr('lead-icon');
var leadClass;
var spanClass = '';
if (leadIcon !== undefined) {
leadClass = mapIcon(leadIcon);
if (leadClass) {
spanClass = 'left-inner-addon';
}
else {
spanClass = 'left-inner-no-icon';
}
}
var icon = '';
if (leadClass !== undefined) {
icon = '<i class="' + leadClass + '"></i>';
}
var elementData = elem[0].outerHTML;
var startIndex = elementData.indexOf('<vc-input') + 9;
var stopIndex = elementData.indexOf('>') ;
var allAtt = elementData.substring(startIndex, stopIndex);
angular.forEach(attrs, function(value, key) {
if (key.lastIndexOf('$',0) && key !== 'leadIcon') {
elem.removeAttr(toDash(key));
}
});
var span1 = '<span class="' + spanClass + '">';
var span2 = '</span>';
var html = $compile(span1 + icon + '<input ' + allAtt + '/>' + span2)($scope);
elem.html(html);
}
};
}])
;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<form name="myForm">
<label>Email:
<vc-input type="email" name="input" ng-model="email.text" ng-init="email.text='joe#aol.com'" required></vc-input>
</label>
<div role="alert">
<span class="error" ng-show="myForm.input.$error.required">
Required!</span>
<span class="error" ng-show="myForm.input.$error.email">
Not valid email!</span>
</div>
<samp>text = {{email.text}}</samp><br/>
<samp>myForm.input.$valid = {{myForm.input.$valid}}</samp><br/>
<samp>myForm.input.$error = {{myForm.input.$error}}</samp><br/>
<samp>myForm.$valid = {{myForm.$valid}}</samp><br/>
<samp>myForm.$error.required = {{!!myForm.$error.required}}</samp><br/>
<samp>myForm.$error.email = {{!!myForm.$error.email}}</samp><br/>
</form>
</div>

Related

Scope from controller does not pass to directive

I have a html like this :
<div id="create-group" ng-controller="groupCreateController">
<div id="container">
<h1>Create group</h1>
<div class="row">
<div class="col-md-4"><input placeholder="Group Name.." ng-model="group.name"></div>
<div class="col-md-8">
<label>Group Description : </label>
<textarea ng-model="group.description"> </textarea>
</div>
</div>
<br/>
<div class="row">
<div class="col-sm-6">
<usermgr-permission-list group="group"></usermgr-permission-list>
<button type="button" class="btn btn-md btn-primary" ng-click="btnSave_click($event)">SAVE</button>
</div>
<div class="col-sm-6">
<usermgr-user-list group="group"></usermgr-user-list>
</div>
</div>
</div>
</div>
My controller is :
(function (module) {
'use strict';
module.controller('groupCreateController', function ($scope, $rootScope, $routeParams, $location, userGroupService, $mdDialog) {
$scope.group = [];
$scope.init = function () {
if ($routeParams.hasOwnProperty('id')) {
//edit mode
// $scope.trans.heading = 'Edit Release';
// $scope.trans.saveBtn = 'Save';
var id = parseInt($routeParams.id);
getUserGroup(id);
} else {
$scope.group[0].id = 0;
$scope.group[0].permissions = [];
$scope.assignedPermissions = [];
$scope.enrolledUsers = [];
$scope.group[0].users = [];
$scope.group[0].name = '';
$scope.group[0].description = '';
}
};
function getUserGroup(id) {
userGroupService.getbyid(id).then(function (info) {
if (info !== undefined && info.id === id) {
$scope.group[0].id = info.id;
$scope.group[0].name = info.name;
$scope.group[0].description = info.description;
console.log($scope.group);
// $rootScope.$broadcast('rCube-user-mgt-users-list', info.id);
// $rootScope.$broadcast('rCube-user-mgt-permissions-list', info.id);
}
else {
}
}).catch(function (exception) {
console.error(exception);
});
}
$scope.init();
});
})(angular.module('r-cube-user-mgt.user-group'));
I have two custom directives in the first block of code for user permissions and users. The group scope that i pass with the directive does not contain the values i put in the getUserGroup(id) function. The group name and group description shows up so the scope.group in the controller is filled, however thats not the case once i pass it to my directives. here is the directives code as well :
permissions list :
(function (module) {
'use strict';
module.directive('usermgrPermissionList', function () {
return {
restrict: 'E',
scope:{
group: '='
},
controller: function ($scope, permissionService) {
$scope.updatedPermissions=[];
console.log($scope.group); //it doesnt have the values from the controller ..
if (!$scope.group.hasOwnProperty('permissions')) {
$scope.group.permissions = [];
}
function getData() {
console.log("inside getDAta for permission list" + $scope.group.id;
permissionService.getPermissionsFiltered($scope.group.id).then(function (info) {
if (info && info.length > 0) {
console.log(info);
$scope.group.permissions = info.map(function (a, index, array) {
return {
id: a.id,
name: a.name,
description: a.description,
assigned: a.assigned
};
});
}
}).catch(function (exception) {
console.error(exception);
});
} //end of getData()
$scope.init = function () {
getData();
};
$scope.init();
},
templateUrl: 'r-cube-user-mgt/permission/list/list.tpl.html'
};
});
})(angular.module('r-cube-user-mgt.permission'));
can anyone help?
you cannot assign property to an array like this $scope.group.id = 0;
either make $scope.group object
$scope.group = {};
or add properties to an index
$scope.group = [];
$scope.init = function () {
if ($routeParams.hasOwnProperty('id')) {
//edit mode
// $scope.trans.heading = 'Edit Release';
// $scope.trans.saveBtn = 'Save';
var id = parseInt($routeParams.id);
getUserGroup(id);
} else {
$scope.group[0].id = 0;
$scope.group[0].permissions = [];
$scope.assignedPermissions = [];
$scope.enrolledUsers = [];
$scope.group[0].users = [];
$scope.group[0].name = '';
$scope.group[0].description = '';
}
};
So I solved the issue by adding broadcast to send the id when the directive loads. This worked!
in the Group controller i add broadcast and send the group.id
function getUserGroup(id) {
userGroupService.getbyid(id).then(function (info) {
if (info !== undefined && info.id === id) {
$scope.group.id = info.id;
$scope.group.name = info.name;
$scope.group.description = info.description;
console.log($scope.group);
$rootScope.$broadcast(rCubeTopics.userMgtPermissionLoadData, $scope.group.id);
}
}).catch(function (exception) {
console.error(exception);
});
}
and in the permission directive get that broadcast :
$scope.$on(rCubeTopics.userMgtPermissionLoadData, function (event, id) {
console.log($scope.group.id);
getData();
});

Angular scope.watch not working in directive

I have a angular directive that watches a list, and then creates a custom select when the list is edited. I have this working on one page, but it refuses to work on another. I dont know why - but it looks like the watch is not catching when the list has changed.
I have reproduced the error here - http://codepen.io/jagdipa/pen/Ramjez - can someone please help?!
angular.module('MyApp',['ngMaterial', 'ngMessages', 'material.svgAssetsCache'])
.controller('AppCtrl', function($scope) {
$scope.clearValue = function() {
$scope.myModel = undefined;
};
$scope.save = function() {
alert('Form was valid!');
};
var Titles =[{"Title":"Mr"},{"Title":"Master"},{"Title":"Miss"},{"Title":"Mrs"}];
$scope.titles = Titles;
});
module MyApp.Directives {
interface TcSelectListScope extends ng.IScope {
sourceList: any[];
sourceListKey: string;
sourceListValue: string;
}
export class TcSelectListController {
static $inject = [];
constructor() {
}
}
export class TcSelectList {
public restrict = "A";
public require = ["ngModel", "^form", '^^mdInputContainer', "select"];
public controller = TcSelectListController;
public controllerAs = 'selectController';
public scope = {
sourceList: "="
}
constructor(private $compile: ng.ICompileService) {
}
public compile(tElement, tAttributes) {
var $compile = this.$compile;
var _cacheService = this.cacheService;
return function postLink(scope, element, attrs, controller) {
var ngModelCtrl = controller[0];
var mdInputContainerCtrl = controller[2];
var selectCtrl = controller[3];
console.log(selectCtrl);
/*if (scope.sourceList == undefined)
{
scope.sourceList = [];
}*/
scope.$watch(scope.sourceList, scope.onModelChanged, true);
//scope.$watchCollection(scope.sourceList, scope.onModelChanged);
scope.onModelChanged = () => {
console.log('tc select list directive 2');
console.log(scope.sourceList);
if (attrs.sourceListKey == undefined) {
throw ("The source-list-key needs to be defined for tc-select-list");
}
if (attrs.sourceListValue == undefined) {
throw ("The source-list-value needs to be defined for tc-select-list");
}
var html = undefined;
html = buildSelect();
html = markSelected(html);
element.append(html);
}
element.on("click", function () {
mdInputContainerCtrl.setHasValue(true);
});
element.bind("blur", function () {
if (ngModelCtrl.$viewValue == undefined) {
mdInputContainerCtrl.setHasValue(false);
}
});
element.bind("change", function () {
mdInputContainerCtrl.setHasValue(true);
});
function buildSelect() {
var html = ``;
angular.forEach(scope.sourceList, (val, index) => {
var itemKey = scope.sourceList[index][attrs.sourceListKey];
var itemValue = scope.sourceList[index][attrs.sourceListValue];
var selected = ``;
html += `<option label="` + itemValue +
`" value="` + itemKey +
`" ` + selected +
` >` + itemValue + ` < /option>`;
});
return html;
}
function markSelected(html) {
if (ngModelCtrl.$modelValue != undefined && ngModelCtrl.$modelValue != null) {
html = html.replace(`value="` + ngModelCtrl.$modelValue + `"`,
`value="` + ngModelCtrl.$modelValue + `" selected`)
}
return html
}
}
}
static factory() {
var directive = ($compile, cacheService) => new TcSelectList($compile, cacheService);
directive.$inject = ["$compile"];
return directive;
}
}
angular.module("MyApp").directive("tcSelectList", TcSelectList.factory());
}
<div ng-controller="AppCtrl" layout="column" layout-align="center center" style="min-height: 300px;" ng-cloak="" class="selectdemoValidations" ng-app="MyApp">
<form name="myForm">
<p>Note that invalid styling only applies if invalid and dirty</p>
<md-input-container class="md-block">
<label>Favorite Number</label>
<md-select name="myModel" ng-model="myModel" required="">
<md-option value="1">One 1</md-option>
<md-option value="2">Two</md-option>
</md-select>
<div class="errors" ng-messages="myForm.myModel.$error" ng-if="myForm.$dirty">
<div ng-message="required">Required</div>
</div>
</md-input-container>
<div layout="row">
<md-button ng-click="clearValue()" ng-disabled="!myModel" style="margin-right: 20px;">Clear</md-button>
<md-button ng-click="save()" ng-disabled="myForm.$invalid" class="md-primary" layout="" layout-align="center end">Save</md-button>
</div>
<md-input-container class="no-errors">
<label>{{translations["Title"]}}</label>
<div class="tc-select">
<select name="title"
tc-select-list
ng-model="myModel.Title"
source-list="titles"
source-list-key="Title"
source-list-value="Title"></select>
</div>
</md-input-container>
<br/>
{{titles}}
<br/>
Title = {{myModel.Title}}
</forms>
</div>
I found the answer. Turns out the following line doesnt work
scope.$watch(scope.sourceList, (newVal) => {
Changing it to the following sorted the problem out
scope.$watch('sourceList', (newVal) => {

Dynamically creating multiselect Dropdown in AngularJs

Hi i have one multiselect dropdown in angular which invokes directive. I want to create 5 dropdown which will refer same directive and controller, but for each dropdown there will be different action method in MVC. Your help can save my life
Controller
var myApp = angular.module('myModule', ['angularjs-dropdown-multiselect']);
myApp.controller('multiselect', ['$scope', '$http', function ($scope, $http) {
$scope.lobsSelected = [];
$scope.lobs = [];
$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 }
);
});
})
}])
Directive
'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: '&',
},
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);
//var obj = getPropertyForObject(option, settings.idProp);
$event.stopImmediatePropagation();
};
$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 () {
debugger;
$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) {
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))" 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);
}
UI
<div ng-app="myModule" ng-controller="multiselect">
<div ng-dropdown-multiselect="" extra-settings="dropdownSetting" options="lobs"
selected-model="lobsSelected" checkboxes="true">
</div>
</div>
Action Method n MVC Controller
public JsonResult GetALLLOB()
{
iMetricsEntities dbContext = new iMetricsEntities();
return new JsonResult
{
Data = dbContext.Levels.Where(objlevel => objlevel.N_ParentID == 0 && objlevel.B_Active && objlevel.N_LevelTypeID == 4),
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
if i create another div with same controller,directive it does not create 2nd dropdown. doing this it disturbs first dropdown as well...
The answer is
1.create another div inside ng-app "mymodule"
<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"
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>
2. write one more method within angular controller
$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 }
);
});
})
3. Write its corresponding method in MVC controller
public JsonResult GetAllProjects()
{
iMetricsEntities dbContext = new iMetricsEntities();
return new JsonResult
{
Data = dbContext.Levels.Where(objlevel => objlevel.N_ParentID == 0 && objlevel.B_Active && objlevel.N_LevelTypeID == 4),
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}

How to get selected value from ng-autocomplete directive to controller

I am using a directive for auto complete / auto suggest in angular Js taken from http://demo.jankuri.com/ngAutocomplete/. It is working fine getting the data from server and filtering it. But I am facing problem into select and use that select item from the auto complete.
Here is the code of directive what I am using for this...
app.factory('ngAutocompleteService', ['$http', function($http)
{
var self = this;
self.getData = function (url, keyword) {
return $http.get(url, { query: keyword });
};
return self;
}])
app.directive('ngAutocomplete', ['$timeout','$filter','ngAutocompleteService',
function($timeout, $filter, ngAutocompleteService)
{
'use strict';
var keys = {
left : 37,
up : 38,
right : 39,
down : 40,
enter : 13,
esc : 27
};
var setScopeValues = function (scope, attrs) {
scope.url = base_url+attrs.url || null;
scope.searchProperty = attrs.searchProperty || 'skills';
scope.maxResults = attrs.maxResults || 10;
scope.delay = parseInt(attrs.delay, 10) || 300;
scope.minLenth = parseInt(attrs.minLenth, 10) || 2;
scope.allowOnlyResults = scope.$eval(attrs.allowOnlyResults) || false;
scope.placeholder = attrs.placeholder || 'Search...';
};
var delay = (function() {
var timer = 0;
return function (callback, ms) {
$timeout.cancel(timer);
timer = $timeout(callback, ms);
};
})();
return {
restrict: 'E',
require: '?ngModel',
scope: true,
link: function(scope, element, attrs, ngModel) {
setScopeValues(scope, attrs);
scope.results = [];
scope.currentIndex = null;
scope.getResults = function () {
if (parseInt(scope.keyword.length, 10) === 0) scope.results = [];
if (scope.keyword.length < scope.minLenth) return;
delay(function() {
ngAutocompleteService.getData(scope.url, scope.keyword).then(function(resp) {
scope.results = [];
var filtered = $filter('filter')(resp.data, {skills: scope.keyword});
for (var i = 0; i < scope.maxResults; i++) {
scope.results.push(filtered[i]);
}
scope.currentIndex = 0;
if (scope.results.length) {
scope.showResults = true;
}
});
}, scope.delay);
};
scope.selectResult = function (r) {
scope.keyword = r.skills;
ngModel.$setViewValue(r.skills);
scope.ngModel = r.skills;
ngModel.$render();
scope.showResults = false;
};
scope.clearResults = function () {
scope.results = [];
scope.currentIndex = null;
};
scope.hoverResult = function (i) {
scope.currentIndex = i;
}
scope.blurHandler = function () {
$timeout(function() {
if (scope.allowOnlyResults) {
var find = $filter('filter')(scope.results, {skills: scope.keyword}, true);
if (!find.length) {
scope.keyword = '';
ngModel.$setViewValue('');
}
}
scope.showResults = false;
}, 100);
};
scope.keyupHandler = function (e) {
var key = e.which || e.keyCode;
if (key === keys.enter) {
scope.selectResult(scope.results[scope.currentIndex]);
}
if (key === keys.left || key === keys.up) {
if (scope.currentIndex > 0) {
scope.currentIndex -= 1;
}
}
if (key === keys.right || key === keys.down) {
if (scope.currentIndex < scope.maxResults - 1) {
scope.currentIndex += 1;
}
}
if (key === keys.esc) {
scope.keyword = '';
ngModel.$setViewValue('');
scope.clearResults();
}
};
},
template:
'<input type="text" class="form-control" ng-model="keyword" placeholder="{{placeholder}}" ng-change="getResults()" ng-keyup="keyupHandler($event)" ng-blur="blurHandler()" ng-focus="currentIndex = 0" autocorrect="off" autocomplete="off">' +
'<input type="hidden" ng-model="skillIdToBeRated">'+
'<div ng-show="showResults">' +
' <div ng-repeat="r in results | filter : {skills: keyword}" ng-click="selectResult(r)" ng-mouseover="hoverResult($index)" ng-class="{\'hover\': $index === currentIndex}">' +
' <span class="form-control">{{ r.skills }}</span>' +
' </div>' +
'</div>'
};
}]);
I am unable to get value which is selected by ng-click="selectResult(r)" function. The value is showing into text field but not getting it into controller.
I was also using the same directive for showing the auto complete text box. I have tried the following for getting the selected value from auto complete.
in HTML
<div ng-controller="Cntrl as cntrl">
<ng-autocomplete ng-model="cntrl.selectedValue" url="url" search-property="keyword" max-results="10" delay="300" min-length="2" allow-only-results="true"></ng-autocomplete>
</div>
in JavaScript
app.controller('Cntrl', function($scope, $http) {
var self = this;
self.selectedValue = '';
$scope.getSelectedValue = function(){
console.log(self.selectedValue);
}
});
I hope this may help you.
I ran into the same issue. I ended up just watching the property from the details attribute in the ng-autocomplete input and it works pretty well.
$scope.$watch(function() {
return vm.location_result;
}, function(location) {
if (location) {
vm.location_list.push(location);
vm.location = '';
}
});
Fiddle Example: http://jsfiddle.net/n3ztwucL/
GitHub Gist: https://gist.github.com/robrothedev/46e1b2a2470b1f8687ad

Angular when and how to release DOM to prevent memory leak?

Angular newbie here. I have this custom directive that wraps a table row to show information of a tag. When I click 'edit' button in the row, the directive template will be changed and allow the user to update the tag name, when I click 'apply' button, the row will be changed back with the updated tag name, or if I click 'cancel edit' button, the row will be changed back too without any updates. So the editTag and cancelEditTag event function goes like this:
scope.editTag = function() {
scope.originalTagName = scope.tag.name;
element.html(getTemplate(true));
$compile(element.contents())(scope);
};
scope.cancelEditTag = function() {
scope.tag.name = scope.originalTagName;
element.html(getTemplate(false));
$compile(element.contents())(scope);
scope.tagSubmitError = false;
scope.errorMessage = '';
};
Yet when profiling this app using Chrome dev tool, I realized while switching on and off 'edit mode' by clicking 'edit' and 'cancel edit' button, the memory usage keeps climbing up(about 0.1-0.2mb each time), I think I've got a memory leak here, my guess is that after $compile, the old DOM hasn't been released? If so, how should I deal with it? If this is not the case, what else could be the troublemaker? Or is it not a memory leak at all? For the full context, below is the full code for my directive:
app.directive('taginfo', function($compile ,$http) {
var directive = {};
directive.tagSubmitError = true;
directive.errorMessage = '';
directive.originalTagName = '';
directive.restrict = 'A';
directive.scope = {
tag : '=',
selectedTagIds : '=selected',
};
function getTemplate(isEditing) {
if (isEditing) {
return '<th><input type="checkbox" ng-click="selectTag()" ng-checked="selectedTagIds.indexOf(tag.id) != -1"></th>' +
'<th>' +
'<input type="text" class="form-control" ng-model="tag.name" placeholder="请输入标签名称">' +
'<div class="alert alert-danger" style="margin-top: 5px; " ng-show="tagSubmitError" ng-bind="errorMessage"></div>' +
'</th>' +
'<th><span class="label num-post"><%tag.num_items%></span></th>' +
'<th><button class="action-submit-edit" ng-click="submitEditTag()"><i class="icon-ok-2"></i></button> <button class="action-cancel-edit" ng-click="cancelEditTag()"><i class="icon-ban"></i></button></th>';
} else {
return '<th><input type="checkbox" ng-click="selectTag()" ng-checked="selectedTagIds.indexOf(tag.id) != -1"></th>' +
'<th><%tag.name%></th>' +
'<th><span class="label num-post"><%tag.num_items%></span></th>' +
'<th><button class="action-edit" ng-click="editTag()"><i class="icon-pencil"></i></button> <button class="action-delete"><i class="icon-bin"></i></button></th>';
}
}
directive.template = getTemplate(false);
directive.link = function(scope, element, attributes) {
scope.selectTag = function() {
var index = scope.selectedTagIds.indexOf(scope.tag.id);
if (index == -1) {
scope.selectedTagIds.push(scope.tag.id);
} else {
scope.selectedTagIds.splice(index, 1);
}
};
scope.submitEditTag = function() {
if (scope.tag.name.length === 0) {
scope.tagSubmitError = true;
scope.errorMessage = '请输入标签名称';
} else {
$http.post('/admin/posts/edit_tag', {'tagId': scope.tag.id, 'tagName': scope.tag.name}).success(function(data, status, headers, config) {
if (data.statusCode == 'error') {
scope.tagSubmitError = true;
scope.errorMessage = data.errorMessage;
} else if (data.statusCode == 'success') {
scope.tag.name = data.tag_name;
scope.tagSubmitError = false;
scope.errorMessage = '';
element.html(getTemplate(false));
$compile(element.contents())(scope);
}
});
}
};
scope.editTag = function() {
scope.originalTagName = scope.tag.name;
element.html(getTemplate(true));
$compile(element.contents())(scope);
};
scope.cancelEditTag = function() {
scope.tag.name = scope.originalTagName;
element.html(getTemplate(false));
$compile(element.contents())(scope);
scope.tagSubmitError = false;
scope.errorMessage = '';
};
};
return directive;
});
Any help will be appreciated, thanks in advance!
So, I've figured a way to not compile directive template dynamically, thus to avoid memory usage climbing up. That is to add a boolean flag named 'isEditMode' which will be used in ng-if to decide which DOM to show, and the source code is follows:
app.directive('taginfo', function($http, $animate, listService) {
var directive = {};
directive.editTagSubmitError = false;
directive.errorMessage = '';
directive.originalTagName = '';
directive.restrict = 'A';
directive.isEditMode = false;
directive.scope = {
tag : '=',
pagination : '=',
data : '='
};
directive.template = '<th><input type="checkbox" ng-click="selectTag()" ng-checked="data.selectedIds.indexOf(tag.id) != -1"></th>' +
'<th ng-if="isEditMode">' +
'<input type="text" class="form-control" ng-model="tag.name" placeholder="请输入标签名称">' +
'<div class="alert alert-danger" style="margin-top: 5px; " ng-show="editTagSubmitError" ng-bind="errorMessage"></div>' +
'</th>' +
'<th ng-if="!isEditMode"><%tag.name%></th>' +
'<th><span class="label num-posts"><%tag.num_items%></span></th>' +
'<th ng-if="isEditMode"><button class="action-submit-edit" ng-click="submitEditTag()"><i class="icon-ok-2"></i></button> <button class="action-cancel-edit" ng-click="cancelEditTag()"><i class="icon-ban"></i></button></th>' +
'<th ng-if="!isEditMode"><button class="action-edit" ng-click="editTag()"><i class="icon-pencil"></i></button> <button class="action-delete" ng-click="deleteTag()"><i class="icon-bin"></i></button></th>';
directive.link = function(scope, element, attributes) {
scope.selectTag = function() {
listService.selectEntry(scope.tag, scope.data);
};
scope.submitEditTag = function() {
if (!scope.tag.name) {
scope.editTagSubmitError = true;
scope.errorMessage = '请输入标签名称';
} else {
bootbox.confirm('是否确定修改标签名称为' + scope.tag.name +'?', function(result) {
if (result === true) {
$http.post('/admin/posts/edit_tag', {'tagId': scope.tag.id, 'tagName': scope.tag.name}).success(function(response, status, headers, config) {
if (response.statusCode == 'error') {
scope.editTagSubmitError = true;
scope.errorMessage = response.errorMessage;
} else if (response.statusCode == 'success') {
scope.isEditMode = false;
scope.tag.name = response.tag_name;
scope.editTagSubmitError = false;
scope.errorMessage = '';
$animate.removeClass(element, 'editing');
}
});
}
});
}
};
scope.editTag = function() {
scope.isEditMode = true;
scope.originalTagName = scope.tag.name;
if (!element.hasClass('editing')) {
element.addClass('editing');
}
};
scope.cancelEditTag = function() {
scope.isEditMode = false;
scope.tag.name = scope.originalTagName;
scope.editTagSubmitError = false;
scope.errorMessage = '';
};
scope.deleteTag = function() {
listService.deleteEntry(scope.tag, scope.tag.name, scope.data, '/admin/posts/delete_tag', scope.pagination, 'tag');
};
};
return directive;
});
This way, the directive template will not be compiled for editing/non-editing mode repeatedly but only show different DOM based on 'ng-if="isEditMode"'. It solved my problem. Yet I am still wondering if there's a way to remove memory leak for dynamic directive template compilation. Any thoughts would be appreciated.

Resources