I've found an example: Here. But after the error message fades, the content keeps the space occupied by the message.
Here's my code. In my view:
<div data-ng-show="showError" ng-class="{fade:doFade}" class="alert alert-danger"> {{authmessage}}</div>
And in my controller:
$scope.userLogin = function () {
http.post('/api/login/', $scope.formData).then(function (response) {
$rootScope.message = response.config.data.email;
$location.url('/Projects');
}).catch(function(response){
$scope.showError = false;
$scope.doFade = false;
$scope.showError = true;
$scope.authmessage = response.data;
$timeout(function(){
$scope.doFade = true;
}, 2500); });
} ;
Please set $scope.showError to false in $timeout function
$timeout(function(){
$scope.doFade = true;
$scope.showError = false;
}, 2500);
Related
I am trying to ticker in AngularJs, I am getting error as
shift() is not a function
Please suggest.
$scope.boxes = [];
$scope.moving = false;
$scope.moveLeft = function() {
$scope.moving = true;
$timeout($scope.switchFirst, 1000);
};
$scope.switchFirst = function() {
$scope.boxes.push($scope.boxes.shift());
$scope.moving = false;
$scope.$apply();
};
$interval($scope.moveLeft, 2000);
Update
The following is my controller code
$scope.memoryMap = [];
$scope.loading = true;
myService.getInfo(function(metrics) {
if (metrics) {
$scope.memoryMap = metrics.memoryMap;
}
});
$scope.moving = false;
$scope.moveLeft = function() {
$scope.moving = true;
$timeout($scope.switchFirst, 1000);
};
$scope.switchFirst = function() {
$scope.memoryMap.push($scope.memoryMap.splice(0, 1)[0]);
$scope.moving = false;
$scope.$apply();
};
$interval($scope.moveLeft, 2000);
Trying to reset password through email. After clicking reset link in email and directed to reset page, console is able to output the "$scope.username" value, however right next line it throws error as "username" not defined. Error Screenshot
Not sure where went wrong. Thanks.
.controller('resetCtrl', function(User, $routeParams, $scope, $timeout, $location) {
app = this;
app.hide = true;
User.resetUser($routeParams.token).then(function(data) {
if (data.data.success) {
app.hide = false;
app.successMsg = 'Please enter a new password';
$scope.username = data.data.user.username;
} else {
app.errorMsg = data.data.message;
}
});
app.savePassword = function(regData, valid, confirmed) {
app.errorMsg = false;
app.disabled = true;
app.loading = true;
console.log($scope.username); // test output
app.regData.username = $scope.username;
User.savePassword(app.regData).then(function(data) {
app.loading = false;
if (data.data.success) {
app.successMsg = data.data.message + '...Redirecting';
$timeout(function() {
$location.path('/login');
}, 2000);
} else {
app.disabled = false;
app.errorMsg = data.data.message;
}
});
}
});
Declare $scope.username outside, that will solve your problem, since you are using controller as syntax, it is better to have it as app.username.
.controller('resetCtrl', function(User, $routeParams, $scope, $timeout, $location) {
app = this;
app.hide = true;
app.username = '';
User.resetUser($routeParams.token).then(function(data) {
if (data.data.success) {
app.hide = false;
app.successMsg = 'Please enter a new password';
app.username = data.data.user.username;
} else {
app.errorMsg = data.data.message;
}
});
app.savePassword = function(regData, valid, confirmed) {
app.errorMsg = false;
app.disabled = true;
app.loading = true;
app.regData.username = app.username;
User.savePassword(app.regData).then(function(data) {
app.loading = false;
if (data.data.success) {
app.successMsg = data.data.message + '...Redirecting';
$timeout(function() {
$location.path('/login');
}, 2000);
} else {
app.disabled = false;
app.errorMsg = data.data.message;
}
});
}
});
I'm trying to change the text of a button to "Loading" while an api request is processing within AngularJS. I use a scope variable of buttontext. I noticed the variable gets updated in the browser developer console but doesn't get updated in the ng-inspector panel in Chrome. I can't figure out why the button text doesn't change. I figure it has to do with the fact the corresponding variable is inside a controller function. Here's my AngularJS code:
angular.module('twitterApp', ['ngResource'])
.controller('mainController', function($scope, TwitterUser, KloutUser) {
$scope.buttontext = "Get Insight";
$scope.error = false;
$scope.users = [];
$scope.getResult = function(id){
$scope.users = [];
$scope.buttontext = "Loading";
$scope.loading = true;
TwitterUser.get({
id: id
}, function(user) {
if(user.error) {
$scope.error = true;
$scope.message = "Validation Error please fill the user_id or screen_name field";
}else{
if(!user.errors){
console.log(user);
$scope.users.push(user);
$scope.error = false;
}else{
$scope.error = true;
$scope.message = user.errors[0]['message']+" - "+user.errors[0]['code'] ;
}
}
}).$promise.then(function(user){
KloutUser.get({
id: user.id
}, function(userkloutscore) {
if(!userkloutscore) {
console.log('An error occurred. No Klout score returned.');
}else{
$scope.kloutscore = userkloutscore.score;
var score_stringified = JSON.stringify(userkloutscore);
console.log('The Klout API response: ' + score_stringified);
}
});
});
$scope.buttontext = "Get Insight";
};
$scope.removeUser = function(index){
$scope.users.splice(index, 1);
};
});
And here's the button HTML:
<a class="btn btn-primary" role="button" ng-click="getResult(id)">{{ buttontext }}</a>
You need to put
$scope.buttontext = "Get Insight";
Inside the promise callback, because at this moment your flow is:
Change text to "Loading"
Make the API request (and wait in background)
Change text to "Get Insight" inmediately
So your text makes the change from "Get Insight" -> "Loading" -> "Get Insight" so rapidly that it goes unnoticed.
Move the last line to inside your callback/promise logic, like:
angular.module('twitterApp', ['ngResource'])
.controller('mainController', function($scope, TwitterUser, KloutUser) {
$scope.buttontext = "Get Insight";
$scope.error = false;
$scope.users = [];
$scope.getResult = function(id){
$scope.users = [];
$scope.buttontext = "Loading";
$scope.loading = true;
TwitterUser.get({
id: id
}, function(user) {
if(user.error) {
$scope.error = true;
$scope.message = "Validation Error please fill the user_id or screen_name field";
}else{
if(!user.errors){
console.log(user);
$scope.users.push(user);
$scope.error = false;
}else{
$scope.error = true;
$scope.message = user.errors[0]['message']+" - "+user.errors[0]['code'] ;
}
}
}).$promise.then(function(user){
KloutUser.get({
id: user.id
}, function(userkloutscore) {
if(!userkloutscore) {
console.log('An error occurred. No Klout score returned.');
}else{
$scope.kloutscore = userkloutscore.score;
var score_stringified = JSON.stringify(userkloutscore);
console.log('The Klout API response: ' + score_stringified);
}
$scope.buttontext = "Get Insight";
});
});
};
$scope.removeUser = function(index){
$scope.users.splice(index, 1);
};
});
But you still need to handle some error scenarios.
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.
The below directive checks/loads the template with value "pass, fail, required".
The conditions are
if(parent value == required){
1. if the value is true --> $scope.msg = "fail" --> loads the template with {{msg}} value
2. if the value is false --> $scope.msg = "pass" --> loads the template with {{msg}} value
}
In detail,
loading Template: [showerror.html]
<div>{{msg}}</div>
Directive Call:
<div show-error="obj"></div>
(obj contains as obj.error and obj.required )
Directive:
angular.module("dsf").directive('showError', [
function () {
'use strict';
return {
scope: {
obj: '=showError'
},
link: function ($scope) {
$scope.showError = {};
$scope.msg = "";
function setTemplate(filename) {
$scope.showError.template = 'app/path/' + filename + '.html';
}
$scope.$watch(function () {
if ($scope.obj.required === true) {
if ($scope.obj.error === false) {
$scope.msg = "Pass";
} else if ($scope.obj.error === "required") {
$scope.msg = "Required";
} else if ($scope.obj.error === true) {
$scope.msg = "fail";
}
} else {
if ($scope.obj.error === true) {
$scope.msg = "fail";
} else if ($scope.obj.error === false) {
$scope.msg = "Pass";
}
}
setTemplate("showerror");
});
},
template: '<div ng-include="showError.template"></div>'
};
}
]);
As i am new to jasmine test, how can i write the test for this directive? any suggestions?
Ok. I have written the unit test for this directive. What is the wrong now?
describe('showError', function () {
'use strict';
var compile, $scope, element;
beforeEach(module('dsf'));
beforeEach(inject(function ($compile, $rootScope) {
element = angular.element('<div show-error="obj"></div>');
$scope = $rootScope.$new();
compile = function (obj) {
$scope.obj = obj;
$compile(element)($scope);
$scope.$digest();
};
}));
it('updates the element when obj validation changes', function () {
var obj;
obj = {};
$scope = compile(obj);
$scope.apply(function () {
obj.required = true;
obj.error = true;
});
expect($scope.obj.msg).toContain('fail');
$scope.apply(function () {
obj.required = true;
obj.error = false;
});
expect($scope.obj.msg).toContain('Pass');
$scope.apply(function () {
obj.required = true;
obj.error = "required";
});
expect($scope.obj.msg).toContain('Required');
$scope.apply(function () {
obj.required = false;
obj.error = true;
});
expect($scope.obj.msg).toContain('Pass');
$scope.apply(function () {
obj.required = false;
obj.error = false;
});
expect($scope.obj.msg).toContain('fail');
});
});
I am getting error:
undefined is not an object (evaluating $scopr.apply) error
The command you're looking for is $scope.$apply() not $scope.apply().