Angular upload file directive - angularjs

I need a directive for upload file (brwose to choose file, and get it as binary data ) , in my angular application.
I use it a lot of times, so it has to be generic.
What is the best way to do it?

https://github.com/flowjs/ng-flow
This works fine.Of course some wrapping directive can be made at your side.

directive code
.directive("fileReaderGallery", ['background', function (background) {
return {
restrict: "E",
scope: true,
templateUrl: "templates/directive.html",
replace: true,
link: function ($scope, el, attrs) {
var input = null,
drag_image_gallery = el.find('.drag_image_gallery');
$scope.dragging = false;
$scope.fileselected = false;
$scope.uploaded = false;
$scope.uploading = false;
$scope.image = null;
$scope.clearFileReader = function () {
if (!attrs.styling || !input) return false;
$scope.formTitan.elementStyles[attrs.styling][$scope.styledItem.pt]['background-image'] = '';
$scope.formSelected[attrs.styling].imageFile = null;
$scope.formSelected[attrs.styling].isImage = false;
input.value = '';
$scope.fileselected = false;
$scope.imageName = '';
};
var readfiles = function (files) {
var reader, bg;
if (files && files[0]) {
if (files.length > 1) {
return console.log("Select single file only");
}
reader = new FileReader;
reader.onload = function (e) {
if (files[0].type.indexOf('image/') !== -1) {
if (e.target.result) {
bg = {
'background-image': e.target.result,
'background-repeat': 'repeat',
'background-position': 'top',
'background-size': ''
};
$scope.uploading = true;
$scope.$apply(function () {
background.add(angular.copy(bg));
$scope.current.dcBGImage = angular.copy(bg);
$scope.imageName = files[0].name;
$scope.image = e.target.result;
$scope.fileselected = true;
console.log(files[0])
});
}
} else {
return console.log('Please select an Image');
}
};
return reader.readAsDataURL(files[0]);
}
};
$scope.clickUpload = function () {
el.find('.bg-file-reader').click();
};
drag_image_gallery[0].ondragover = function () {
$scope.dragging = true;
//drag_image_gallery.addClass('ondragover');
$scope.$digest();
return false;
};
drag_image_gallery[0].ondragleave = function () {
$scope.dragging = false;
$scope.$digest();
//drag_image_gallery.removeClass('ondragover');
};
drag_image_gallery[0].ondrop = function (e) {
$scope.dragging = false;
$scope.$digest();
//drag_image_gallery.removeClass('ondragover');
e.preventDefault();
readfiles(e.dataTransfer.files);
};
el.find('.bg-file-reader').on('change', function () {
readfiles(this.files);
});
}
};
}]);
html template code
<div class="row upload_image text-center">
<div class="drag_image drag_image_gallery row text-center" ng-class=" {ondragover:dragging}">
Drag an Image here
</div>
OR
<div class="row text-center choose_computer">
<button ng-click="clickUpload()" class="btn btn-default">Choose from your computer</button>
<input type="file" class="bg-file-reader upload" name="gallery"/>
</div>
</div>
directive with drag and drop and chose file both functionality

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();
});

Define and Watch a variable according to windowidth

