3

I am trying to remove a great number of string literal values from angular directives all over my views. I have the beginnings of a constant I wish to make global, e.g:

// UI Validation constants.
MyAngularApp.constant('valid', {
    "postalCode": {
        "pattern": "/^[0-9]+$/",
        "minLength": 4
    }
});

Then I would like to use this constant in input directives in my view templates, in a way similar to:

<input type="text" ng-pattern="{{valid.postalCode.pattern}}" blah blah>

But here I have serious doubts about binding within a directive's parameter.

I have seen a few suggestions to add such a constant to root scope, but also some suggestions to avoid root scope and only add this to local scopes inside controllers, but this would involve code changes to several controllers, where I would prefer just one change.

if I decide to go with root scope, how would I add this constant? My misguided attempt at this is:

console.log("Abandon all hope who enter here!");
MyAngularApp.config(function ($rootScope) {
    $rootScope.valid = valid; // How to access the constant here?
});

But this code gives me the error:

Uncaught Error: [$injector:modulerr]

ADDED: Some suggestions below involve the Angular run function, but I can't find that. The module call is like this:

var MyAngularApp = angular.module('MlamAngularApp', ['kendo.directives', 'ngRoute', 'ui.bootstrap', 'ngReallyClickModule', 'ngMessages', 'angular-loading-bar', 'ngAnimate']);
ProfK
  • 49,207
  • 121
  • 399
  • 775

2 Answers2

3

Per the OP - If you want to bind to rootScope you when the app loads you can use a run block like this:

app.run(function($rootScope) {
  $rootScope.name = 'bound during run'
})

I updated the plunker demonstrating scope inheritance to show it in action.

Plunker

It's bad practice to store stuff in $rootScope. Here is an example why:

$scope inheritance

It can make things confusing and messy, especially if multiple people are maintaining code. You bind something to rootScope and EVERY controller's scope has that property attached to it's scope. If you have to do it, you should document it very clearly.

Do it like this:

app.constant('myConstant', {
    "postalCode": {
        "pattern": "/^[0-9]+$/",
        "minLength": 4
    }

})

Just inject wherever you need it:

app.controller('MainCtrl', function($scope, myConstant) {
  $scope.name = myConstant;
});

Banner's method is fine as well, though I think it is better to do it this way to maintain a high level of abstraction rather than attaching a bunch of stuff to the module like that. This way let's you tuck all your constants into constants.js or whatever, and keeps everything clean.

tpie
  • 6,021
  • 3
  • 22
  • 41
  • 1
    If these are constants, these are globals. I disagree *It's bad practice to store stuff in $rootScope.* – Cyril CHAPON May 11 '15 at 06:32
  • Yes, @CyrilCHAPON, these are values that are only ever changed at 'compile time', by a developer, and never change in the course of the app, so I also disagree that global is wrong here. – ProfK May 11 '15 at 06:38
  • If they are constants it doesn't seem like they should be defined in rootScope. rootScope isn't constant. You are free to disagree as well. :) If you care to elaborate on why it's not, then by all means. There are two sides to the issue. I agree that rootScope is there for a reason, so if you have a good reason to use it, use it. But in this case I don't think it's a good reason. – tpie May 11 '15 at 06:38
  • Ok for constants != rootScope. Good point, but to clarify I'm not saying that this precise use case is not a "good reason", I'm saying *It's bad practice to store stuff in $rootScope.* is a lil opiniated and kinda hard – Cyril CHAPON May 11 '15 at 06:47
  • Concernig our issue here, what if, as he seems to want to, he wants to move to constants every 300 strings from it's app ? Would he inject it one by one in each controller ? (Just asking your opinion, not being offensive) – Cyril CHAPON May 11 '15 at 06:49
  • sure but opinions are fine, and we all have them. I added some explanation and example to my answer as to why (generally speaking) it's bad practice to attach stuff to rootScope. Again, I'm not saying in all cases, NEVER DO IT...I personally have found almost no occasion though where it was necessary. There's usually some better way. In a pinch? Sure go for it, I would..but then find the right way to do it. – tpie May 11 '15 at 06:50
  • @CyrilCHAPON - I think it would depend on the data...it's structure and quantity. It may be easier to store a big object in a constant and then access what parts you need to, or slice it up into several constants, but in both cases I would rather have it injected and know that it's there than having things lurking around on rootScope. I do see where it could become laborious at some point in some cases. Good to talk it through..we all need to learn from each other. – tpie May 11 '15 at 06:56
  • 2
    Ok buddy. Got it. Thanks for clarification and edit. Though, as a personal opinion, I find that *You bind something to rootScope and EVERY controller's scope has that property attached to it's scope*, in **that precise case**, would facilitate and clarify his codebase. I wouldn't want to inject a service (even a wrapper one) in each controller where I want to use a string. Plus some parts of the HtML page are certainly not handled by a controller at all, but he might want to use strings here too. – Cyril CHAPON May 11 '15 at 06:58
  • Whether or not it's a good idea to put stuffs in $rootScope, change is still needed here in multiple view even if it is injected into $rootScope. I like the binding in each controller since it's more aligned to the **recommended practices** but ... the choice is his... no free lunch. Change the controllers or change the view... either way.. change still happens. – Jimmy Chandra May 11 '15 at 07:00
  • Jimmy, I couldn't find in some styleguides (johnpapa's, toddmotto's) any part recommending a way to do or not to do such a thing (application-wide constant using). Can you source your words please ? Ok for "change anyway needed, even with rootScope" – Cyril CHAPON May 11 '15 at 07:03
  • @tpie I have decided to go with injecting into the controller, but when I try this, I see the `valid` parameter is undefined. I am 'declaring' my constant in the same js file as where the `module` function is called to power up the module, and I see it is called before the controller code. – ProfK May 11 '15 at 07:12
  • could you update the OP with that? You are doing it like this? `app.constant('valid', { //some val } })` – tpie May 11 '15 at 07:14
  • did you do $scope.valid = valid? – tpie May 11 '15 at 07:23
0

See the snippet. valid constant is getting injected into $rootScope when calling run.

On the view side, just do {{$root.valid.postalCode.pattern}} where you need to inject it.

And thus... the only thing that you need to change is the run function and the corresponding views' binding.

Injecting into config will not work as per Cannot get to $rootScope

(function() {
  'use strict';
  
  angular.module('myApp', []);
  
  angular.module('myApp')
    .constant('valid', {
      "postalCode": {
        "pattern": "/^[0-9]+$/",
        "minLength": 4
      }
    });
  
  angular.module('myApp')
    .run(runFn);
  
  runFn.$inject = ['$rootScope', 'valid'];
  function runFn($rootScope, valid) {
    $rootScope.valid = valid;
  }
  
  angular.module('myApp')
    .controller('myCtrl', myCtrl);
  
  function myCtrl() {
    var self = this;
  }
}());
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="myApp" ng-controller="myCtrl as vm">
  {{$root.valid.postalCode.pattern}} 
</div>
Community
  • 1
  • 1
Jimmy Chandra
  • 6,472
  • 4
  • 26
  • 38