Skip to content

add support for feedback icons and problem list #52

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 54 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,61 @@ To force the validity check, broadcast the `show-errors-check-validity` event.
```javascript
$scope.save = function() {
$scope.$broadcast('show-errors-check-validity');

if ($scope.userForm.$valid) {
// save the user
}
}
```

Show Feedback Icons
---
Feedback icons are supported through the ShowErrorIcons directive.

#### Example

```html
<form name="userForm">
<div class="form-group has-feedback" show-errors>
<input type="text" name="firstName" ng-model="firstName" ng-required />
<span class="form-control-feedback" show-error-icons></span>
</div>
<input type="submit" ng-click="save()" />
</form>
```

Show Validation error hints
---
Validation error hints are supported through the ShowErrorHint directive.
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.

Do not forget to add the css fix from below if you intend to show a bullet point list

#### Example

```css
li.help-block {
display: list-item
}

```

```html
<form name="userForm">
<div class="form-group" show-errors>
<input type="text" name="firstName" ng-model="firstName" ng-required
ui-validate="{ length: '$value.length >= 4', 'unique': 'userExists($value)'}"/>
<ul>
<li class="help-block" show-error-hint="unique">It is not unique!</li>
<li class="help-block" show-error-hint="length">Too short!</li>
<li class="help-block" show-error-hint="required">This field is required!</li>
<li class="help-block" show-error-hint>All errors {{userForm.firstName.$error}}</li>
</ul>
</div>
<input type="submit" ng-click="save()" />
</form>
```

Reset
---
If you have functionality to reset your form, you can broadcast the 'show-errors-reset' event to remove any errors on the form elements.
Expand All @@ -91,7 +139,7 @@ $scope.reset = function() {

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

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

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

##### By Input Element
Expand All @@ -149,7 +197,7 @@ app.config(['showErrorsConfigProvider', function(showErrorsConfigProvider) {
showErrorsConfigProvider.trigger('keypress');
}]);
```

## Development

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

If you want to have grunt watch for any file changes and automatically compile and run the karma
If you want to have grunt watch for any file changes and automatically compile and run the karma
unit tests, then run the following command:
```
grunt karma:continuous:start watch
Expand Down
168 changes: 163 additions & 5 deletions src/showErrors.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
return showSuccess;
};
linkFn = function(scope, el, attrs, formCtrl) {
var blurred, inputEl, inputName, inputNgEl, options, showSuccess, toggleClasses, trigger;
blurred = false;
var inputEl, inputName, inputNgEl, options, showSuccess, toggleClasses, trigger;
options = scope.$eval(attrs.showErrors);
showSuccess = getShowSuccess(options);
trigger = getTrigger(options);
Expand All @@ -35,13 +34,15 @@
throw "show-errors element has no child input elements with a 'name' attribute and a 'form-control' class";
}
inputNgEl.bind(trigger, function() {
blurred = true;
scope.$apply(function() {
formCtrl[inputName].$blurred = true;
})
return toggleClasses(formCtrl[inputName].$invalid);
});
scope.$watch(function() {
return formCtrl[inputName] && formCtrl[inputName].$invalid;
}, function(invalid) {
if (!blurred) {
if (!formCtrl[inputName].$blurred) {
return;
}
return toggleClasses(invalid);
Expand All @@ -53,7 +54,7 @@
return $timeout(function() {
el.removeClass('has-error');
el.removeClass('has-success');
return blurred = false;
return formCtrl[inputName].$blurred = false;
}, 0, false);
});
return toggleClasses = function(invalid) {
Expand All @@ -78,6 +79,163 @@
}
]);