I'm struggling creating a directive to assign and update a variable, that compares to the window width, and updates with resize.
I need the variable as compared to using CSS because I will work it into ng-if. What am I doing wrong? Here is the code:
var app = angular.module('miniapp', []);
function AppController($scope) {}
app.directive('widthCheck', function ($window) {
return function (scope, element, attr) {
var w = angular.element($window);
scope.$watch(function () {
return {
'w': window.innerWidth
};
}, function (newValue, oldValue, desktopPlus, isMobile) {
scope.windowWidth = newValue.w;
scope.desktopPlus = false;
scope.isMobile = false;
scope.widthCheck = function (windowWidth, desktopPlus) {
if (windowWidth > 1399) {
scope.desktopPlus = true;
}
else if (windowWidth < 769) {
scope.isMobile = true;
}
else {
scope.desktopPlus = false;
scope.isMoblie = false;
}
}
}, true);
w.bind('resize', function () {
scope.$apply();
});
}
});
JSfiddle here: http://jsfiddle.net/h8m4eaem/2/
As mentioned in this SO answer it's probably better to bind to the window resize event with-out watch. (Similar to Mr. Berg's answer.)
Something like in the demo below or in this fiddle should work.
var app = angular.module('miniapp', []);
function AppController($scope) {}
app.directive('widthCheck', function($window) {
return function(scope, element, attr) {
var width, detectFalse = {
desktopPlus: false,
isTablet: false,
isMobile: false
};
scope.windowWidth = $window.innerWidth;
checkSize(scope.windowWidth); // first run
//scope.desktopPlus = false;
//scope.isMoblie = false; // typo
//scope.isTablet = false;
//scope.isMobile = false;
function resetDetection() {
return angular.copy(detectFalse);
}
function checkSize(windowWidth) {
scope.detection = resetDetection();
if (windowWidth > 1000) { //1399) {
scope.detection.desktopPlus = true;
} else if (windowWidth > 600) {
scope.detection.isTablet = true;
} else {
scope.detection.isMobile = true;
}
}
angular.element($window).bind('resize', function() {
width = $window.innerWidth;
scope.windowWidth = width
checkSize(width);
// manuall $digest required as resize event
// is outside of angular
scope.$digest();
});
}
});
.fess {
border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="miniapp" ng-controller="AppController">
<div width-check class="fess" resize>
window.width: {{windowWidth}}
<br />desktop plus: {{detection.desktopPlus}}
<br />mobile: {{detection.isMobile}}
<br />tablet: {{detection.isTablet}}
<br/>
</div>
</div>
scope.widthCheck is assigned an anonymous function and RE-ASSIGNED that same function each time this watcher fires. I also notice it's never called.
you should move that piece out of the $watch and call the function when the watcher fires
There's no need for a watch, you're already binding to resize. Just move your logic in there. And as Jun Duan said you continously create the funciton. Here's the change:
app.directive('widthCheck', function ($window) {
return function (scope, element, attr) {
function widthCheck(windowWidth) {
if (windowWidth > 1399) {
scope.desktopPlus = true;
}
else if (windowWidth < 769) {
scope.isMobile = true;
}
else {
scope.desktopPlus = false;
scope.isMoblie = false;
}
}
windowSizeChanged();
angular.element($window).bind('resize', function () {
windowSizeChanged();
scope.$apply();
});
function windowSizeChanged(){
scope.windowWidth = $window.innerWidth;
scope.desktopPlus = false;
scope.isMobile = false;
widthCheck(scope.windowWidth);
}
}
});
jsFiddle: http://jsfiddle.net/eqd3zu0j/

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

AngularJS UI Bootstrap dismiss() won´t work

Let me start by saying that I am very new to angular.
I have tried to setup a modal ui which works fine. But when I click the "cancel" button on the interface nothing happens.
This is my code:
(function () {
var domainName = 'TournamentCategory';
angular.module('tournamentCategories').controller("CategoriesController",
['$scope', '$modal', 'guidGenerator', 'eventBus', domainName + "Service",
'TournamentCategoryModelFactory',
function ($scope, $modal, guidGenerator, eventBus, domainService, modelFactory)
{$scope.openTournamentTree = function () {
var modalInstance = $modal.open({
templateUrl: 'TournamentTreeModal.html',
controller: 'TreeController',
size: 'wide-90',
resolve: {
}
});
modalInstance.result.then(function (selectedItem) {
//$scope.selected = selectedItem;
}, function () {
//$log.info('Modal dismissed at: ' + new Date());
});
};
$scope.ok = function () {
modalInstance.close();
}
$scope.cancel = function () {
modalInstance.dismiss('cancel');
};
}]);
})();
The HTML:
<script type="text/ng-template" id="TournamentTreeModal.html">
<div class="modal-header">
</div>
<div class="modal-body">
<div class="include-tree" ng-include="'tournament-tree.html'"></div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">GEM</button>
<button ng-click="cancel()" class="btn btn-warning" type="button" data-dismiss="modal">LUK, uden at gemme</button>
</div>
This is my "treeController"
angular.module('tournamentTree').controller("TreeController", ['$scope', 'guidGenerator', function ($scope, guidGenerator) {
$scope.allMatches = [];
$scope.finalMatch = [createNewMatch()];
$scope.tierCount = 1;
$scope.previousMatchTier = 0;
$scope.deletePreviousMatches = function (parentMatch) {
for (var i in parentMatch.previousMatches) {
var previousMatch = parentMatch.previousMatches[i];
_.remove($scope.allMatches, function (match) { return match.id == previousMatch.id });
}
parentMatch.previousMatches = [];
}
$scope.addMatches = function (thisMatch) {
if (thisMatch && !thisMatch.previousMatches)
thisMatch.previousMatches = [];
if (thisMatch && thisMatch.previousMatches.length == 0) {
thisMatch.previousMatches.push(createNewMatch(thisMatch));
thisMatch.previousMatches.push(createNewMatch(thisMatch));
}
}
$scope.addMatchTier = function () {
for (var i in $scope.allMatches) {
var match = $scope.allMatches[i];
if (match.previousMatches.length == 0) {
$scope.addMatches(match);
}
}
$scope.tierCount++;
}
$scope.previousMatchTier = function () {
for (var i in $scope.allMatches) {
var previous;
if (previous.allMatches.length == i) {
previous = $scope.allMatches[i] - $scope.allMatches[i - 1];
}
}
}
$scope.removeMatchTier = function () {
//if (confirm('Er du sikker på, at du vil slette det yderste niveau af turneringstræet?')) {
var matchesToDelete = [];
for (var i in $scope.allMatches) {
var match = $scope.allMatches[i];
if (match.previousMatches.length == 0) {
matchesToDelete.push(match.nextMatch);
//$scope.deletePreviousMatches(match.nextMatch);
}
}
for (var i in matchesToDelete) {
$scope.deletePreviousMatches(matchesToDelete[i]);
}
$scope.tierCount--;
//}
}
$scope.hasPreviousMatches = function (match) {
return match && match.previousMatches && match.previousMatches.length > 0;
}
$scope.moveWinnerToNextMatch = function (match, winnerName) {
match.nextMatch.setPlayerName(match.id, winnerName);
}
function createNewMatch(nextMatch) {
var newMatch = {};
newMatch.tier = nextMatch && nextMatch.tier ? nextMatch.tier + 1 : 1;
newMatch.id = guidGenerator.newGuid();
newMatch.player1 = "";
newMatch.player2 = "";
newMatch.nextMatch = nextMatch;
newMatch.previousMatches = [];
newMatch.setPlayerName = function(matchId, playerName) {
for (var i in newMatch.previousMatches)
{
if (newMatch.previousMatches[i].id == matchId)
{
if (i == 0)
newMatch.player1 = playerName;
else
newMatch.player2 = playerName;
}
}
}
$scope.allMatches.push(newMatch);
return newMatch;
}
}]);
$dismiss is already available on modal scope so in template raplace cancel() with $dimiss() http://angular-ui.github.io/bootstrap/#/modal
That needs to go in your modals controller 'TreeController'
angular.module('tournamentCategories').controller('TreeController', function($scope, $modalInstance) {
$scope.ok = function () {
modalInstance.close();
}
$scope.cancel = function () {
modalInstance.dismiss('cancel');
};
});
I have an example in this plunker

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