I wrote a little while ago about creating a directive to present server errors on the screen in an Angular application. In my own (not so humble opinion), it was really quite nice. I was particularly proud of my usage of isolate scope. However, pride comes before a fall.
It turns out that using isolate scope in a directive is not always wise. Or rather – not always possible. And this is why:
Error: [$compile:multidir] Multiple directives [datepickerPopup, serverError] asking for new/isolated scope on: <input name="sage.dateOfBirth" class="col-xs-12 col-sm-9" type="text" value="" ng-click="vm.dateOfBirthDatePickerOpen()" server-error="vm.errors" ng-model="vm.sage.dateOfBirth" is-open="vm.dateOfBirthDatePickerIsOpen" datepicker-popup="dd MMM yyyy"> Ug. What happened here? Well, I had a date field that I was using my serverError directive on. Nothing too controversial there. The problem came when I tried to plug in UI Bootstrap’s datepicker as well. That’s right the directives are fighting. Sad face.
To be more precise, it turns out that only one directive on an element is allowed to create an isolated scope. So if I want to use UI Bootstrap’s datepicker (and I do) – well my serverError directive is toast.
So ladies and gentlemen, let me present serverError 2.0 – this time without isolated scope:
This version of the serverError directive is from a users perspective identical to the previous version. But it doesn’t use isolated scope – this means it can be used in concert with other directives which do.
It works by pulling the
serverError values off the attrs parameter.
name is just a string - the value of which never changes so it can be used as is.
serverError is an expression that represents the error dictionary that is used to store the server error messages. This is accessed through use of
scope.$eval as an when it needs to.
What I’ve outlined here works. I’ll admit that usage of
$eval makes me feel a little bit dirty (I’ve got “eval is evil” running through my head). Whilst it works, I’m not sure what I’ve done is necessarily best practice. After all the Angular docs themselves say:
*Best Practice: Use the scope option to create isolate scopes when making components that you want to reuse throughout your app. *
But as we’ve seen this isn’t always an option. I’ve written this post to document my own particular struggle and ask the question “is there a better way?” If you know then please tell me!