I am using a directive which modifies the DOM and therefore must be done after all postLink fns are called. (see angular-ui modules/directives/select2/select2.js:103)
Given certain preconditions (not encoded in the URL), I would like to set the focus to the first select2 element on the page. This is problematic because before setting focus, select2 must be initialized, and without having some cheesy arbitrary delay > 0, I can't schedule an event to be broadcasted that will set the focus.
I have created the following directive which will listen for a focus primaryInput/focus event and set the focus.
# TODO - should we create a directive for tab order instead?
app.directive "primaryInput", () ->
{
name: "primaryInput"
link: (scope, element, attrs) ->
scope.$on "primaryInput/focus", ->
element.focus()
}
Changing the primaryInput directive priority does not work, as any priority will run before the setTimeout function is scheduled.
Ideally, I'd like to be able to have a collection of promises which each delayed directive pushes to a stack, and then a promise which gets resolved once all of those promises are complete. I would have focused my question on how to best achieve this, but I can think of a not-very-good way already, would like to explore better ways, and want to avoid the XY problem.
Thank you!
After going through the effort of describing the problem here, taking a step back, I thought of an acceptable approach to my problem and will answer my own question, but leave it open if anyone has a better approach.
Since the delayed execution complexity is in the directive dealing with Select2, I decided, for now, to isolate that complexity there.
I override the focus function on the element in my Select2 directive, so focus calls are forwarded to the select2 instance once it is available. This way, the element can be told to focus before select2.
My focus directive:
app.directive "input", ($injector) ->
primaryMatcher = (e,attrs) ->
attrs.primary?
{
name: "inputFocus"
restrict: 'E'
link: (scope, element, attrs) ->
scope.$on "focus", (e, matcher)->
if ((matcher || primaryMatcher)(element, attrs))
console.log("focus", element, attrs)
element.focus()
}
In my Select2 directive:
app.directive "s2Factor", () ->
# ...
{
name: "s2Factor"
require: "?ngModel"
priority: 1
restrict: "A"
link: (scope, el, attr, controller) ->
select2_p = $.Deferred()
# ...
setTimeout ->
el.select2 opts
select2_p.resolve(el.data("select2"))
el.focus = () ->
select2_p.then (select2) ->
select2.focus()
}
It involves instance monkey patching, but I find it to be acceptable in this case.