angular-errorlookup

Because AngularJS general error messages still suck. ngMessages can kiss my ass.
0 people use it
Author: 461084?v=3 pocesar

Build Status

AngularJS ErrorLookup

Because AngularJS general error messages still suck. ngMessages can kiss my ass.

TL;DR http://pocesar.github.io/angular-errorlookup

Made for Angular 1.4+, made in sexy Typescript

Motivation

How is it better than ngMessage / ngMessages? Or plain old ng-switch-when / ng-if directive and a bunch of divs?

Because you need to write the same boring HTML markup over and over, and you need to cluttering your scope and controllers with useless state error messages. Plus, you are usually stuck with modelController.$error / myForm.myModel.$error.required / myForm.$error.required[0].$error (srsly wtf) boolean states.

It's nearly impossible to $setValidity without a helper directive that has access to some sort of global state, since the ngModel controllers and form controllers are only available inside directives that require them or tied to a controller / scope. It's even worse, when you have to return validation from the server after a socket/ajax call and show it in your form / models, or after async validation.

So, are you tired of no way of assigning errors dynamically, bypassing them when necessary? Does your scope variables look like a mess with state errors? How about show a simple plain string on a form or console without assigning it to a scope/controller variable?

Take this superb code for example:

Now multiply that for 20 fields! Awesome right?

NO NO NO NO! HELL NO!

This doesn't tie you with a controller or directives, and it doesn't aim to provide you a validation interface. (Use angular-async-validator for that)

This module aims to provide a D.R.Y. service for your errors, watching your models for ERRORS only

It's an application-wide error messages service with helper directives for the heavy lifting! ngMessages doesn't offer you a way to programatically set errors (unless you create a directive that requires ngModel and ngMessages, and you do the bridge, aka, hot mess).

You can use ErrorLookup in your DOM in a declarative manner, in your controller(s), in your directive(s), in services (biggest win!), it keeps all your error messages under complete control (along with access to the bound element, scope and attributes), and you can have them using translations as well.

Best of all: interpolation and callbacks! Make your errors beautiful and meaningful with magic. No more useless boring generic messages like "Please fill this field" for every 350 fields in your forms and copy pasting divs all over the place or making a damn directive that adds them after each of your inputs, and the need to use $compile, and all of the haX, like appending divs to DOM without you wanting it to.

Usage

Provider and Service

The ErrorLookup provider and service is that holds all messages and instances, models and attributes from your elements so it can be the ultimate overlord of your errors (and messages, but mostly errors, since it checks the $error member of the ngModelController and FormController).

Easily retrieve localized messages from the server:

But that's only for adding and manually setting error messages, which isn't much different from adding stuff to your controllers. We want moar.

There are a couple of locals you can use in your string messages:

{{ $label }}

Pretty name of the model, instead of displaying "login.data.user.email" to the user, display something like "Your email". On the directive, it's the value of error-lookup-label

{{ $model }}

The ngModel itself with all the bells and whistles, unchanged. On the directive, it's the value of ng-model

{{ $attrs }}

The $attrs from the current element with all the bells and whistles, unchanged You can even add CSS classes to the element through here, omg messy spaghetti!

{{ $value }}

Alias for the current model $viewValue

{{ $scope }}

The assigned scope. You may access your controller doing $scope.yourcontroller in here as well.

{{ $models }}

ALL the models in the current group! Means you can reference other ngModels, how cool is that?

Since it uses interpolation, you always need to run the curryed function returned from the ErrorLookup.error(), since it has no watches and doesn't use $compile (that watch over expressions automatically):

ErrorLookup.error(group, modelIdentifier, predefine) return a function that has the following signature function(extra). Effectively, you can trigger new errors without the error being present on the model, like when you return validation from the server, without using model.$setValidity.

But wait! You don't need to add an string that will be interpolated, you can use a function! ...Here be dragons...

So let the clusterfuck ensue! Break ALL the conventions! Access ALL the models! Pass ALL elements to callbacks! Wreck ALL the declarative behavior!

