Skip to content
This repository was archived by the owner on Aug 30, 2021. It is now read-only.

Commit 01bd98b

Browse files
committed
Merge pull request #759 from Gym/client-side-validation
client-side form validation with ng-messages.
2 parents 05355b9 + 8015476 commit 01bd98b

20 files changed

+274
-106
lines changed

bower.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"angular-bootstrap": "~0.13",
1212
"angular-ui-utils": "bower",
1313
"angular-ui-router": "~0.2",
14-
"angular-file-upload": "1.1.5"
14+
"angular-file-upload": "1.1.5",
15+
"angular-messages": "1.3.17"
1516
},
1617
"resolutions": {
1718
"angular": "~1.3"

config/assets/default.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module.exports = {
1111
'public/lib/angular/angular.js',
1212
'public/lib/angular-resource/angular-resource.js',
1313
'public/lib/angular-animate/angular-animate.js',
14+
'public/lib/angular-messages/angular-messages.js',
1415
'public/lib/angular-ui-router/release/angular-ui-router.js',
1516
'public/lib/angular-ui-utils/ui-utils.js',
1617
'public/lib/angular-bootstrap/ui-bootstrap-tpls.js',

config/assets/production.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module.exports = {
1111
'public/lib/angular/angular.min.js',
1212
'public/lib/angular-resource/angular-resource.min.js',
1313
'public/lib/angular-animate/angular-animate.min.js',
14+
'public/lib/angular-messages/angular-messages.min.js',
1415
'public/lib/angular-ui-router/release/angular-ui-router.min.js',
1516
'public/lib/angular-ui-utils/ui-utils.min.js',
1617
'public/lib/angular-bootstrap/ui-bootstrap-tpls.min.js',

modules/articles/client/controllers/articles.client.controller.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@ angular.module('articles').controller('ArticlesController', ['$scope', '$statePa
66
$scope.authentication = Authentication;
77

88
// Create new Article
9-
$scope.create = function () {
9+
$scope.create = function (isValid) {
10+
$scope.error = null;
11+
12+
if (!isValid) {
13+
$scope.$broadcast('show-errors-check-validity', 'articleForm');
14+
15+
return false;
16+
}
17+
1018
// Create new Article object
1119
var article = new Articles({
1220
title: this.title,
@@ -43,7 +51,15 @@ angular.module('articles').controller('ArticlesController', ['$scope', '$statePa
4351
};
4452

4553
// Update existing Article
46-
$scope.update = function () {
54+
$scope.update = function (isValid) {
55+
$scope.error = null;
56+
57+
if (!isValid) {
58+
$scope.$broadcast('show-errors-check-validity', 'articleForm');
59+
60+
return false;
61+
}
62+
4763
var article = $scope.article;
4864

4965
article.$update(function () {

modules/articles/client/views/create-article.client.view.html

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,18 @@
33
<h1>New Article</h1>
44
</div>
55
<div class="col-md-12">
6-
<form name="articleForm" class="form-horizontal" data-ng-submit="create()" novalidate>
6+
<form name="articleForm" class="form-horizontal" ng-submit="create(articleForm.$valid)" novalidate>
77
<fieldset>
8-
<div class="form-group">
9-
<label class="control-label" for="title">Title</label>
10-
<div class="controls">
11-
<input name="title" type="text" data-ng-model="title" id="title" class="form-control" placeholder="Title">
8+
<div class="form-group" show-errors>
9+
<label for="title">Title</label>
10+
<input name="title" type="text" ng-model="title" id="title" class="form-control" placeholder="Title" required>
11+
<div ng-messages="articleForm.title.$error" role="alert">
12+
<p class="help-block error-text" ng-message="required">Article title is required.</p>
1213
</div>
1314
</div>
1415
<div class="form-group">
15-
<label class="control-label" for="content">Content</label>
16-
<div class="controls">
17-
<textarea name="content" data-ng-model="content" id="content" class="form-control" cols="30" rows="10" placeholder="Content"></textarea>
18-
</div>
16+
<label for="content">Content</label>
17+
<textarea name="content" ng-model="content" id="content" class="form-control" cols="30" rows="10" placeholder="Content"></textarea>
1918
</div>
2019
<div class="form-group">
2120
<input type="submit" class="btn btn-default">

modules/articles/client/views/edit-article.client.view.html

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,18 @@
33
<h1>Edit Article</h1>
44
</div>
55
<div class="col-md-12">
6-
<form name="articleForm" class="form-horizontal" data-ng-submit="update()" novalidate>
6+
<form name="articleForm" class="form-horizontal" ng-submit="update(articleForm.$valid)" novalidate>
77
<fieldset>
8-
<div class="form-group">
9-
<label class="control-label" for="title">Title</label>
10-
<div class="controls">
11-
<input name="title" type="text" data-ng-model="article.title" id="title" class="form-control" placeholder="Title" required>
8+
<div class="form-group" show-errors>
9+
<label for="title">Title</label>
10+
<input name="title" type="text" ng-model="article.title" id="title" class="form-control" placeholder="Title" required>
11+
<div ng-messages="articleForm.title.$error" role="alert">
12+
<p class="help-block error-text" ng-message="required">Article title is required.</p>
1213
</div>
1314
</div>
1415
<div class="form-group">
15-
<label class="control-label" for="content">Content</label>
16-
<div class="controls">
17-
<textarea name="content" data-ng-model="article.content" id="content" class="form-control" cols="30" rows="10" placeholder="Content"></textarea>
18-
</div>
16+
<label for="content">Content</label>
17+
<textarea name="content" ng-model="article.content" id="content" class="form-control" cols="30" rows="10" placeholder="Content"></textarea>
1918
</div>
2019
<div class="form-group">
2120
<input type="submit" value="Update" class="btn btn-default">

modules/articles/tests/client/articles.client.controller.tests.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797
expect(scope.article).toEqualData(mockArticle);
9898
}));
9999

100-
describe('$scope.craete()', function () {
100+
describe('$scope.create()', function () {
101101
var sampleArticlePostData;
102102

103103
beforeEach(function () {
@@ -119,7 +119,7 @@
119119
$httpBackend.expectPOST('api/articles', sampleArticlePostData).respond(mockArticle);
120120

121121
// Run controller functionality
122-
scope.create();
122+
scope.create(true);
123123
$httpBackend.flush();
124124

125125
// Test form inputs are reset
@@ -136,7 +136,7 @@
136136
message: errorMessage
137137
});
138138

139-
scope.create();
139+
scope.create(true);
140140
$httpBackend.flush();
141141

142142
expect(scope.error).toBe(errorMessage);
@@ -154,7 +154,7 @@
154154
$httpBackend.expectPUT(/api\/articles\/([0-9a-fA-F]{24})$/).respond();
155155

156156
// Run controller functionality
157-
scope.update();
157+
scope.update(true);
158158
$httpBackend.flush();
159159

160160
// Test URL location to new object
@@ -167,7 +167,7 @@
167167
message: errorMessage
168168
});
169169

170-
scope.update();
170+
scope.update(true);
171171
$httpBackend.flush();
172172

173173
expect(scope.error).toBe(errorMessage);

modules/core/client/app/config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
var ApplicationConfiguration = (function () {
55
// Init module configuration options
66
var applicationModuleName = 'mean';
7-
var applicationModuleVendorDependencies = ['ngResource', 'ngAnimate', 'ui.router', 'ui.bootstrap', 'ui.utils', 'angularFileUpload'];
7+
var applicationModuleVendorDependencies = ['ngResource', 'ngAnimate', 'ngMessages', 'ui.router', 'ui.bootstrap', 'ui.utils', 'angularFileUpload'];
88

99
// Add a new vertical module
1010
var registerModule = function (moduleName, dependencies) {

modules/core/client/css/core.css

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@
1212
.x-ng-cloak {
1313
display: none !important;
1414
}
15-
.ng-invalid.ng-dirty {
16-
border-color: #FA787E;
17-
}
18-
.ng-valid.ng-dirty {
19-
border-color: #78FA89;
20-
}
2115
.header-profile-image {
2216
opacity: 0.8;
2317
height: 28px;
@@ -33,3 +27,12 @@ a:hover .header-profile-image {
3327
padding-top: 11px !important;
3428
padding-bottom: 11px !important;
3529
}
30+
.error-text {
31+
display: none;
32+
}
33+
.has-error .help-block.error-text {
34+
display: block;
35+
}
36+
.has-error .help-inline.error-text {
37+
display: inline;
38+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
'use strict';
2+
3+
/**
4+
* Edits by Ryan Hutchison
5+
* Credit: https://github.com/paulyoder/angular-bootstrap-show-errors */
6+
7+
angular.module('core')
8+
.directive('showErrors', ['$timeout', '$interpolate', function ($timeout, $interpolate) {
9+
var linkFn = function (scope, el, attrs, formCtrl) {
10+
var inputEl, inputName, inputNgEl, options, showSuccess, toggleClasses,
11+
initCheck = false,
12+
showValidationMessages = false,
13+
blurred = false;
14+
15+
options = scope.$eval(attrs.showErrors) || {};
16+
showSuccess = options.showSuccess || false;
17+
inputEl = el[0].querySelector('.form-control[name]') || el[0].querySelector('[name]');
18+
inputNgEl = angular.element(inputEl);
19+
inputName = $interpolate(inputNgEl.attr('name') || '')(scope);
20+
21+
if (!inputName) {
22+
throw 'show-errors element has no child input elements with a \'name\' attribute class';
23+
}
24+
25+
var reset = function () {
26+
return $timeout(function () {
27+
el.removeClass('has-error');
28+
el.removeClass('has-success');
29+
showValidationMessages = false;
30+
}, 0, false);
31+
};
32+
33+
scope.$watch(function () {
34+
return formCtrl[inputName] && formCtrl[inputName].$invalid;
35+
}, function (invalid) {
36+
return toggleClasses(invalid);
37+
});
38+
39+
scope.$on('show-errors-check-validity', function (event, name) {
40+
if (angular.isUndefined(name) || formCtrl.$name === name) {
41+
initCheck = true;
42+
showValidationMessages = true;
43+
44+
return toggleClasses(formCtrl[inputName].$invalid);
45+
}
46+
});
47+
48+
scope.$on('show-errors-reset', function (event, name) {
49+
if (angular.isUndefined(name) || formCtrl.$name === name) {
50+
return reset();
51+
}
52+
});
53+
54+
toggleClasses = function (invalid) {
55+
el.toggleClass('has-error', showValidationMessages && invalid);
56+
if (showSuccess) {
57+
return el.toggleClass('has-success', showValidationMessages && !invalid);
58+
}
59+
};
60+
};
61+
62+
return {
63+
restrict: 'A',
64+
require: '^form',
65+
compile: function (elem, attrs) {
66+
if (attrs.showErrors.indexOf('skipFormGroupCheck') === -1) {
67+
if (!(elem.hasClass('form-group') || elem.hasClass('input-group'))) {
68+
throw 'show-errors element does not have the \'form-group\' or \'input-group\' class';
69+
}
70+
}
71+
return linkFn;
72+
}
73+
};
74+
}]);

modules/users/client/controllers/admin/user.client.controller.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@ angular.module('users.admin').controller('UserController', ['$scope', '$state',
1919
}
2020
};
2121

22-
$scope.update = function () {
22+
$scope.update = function (isValid) {
23+
if (!isValid) {
24+
$scope.$broadcast('show-errors-check-validity', 'userForm');
25+
26+
return false;
27+
}
28+
2329
var user = $scope.user;
2430

2531
user.$update(function () {

modules/users/client/controllers/authentication.client.controller.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,15 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$stat
1212
$location.path('/');
1313
}
1414

15-
$scope.signup = function () {
15+
$scope.signup = function (isValid) {
16+
$scope.error = null;
17+
18+
if (!isValid) {
19+
$scope.$broadcast('show-errors-check-validity', 'userForm');
20+
21+
return false;
22+
}
23+
1624
$http.post('/api/auth/signup', $scope.credentials).success(function (response) {
1725
// If successful we assign the response to the global user model
1826
$scope.authentication.user = response;
@@ -24,7 +32,15 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$stat
2432
});
2533
};
2634

27-
$scope.signin = function () {
35+
$scope.signin = function (isValid) {
36+
$scope.error = null;
37+
38+
if (!isValid) {
39+
$scope.$broadcast('show-errors-check-validity', 'userForm');
40+
41+
return false;
42+
}
43+
2844
$http.post('/api/auth/signin', $scope.credentials).success(function (response) {
2945
// If successful we assign the response to the global user model
3046
$scope.authentication.user = response;

modules/users/client/controllers/settings/change-password.client.controller.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@ angular.module('users').controller('ChangePasswordController', ['$scope', '$http
55
$scope.user = Authentication.user;
66

77
// Change user password
8-
$scope.changeUserPassword = function () {
8+
$scope.changeUserPassword = function (isValid) {
99
$scope.success = $scope.error = null;
1010

11+
if (!isValid) {
12+
$scope.$broadcast('show-errors-check-validity', 'passwordForm');
13+
14+
return false;
15+
}
16+
1117
$http.post('/api/users/password', $scope.passwordDetails).success(function (response) {
1218
// If successful show success message and clear form
19+
$scope.$broadcast('show-errors-reset', 'passwordForm');
1320
$scope.success = true;
1421
$scope.passwordDetails = null;
1522
}).error(function (response) {

modules/users/client/controllers/settings/edit-profile.client.controller.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,24 @@ angular.module('users').controller('EditProfileController', ['$scope', '$http',
66

77
// Update a user profile
88
$scope.updateUserProfile = function (isValid) {
9-
if (isValid) {
10-
$scope.success = $scope.error = null;
11-
var user = new Users($scope.user);
9+
$scope.success = $scope.error = null;
1210

13-
user.$update(function (response) {
14-
$scope.success = true;
15-
Authentication.user = response;
16-
}, function (response) {
17-
$scope.error = response.data.message;
18-
});
19-
} else {
20-
$scope.submitted = true;
11+
if (!isValid) {
12+
$scope.$broadcast('show-errors-check-validity', 'userForm');
13+
14+
return false;
2115
}
16+
17+
var user = new Users($scope.user);
18+
19+
user.$update(function (response) {
20+
$scope.$broadcast('show-errors-reset', 'userForm');
21+
22+
$scope.success = true;
23+
Authentication.user = response;
24+
}, function (response) {
25+
$scope.error = response.data.message;
26+
});
2227
};
2328
}
2429
]);

0 commit comments

Comments
 (0)