Summary of the Problem: I want to dynamically apply a CSS style to several div
's using Angular JS'
s ng-style
directive. The trouble is that the expression bound to ng-style
depends on a specific scope variable that I use to define the style I want to return. But here’s the catch: this scope variable is not set initially. It is only set after a particular event handler fires, so I need to make ng-style
wait for this scope variable to be set before firing.
Background: I'm using Angular JS v.1.7.8
My Code:
<!-- index.html -->
<div ng-app='myApp'>
<div ng-controller='myCtrl as ctrl'>
<div ng-style='clientScope.ctrl.getStyle()'></div>
<div ng-style='clientScope.ctrl.getStyle()'></div>
<div ng-style='clientScope.ctrl.getStyle()'></div>
<div ng-style='clientScope.ctrl.getStyle()'></div>
</div>
</div>
// app.js
(function(){
'use strict';
angular
.module('myApp', [])
.controller('myCtrl', function($scope) {
var ctrl = this;
ctrl.colorInScope = function() {
if ($scope.colors) { return true; };
return false;
}
ctrl.getStyles = function() {
return {'color': $scope.colors['myColor'] };
}
/* Note I'm not actually using setTimeout() in my real
setMyColor function, but am just using it here as an
easy way to simulate to delayed nature of how myColor gets set. */
function setMyColor() {
setTimeout(function() {
$scope.colors = {};
$scope.colors['myColor'] = '#7e9cee';
}, 3000);
}
setMyColor();
});
}());
Currently, ng-style
's expression is evaluated as soon as the div
it is bound to loads, which happens before setMyColor
is ever called. As a result, $scope.colors
in ctrl.getStyles
is undefined
and Javascript throws this error:
TypeError: Cannot read property 'myColor' of
undefined
What I've tried so far:
I see two possible approaches to fixing this:
1) Prevent ng-style
's expression from being evaluted before $scope.colors["myColor"]
is set.
Note: I've already looked into two Angular JS directives (ng-if
and ng-show
) that do something similar, but they ultimiately did not achieve what I want. Here's why:
ng-if="expression"
removes the element it is bound to from the DOM ifexpression
evaluates tofalse
, thus the element is only included in the DOM ifexpression
istrue
. I tried attachingng-if="clientScope.ctrl.colorInScope()"
to mydiv
's' like so:<div ng-if="clientScope.ctrl.colorInScope()" ng- style="clientScope.ctrl.getStyle()"></div> . . . <div ng-if="clientScope.ctrl.colorInScope()" ng- style="clientScope.ctrl.getStyle()"></div>
but this immediately removed the
div
's from the DOM upon the DOM's initial loading becauseclientScope.ctrl.colorInScope
does not evaluate totrue
at that point.ng-show="expression"
applies thevisibility: hidden
style to the element it is bound to ifexpression
evalutes tofalse
, thus the element is only visable ifexpression
is true. I also tried attachingng-show="clientScope.ctrl.colorInScope()"
to mydiv
's but even though mydiv
's visibility was initially hidden,ng-style
was evaluated prematurely anyways and Javascript threw an error because$scope.colors
wasundefined
.
2) Have some sort of method in ctrl.getStyles
to pause the execution of the function until $scope.colors["myColor"]
is set. Note: I do not want to use a timer in ctrl.getStyles
because then the function would wait every time it is called. Basically I want the function
to pause if $scope.colors["myColor"]
is undefined
(and wait for it's value to be set) or proceed straight away if $scope.colors["myColor"]
has a value.
I've read a little bit about $scope.$watch
, which is basically an Angular JS method for $scope
that takes the name of a scope variable to watch and an event handler and triggers the event handler when the scope variable changes. I could potentially initialize $scope.colors
to 0
at the start of my controller and then add await $scope.$watch('colors', myHandler())
to ctrl.getStyles = async () => ...
and return {'color': $scope.colors["myColor"]}
from myHandler
but this wouldn't work because $scope.$watch
would only get triggered once (after setMyColor
finishes) and Javascript would eventually throw some kind of timeout error or something because it would be waiting on $scope.$watch
forever (although I'm not sure exactly what my program's behavior would be in this case because I'm still learning about how async/await
work).
I've also read Wait until scope variable is loaded before using it in the view in angular.js which was helpful in showing how to leverage ng-show
, but it doesn't really answer my question about how to stall ng-style
.
Conclusion:
Do any of you Angular JS experts out there know of a better solution for what I'm trying to achieve?