Skip to content

Commit 74e97fa

Browse files
author
Akos Vandra
committed
add support for feedback icons and problem list
1 parent 23bfa9f commit 74e97fa

File tree

2 files changed

+217
-11
lines changed

2 files changed

+217
-11
lines changed

README.md

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,61 @@ To force the validity check, broadcast the `show-errors-check-validity` event.
6161
```javascript
6262
$scope.save = function() {
6363
$scope.$broadcast('show-errors-check-validity');
64-
64+
6565
if ($scope.userForm.$valid) {
6666
// save the user
6767
}
6868
}
6969
```
7070

71+
Show Feedback Icons
72+
---
73+
Feedback icons are supported through the ShowErrorIcons directive.
74+
75+
#### Example
76+
77+
```html
78+
<form name="userForm">
79+
<div class="form-group has-feedback" show-errors>
80+
<input type="text" name="firstName" ng-model="firstName" ng-required />
81+
<span class="form-control-feedback" show-error-icons></span>
82+
</div>
83+
<input type="submit" ng-click="save()" />
84+
</form>
85+
```
86+
87+
Show Validation error hints
88+
---
89+
Validation error hints are supported through the ShowErrorHint directive.
90+
The span that has this added directive will be shown if the specified validation error occured. This integrates well with the ui-validator module for example.
91+
92+
Do not forget to add the css fix from below if you intend to show a bullet point list
93+
94+
#### Example
95+
96+
```css
97+
li.help-block {
98+
display: list-item
99+
}
100+
101+
```
102+
103+
```html
104+
<form name="userForm">
105+
<div class="form-group" show-errors>
106+
<input type="text" name="firstName" ng-model="firstName" ng-required
107+
ui-validate="{ length: '$value.length >= 4', 'unique': 'userExists($value)'}"/>
108+
<ul>
109+
<li class="help-block" show-error-hint="unique">It is not unique!</li>
110+
<li class="help-block" show-error-hint="length">Too short!</li>
111+
<li class="help-block" show-error-hint="required">This field is required!</li>
112+
<li class="help-block" show-error-hint>All errors {{userForm.firstName.$error}}</li>
113+
</ul>
114+
</div>
115+
<input type="submit" ng-click="save()" />
116+
</form>
117+
```
118+
71119
Reset
72120
---
73121
If you have functionality to reset your form, you can broadcast the 'show-errors-reset' event to remove any errors on the form elements.
@@ -91,7 +139,7 @@ $scope.reset = function() {
91139

92140
Show Valid Entries
93141
---
94-
It's also possible to let the user know when they have entered valid values by applying the 'show-success' class that Bootstrap provides.
142+
It's also possible to let the user know when they have entered valid values by applying the 'show-success' class that Bootstrap provides.
95143
You can either apply this globally or on an element by element basis.
96144

97145
##### Globally
@@ -129,8 +177,8 @@ If your HTML code doesn't have a form-group class, the form group check can be s
129177

130178
Custom Trigger
131179
---
132-
By default, the validation is not performed until the `blur` event is trigger on the input
133-
element. However, there are some scenarios where this is not desirable, so it's possible to
180+
By default, the validation is not performed until the `blur` event is trigger on the input
181+
element. However, there are some scenarios where this is not desirable, so it's possible to
134182
override this with the `trigger` option.
135183

136184
##### By Input Element
@@ -149,7 +197,7 @@ app.config(['showErrorsConfigProvider', function(showErrorsConfigProvider) {
149197
showErrorsConfigProvider.trigger('keypress');
150198
}]);
151199
```
152-
200+
153201
## Development
154202

155203
### Install Development Dependencies
@@ -163,7 +211,7 @@ bower install
163211
### Compile and Run the Unit Tests
164212
Just type `grunt` in the command line to compile and run the karma unit tests once.
165213

166-
If you want to have grunt watch for any file changes and automatically compile and run the karma
214+
If you want to have grunt watch for any file changes and automatically compile and run the karma
167215
unit tests, then run the following command:
168216
```
169217
grunt karma:continuous:start watch

src/showErrors.js

Lines changed: 163 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323
return showSuccess;
2424
};
2525
linkFn = function(scope, el, attrs, formCtrl) {
26-
var blurred, inputEl, inputName, inputNgEl, options, showSuccess, toggleClasses, trigger;
27-
blurred = false;
26+
var inputEl, inputName, inputNgEl, options, showSuccess, toggleClasses, trigger;
2827
options = scope.$eval(attrs.showErrors);
2928
showSuccess = getShowSuccess(options);
3029
trigger = getTrigger(options);
@@ -35,13 +34,15 @@
3534
throw "show-errors element has no child input elements with a 'name' attribute and a 'form-control' class";
3635
}
3736
inputNgEl.bind(trigger, function() {
38-
blurred = true;
37+
scope.$apply(function() {
38+
formCtrl[inputName].$blurred = true;
39+
})
3940
return toggleClasses(formCtrl[inputName].$invalid);
4041
});
4142
scope.$watch(function() {
4243
return formCtrl[inputName] && formCtrl[inputName].$invalid;
4344
}, function(invalid) {
44-
if (!blurred) {
45+
if (!formCtrl[inputName].$blurred) {
4546
return;
4647
}
4748
return toggleClasses(invalid);
@@ -53,7 +54,7 @@
5354
return $timeout(function() {
5455
el.removeClass('has-error');
5556
el.removeClass('has-success');
56-
return blurred = false;
57+
return formCtrl[inputName].$blurred = false;
5758
}, 0, false);
5859
});
5960
return toggleClasses = function(invalid) {
@@ -78,6 +79,163 @@
7879
}
7980
]);
8081