API

Provider

ErrorLookupProvider.add(name: String, expr: String|Function, trustedContext:String)

Queue a message to be lazy initialized when the ErrorLookup service is instantiated for the first time.

ErrorLookupProvider.remove(name: String)

Remove a message from the queue.

Service

ErrorLookup.error(group: String, name: String, predefine: Object)

Returns a function so you can control the error for that field. Executing the returning function returns an array with the errors (or an empty array if none). The errors are kept internally between calls per model.

You can also bypass the current messages that are set for a particular model, making it unique for the same error type

And you can bypass the bypass!

ErrorLookup.errors(group: String, pick: string[], arrays: Boolean = false, predefine: Object = {})

Returns an object with all the error functions from above. If you define an array in pick, you can retrieve only some members of the group.

setting arrays to true returns the current errors array:

You can override the default errors of a group:

ErrorLookup.remove(group: String, name: String)

Remove the model from the errors pile

ErrorLookup.add(config: Object)

This method is boring as hell. Long parameter list and you shouldn't need to call it manually if you use the directives. You need to always provide non-optional stuff everytime to the function or it breaks.

The config object is as following:

  • config.scope : the scope. [Not Optional]
  • config.name : the name of the ng-model, or any internal name you want to use. it must exist in the given scope [Not Optional]
  • config.model : the model itself ngModelController (or ngFormController if you add a form model to it) [Not Optional]
  • config.attrs : the $attrs of the element, you can pass an object too when not adding from inside a directive
  • config.group : the group name, fallbacks to using scope.$id
  • config.label : the label to give the error. Defaults to the name of the model. pretty name for your login.data.user as Your username for example
  • config.el : assign a DOM element to the current model
ErrorLookup.messages

Keeps your application wide messages in a repository

ErrorLookup.messages.add(name: String, expr: String|Function, trustedContext: String)

Adds a message. Accepts a function (callback!) or a interpolated string. If you set trustedContext to 'html' it will use the $sce service and accept safe HTML in your interpolated string.

Returns the current $interpolated string or the function you passed, you can call it right way.

ErrorLookup.messages.remove(name: String)

Remove a message from the service

ErrorLookup.messages.include(url: String)

Loads a JSON representation of your messages. Returns a promise. If you modify the resulting value, you can modify the included messages

Directives

error-lookup

The error-lookup directive will add any ng-model element to the bunch.

By default, error-lookup group elements by scope.$parent.$id, but you can set your own name using error-lookup="othergroup" (it's preferable this way, so you can reuse in your code and set errors from inside services and other directives)

The name of the model will be your error-lookup-name, then name attribute, then your ng-model, in this order.

error-lookup-label can apply a nice name to your model, like error-lookup-label="Your email".

error-lookup-form

This one is for forms, they keep all their children in sync (long missing from angular itself).

error-lookup-display

The error-lookup-display is a shortcut to the ErrorLookup.error() function.

This directive exposes to the DOM the following scope variables:

$model: IErrorHelper;

The ErrorLookup model

$errorCount: number;

Current error count

$first: IErrorMessage;

Is first error for that field, containing the fields described in ErrorLookup.error()

$latest: IErrorMessage;

Is the top most error for that field, containing the fields described in ErrorLookup.error()

$errors: any[];

Current cached array of errors

$hasChanged(): boolean;

If the field has errors AND is $dirty AND has been $touched

N.B: Since the scope isn't isolated, but a child scope, it inherits from the current scope it's in, make sure to understand scope inheritance before you try your hax0rs in the code.

$latestHtml: angular.IAugmentedJQuery;

The $sce.trustAsHtml version of $latest

$firstHtml: angular.IAugmentedJQuery;

The $sce.trustAsHtml version of $first

error-lookup-template

This directive creates a ul with a default limit of 1 item for errors. It needs to be applied in the same element that has error-lookup-display on it.

Filter

It's used to filter error messages from a bunch of items in an array, used by the error-lookup-template directive:

comments powered by Disqus
This page was last updated over 1 year ago.