showErrorsModule.directive('showErrorIcons', [
'$timeout', 'showErrorsConfig', '$interpolate', function($timeout, showErrorsConfig, $interpolate) {
var getShowSuccess, linkFn;
getShowSuccess = function(options) {
var showSuccess;
showSuccess = showErrorsConfig.showSuccess;
if (options && (options.showSuccess != null)) {
showSuccess = options.showSuccess;
}
return showSuccess;
};

linkFn = function(scope, el, attrs, formCtrl) {
var inputEl, inputName, inputNgEl, options, showSuccess, toggleClasses, trigger;
options = scope.$eval(attrs.showErrors);
showSuccess = getShowSuccess(options);

inputEl = el[0].parentNode.querySelector('.form-control[name]');
inputNgEl = angular.element(inputEl);
inputName = $interpolate(inputNgEl.attr('name') || '')(scope);
if (!inputName) {
throw "show-error-icons element's parent has no child input elements with a 'name' attribute and a 'form-control' class";
}

scope.$watch(function() {
return formCtrl[inputName] ^ formCtrl[inputName].$invalid ^ formCtrl[inputName].$blurred;
}, function() {
return toggleClasses(formCtrl[inputName].$blurred, formCtrl[inputName].$invalid);
})

scope.$on('show-errors-check-validity', function() {
return toggleClasses(true, formCtrl[inputName].$invalid);
});

scope.$on('show-errors-reset', function() {
return $timeout(function() {
toggleClasses(false)
}, 0, false);
});
return toggleClasses = function(show, invalid) {
el.toggleClass('ng-hide', !show)
el.toggleClass('glyphicon', true)
el.toggleClass('glyphicon-remove', invalid);
if (showSuccess) {
return el.toggleClass('glyphicon-ok', !invalid);
}
};
};
return {
restrict: 'A',
require: '^form',
compile: function(elem, attrs) {
if (!elem.parent().children(".form-control[name]")) {
throw "show-error-icons must be applied on an element whose parent contains an input[name] element!"
}

return linkFn;
}
};
}
]);

showErrorsModule.directive('showErrorHint', [
'$timeout', 'showErrorsConfig', '$interpolate', function($timeout, showErrorsConfig, $interpolate) {
var getShowSuccess, linkFn;
getShowSuccess = function(options) {
var showSuccess;
showSuccess = showErrorsConfig.showSuccess;
if (options && (options.showSuccess != null)) {
showSuccess = options.showSuccess;
}
return showSuccess;
};

linkFn = function(formgroup) {
return function(scope, el, attrs, formCtrl) {
var inputEl, inputName, inputNgEl, toggleClasses, validator;

inputEl = formgroup[0].querySelector('input.form-control[name]');
inputNgEl = angular.element(inputEl)
inputName = $interpolate(inputNgEl.attr('name') || '')(scope);
validator = scope.showErrorHint

if (!inputName) {
throw "show-error-hint element's parent has no child input elements with a 'name' attribute and a 'form-control' class";
}

var update = function(force) {
return function() {
if (validator) {
return scope.toggleClasses((formCtrl[inputName].$blurred || force) && formCtrl[inputName].$error[validator])
} else {
var errors = formCtrl[inputName].$error || {}
var keys = Object.keys(errors)
var errorCount = keys.filter(function(e) { return errors[e]; }).length

return scope.toggleClasses((formCtrl[inputName].$blurred || force) && errorCount > 0)
}
}
}

if (validator) {
scope.$watch(function() {
return formCtrl[inputName].$error[validator]
}, update(false))
} else {
scope.$watch(function() {
return JSON.stringify(formCtrl[inputName].$error)
}, update(false))
}

scope.$watch(function() {
return formCtrl[inputName].$blurred
}, update(false))

scope.$on('show-errors-check-validity', function() {
return update(true)()
});

scope.$on('show-errors-reset', function() {
return $timeout(function() {
scope.toggleClasses(true)
}, 0, false);
});

scope.toggleClasses = function(show) {
el.toggleClass('ng-hide', !show)
};

return scope.toggleClasses
};
}

return {
restrict: 'A',
require: '^form',
scope: {
showErrorHint: '@'
},

compile: function(elem, attrs) {

//Find first ancestor with a form control
while(elem && !elem[0].querySelector(".form-control[name]")) {
elem = elem.parent()
}

if (!elem) {
throw "show-error-hint must be applied on an element whose parent contains an input[name] element!"
}

return linkFn(elem);
}
};
}
]);

showErrorsModule.provider('showErrorsConfig', function() {
var _showSuccess, _trigger;
_showSuccess = false;
Expand Down