82+
showErrorsModule.directive('showErrorIcons', [
83+
'$timeout', 'showErrorsConfig', '$interpolate', function($timeout, showErrorsConfig, $interpolate) {
84+
var getShowSuccess, linkFn;
85+
getShowSuccess = function(options) {
86+
var showSuccess;
87+
showSuccess = showErrorsConfig.showSuccess;
88+
if (options && (options.showSuccess != null)) {
89+
showSuccess = options.showSuccess;
90+
}
91+
return showSuccess;
92+
};
93+
94+
linkFn = function(scope, el, attrs, formCtrl) {
95+
var inputEl, inputName, inputNgEl, options, showSuccess, toggleClasses, trigger;
96+
options = scope.$eval(attrs.showErrors);
97+
showSuccess = getShowSuccess(options);
98+
99+
inputEl = el[0].parentNode.querySelector('.form-control[name]');
100+
inputNgEl = angular.element(inputEl);
101+
inputName = $interpolate(inputNgEl.attr('name') || '')(scope);
102+
if (!inputName) {
103+
throw "show-error-icons element's parent has no child input elements with a 'name' attribute and a 'form-control' class";
104+
}
105+
106+
scope.$watch(function() {
107+
return formCtrl[inputName] ^ formCtrl[inputName].$invalid ^ formCtrl[inputName].$blurred;
108+
}, function() {
109+
return toggleClasses(formCtrl[inputName].$blurred, formCtrl[inputName].$invalid);
110+
})
111+
112+
scope.$on('show-errors-check-validity', function() {
113+
return toggleClasses(true, formCtrl[inputName].$invalid);
114+
});
115+
116+
scope.$on('show-errors-reset', function() {
117+
return $timeout(function() {
118+
toggleClasses(false)
119+
}, 0, false);
120+
});
121+
return toggleClasses = function(show, invalid) {
122+
el.toggleClass('ng-hide', !show)
123+
el.toggleClass('glyphicon', true)
124+
el.toggleClass('glyphicon-remove', invalid);
125+
if (showSuccess) {
126+
return el.toggleClass('glyphicon-ok', !invalid);
127+
}
128+
};
129+
};
130+
return {
131+
restrict: 'A',
132+
require: '^form',
133+
compile: function(elem, attrs) {
134+
if (!elem.parent().children(".form-control[name]")) {
135+
throw "show-error-icons must be applied on an element whose parent contains an input[name] element!"
136+
}
137+
138+
return linkFn;
139+
}
140+
};
141+
}
142+
]);
143+
144+
showErrorsModule.directive('showErrorHint', [
145+
'$timeout', 'showErrorsConfig', '$interpolate', function($timeout, showErrorsConfig, $interpolate) {
146+
var getShowSuccess, linkFn;
147+
getShowSuccess = function(options) {
148+
var showSuccess;
149+
showSuccess = showErrorsConfig.showSuccess;
150+
if (options && (options.showSuccess != null)) {
151+
showSuccess = options.showSuccess;
152+
}
153+
return showSuccess;
154+
};
155+
156+
linkFn = function(formgroup) {
157+
return function(scope, el, attrs, formCtrl) {
158+
var inputEl, inputName, inputNgEl, toggleClasses, validator;
159+
160+
inputEl = formgroup[0].querySelector('input.form-control[name]');
161+
inputNgEl = angular.element(inputEl)
162+
inputName = $interpolate(inputNgEl.attr('name') || '')(scope);
163+
validator = scope.showErrorHint
164+
165+
if (!inputName) {
166+
throw "show-error-hint element's parent has no child input elements with a 'name' attribute and a 'form-control' class";
167+
}
168+
169+
var update = function(force) {
170+
return function() {
171+
if (validator) {
172+
return scope.toggleClasses((formCtrl[inputName].$blurred || force) && formCtrl[inputName].$error[validator])
173+
} else {
174+
var errors = formCtrl[inputName].$error || {}
175+
var keys = Object.keys(errors)
176+
var errorCount = keys.filter(function(e) { return errors[e]; }).length
177+
178+
return scope.toggleClasses((formCtrl[inputName].$blurred || force) && errorCount > 0)
179+
}
180+
}
181+
}
182+
183+
if (validator) {
184+
scope.$watch(function() {
185+
return formCtrl[inputName].$error[validator]
186+
}, update(false))
187+
} else {
188+
scope.$watch(function() {
189+
return JSON.stringify(formCtrl[inputName].$error)
190+
}, update(false))
191+
}
192+
193+
scope.$watch(function() {
194+
return formCtrl[inputName].$blurred
195+
}, update(false))
196+
197+
scope.$on('show-errors-check-validity', function() {
198+
return update(true)()
199+
});
200+
201+
scope.$on('show-errors-reset', function() {
202+
return $timeout(function() {
203+
scope.toggleClasses(true)
204+
}, 0, false);
205+
});
206+
207+
scope.toggleClasses = function(show) {
208+
el.toggleClass('ng-hide', !show)
209+
};
210+
211+
return scope.toggleClasses
212+
};
213+
}
214+
215+
return {
216+
restrict: 'A',
217+
require: '^form',
218+
scope: {
219+
showErrorHint: '@'
220+
},
221+
222+
compile: function(elem, attrs) {
223+
224+
//Find first ancestor with a form control
225+
while(elem && !elem[0].querySelector(".form-control[name]")) {
226+
elem = elem.parent()
227+
}
228+
229+
if (!elem) {
230+
throw "show-error-hint must be applied on an element whose parent contains an input[name] element!"
231+
}
232+
233+
return linkFn(elem);
234+
}
235+
};
236+
}
237+
]);
238+
81239
showErrorsModule.provider('showErrorsConfig', function() {
82240
var _showSuccess, _trigger;
83241
_showSuccess = false;

0 commit comments

Comments
 (0)