What is a good approach to pass setting to AngularJS app?
Technology stack:
Node.js
AngularJS
Settings look like this:
window.settings = {};
settings.webSocketURL = 'ws://domain.com/websocket';
settings.webSocketTopic = 'name';
Here are few options:
Include script
<script src="scripts/settings.js"></script>
Disadvantages: settings.js file is in scripts directory and not the root directory, additional script to load.
Include script like in 1 but settings.js is generated by Node.js.
Disadvantage: additional script to load.
Embed setting directly into HTML.
Disadvantage: need to use templating like EJS instead of HTML.
I had a similar problem, and I solved using the config:
myapp
.constant("settings", {
"webSocketURL": 'ws://domain.com/websocket',
"webSocketTopic": "name"
})
then in your controllers you just have to inject the settings, and get them for example with settings.webSocketURL
I suppose you could create a config module anywhere you want
// config/app.coffee
angular.module('MyAppConfig', [])
.config ($provide) ->
$provide.constant 'webSocketURL', 'ws://domain.com/websocket'
$provide.constant 'webSocketTopic', 'name'
And use a Grunt task, or script or whatever to concatenate it with your application script.
Here is my solution:
Put client settings in server config.
config.settings = {};
Generate JS file with Node.js
app.get('/settings.js', function(req, res) {
res.setHeader('Content-Type', 'application/javascript');
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', 0);
res.send('window.settings = ' + JSON.stringify(config.settings) + ';');
});
Define AngularJS constant
ngular.module('app')
.constant('settings', window.settings)
Related
In my Rack-based app I want to serve CSS and JS and so I use Rack::Static middleware as shown below:
config.ru
use Rack::Static, urls: ["/css" ], root: "public"
run MyApp
public folder structure:
public
css
application.min.css
As per Rack::Static implementation at https://github.com/rack/rack/blob/2.2.4/lib/rack/static.rb (link refers to code in the version of Rack I am using i.e. 2.2.4) by default Cache-Control header will not be set
in Response.
But if I use following configuration
use Rack::Static, urls: ["/css" ], root: "public",
:header_rules => [
# Cache CSS/JS files, matching given regex in public caches (e.g. Rack::Cache) as well as in the browser. For e.g. myfile.1.2.1.css
#
[ /\.(?:[1-9]\.[0-9]\.[0-9])\.(?:css|js)\z/, {'cache-Control' => 'public, max-age=60'} ]
]
Then I can see following header Cache-Control: public, max-age=60 under Response Headers for e.g. in Network tab under Web Developer Tools in Firefox.
Now I want to cache bust that CSS file using fingerprint strategy as explained in following resources I found
https://css-tricks.com/strategies-for-cache-busting-css/#aa-changing-file-name
https://csswizardry.com/2019/03/cache-control-for-civilians/
So in my HTML pages I would have my stylesheet name include the fingerprint version for e.g. like following
<head>
...
...
<link href="/css/application.min.<MY_ASSET_VERSION>.css" rel="stylesheet">
</head>
where say <MY_ASSET_VERSION> is set to 1.0.0.
But I should not have any file by name application.min.1.0.0.css in my public folder. That naming is just done so as to trigger cache bust. So how can I make Rack::Static
to serve the file css/application.min.css when it encounters path /css/application.min.1.0.0.css?
Will I need to implement a middleware which should be put in application's middleware stack after Rack::Static? If yes, can anybody please help me with an example because I have not implemented any middleware.
Or if there is any other standard way for addressing the need at hand, then please suggest that.
Thanks.
Posting below the solution which I implemented using a middleware and which is working for me.
middlewares/custom_middleware/util.rb
module CustomMiddleware
module Util
extend self
EXTENSIONS_OF_ASSETS_TO_BE_FINGER_PRINTED = /css|js/
ASSET_FINGER_PRINT_FORMAT_REGEX = /[1-9]\.[0-9]\.[0-9]/
FINGER_PRINTED_ASSET_NAME_MATCHER_REGEX = /\.(?:#{ASSET_FINGER_PRINT_FORMAT_REGEX})\.(?:#{EXTENSIONS_OF_ASSETS_TO_BE_FINGER_PRINTED})\z/
ORIGINAL_ASSET_NAME_DETERMINER_FROM_FINGER_PRINTED_NAME_REGEX = /(.+)\.(?:#{ASSET_FINGER_PRINT_FORMAT_REGEX})\.(#{EXTENSIONS_OF_ASSETS_TO_BE_FINGER_PRINTED})\z/
def determine_original_asset_name(fingerprinted_asset_name:)
md = fingerprinted_asset_name.match(ORIGINAL_ASSET_NAME_DETERMINER_FROM_FINGER_PRINTED_NAME_REGEX)
return fingerprinted_asset_name if md.nil?
arr = md.captures
asset_file_name = arr[0]
asset_file_extension = arr[1]
asset_name = "#{asset_file_name}.#{asset_file_extension}"
asset_name
end
end
end
middlewares/custom_middleware/fingerprinted_asset_name_modifier.rb
require_relative 'util'
module CustomMiddleware
class FingeprintedAssetNameModifier
def initialize(app)
#app = app
end
def call(env)
env_path_info_key = 'PATH_INFO'
orig_path = env[env_path_info_key]
modified_path = Util.determine_original_asset_name(fingerprinted_asset_name: orig_path)
if modified_path != orig_path
env.merge!(env_path_info_key => modified_path)
end
#app.call(env)
end
end
end
config.ru
require_relative "middlewares/custom_middleware/fingerprinted_asset_name_modifier"
use CustomMiddleware::FingeprintedAssetNameModifier
use Rack::Static, urls: ["/css", "/js" ], root: "public",
:header_rules => [
# Cache CSS/JS files in public caches (e.g. Rack::Cache) as well as in the browser. For e.g. myfile.css
[ %w(css js), {'cache-control' => 'public, max-age=60'} ]
]
run MyApp
With above solution when following CSS file is included in my page
<head>
...
...
<link href="/css/application.min.1.0.0.css" rel="stylesheet">
</head>
application.min.1.0.0.css file serves my file at public/css/application.min.css and in Response headers Cache-Control: public, max-age=60 is set implying the after 60 seconds if application.min.1.0.0.css is re-requested it will be served from my application and not from browser's cache.
Also within 60 seconds of 1st request to the asset if changing the asset fingerprint in page like following
<link href="/css/application.min.1.0.5.css" rel="stylesheet">
and reloading the page, the asset is served from my application and not from browser's cache.
Hoping that this turns out to be useful to people who may come up with a requirement like in question post.
Thanks.
We use environment variables within our angular app to read settings etc but is there a way to generate assets/files on build?
Basically we'd like to create an 'auth/settings.js' file in the assets folder containing client id's and apiUrl's unique to each environment. These will be used in the index.html (so outside of the angular app bootstrap )
e.g. the values in the environment.ts exported into a js / json file output to the assets folder so they can be read in index.html
export const environment = {
production: false,
title: 'default',
clientId: 'xxxx-xxxx-xxxx-xxxx-xxxx',
clientUrl: 'https://localhost:4200/app',
apiUrl: 'https://localhost/api'
};
I have read that you can use mulitapps:
https://github.com/angular/angular-cli/wiki/stories-multiple-apps
This may work but looks like a lot of copy and pasting and we'll have quite a few versions of the build - I'm not sure if you can declare the common settings once and just extend the extra app settings (inheritance)?
Thanks
What we are doing in our case is actually having an config.json and config.[env-name].json files in app/config folder that configured in project assets. The config.json file is getting fetched before angular bootstrap using browser Fetch API
On our build server we are just replacing the content of config.json withconfig.staging.json or config.prod.json based on environment build. Also we have AppSettings class that gets created on bootstrap. Here is how it is looks like:
fetch(configUrl, { method: 'get' })
.then((response) => {
response.json()
.then((data: any) => {
if (environment.production) {
enableProdMode();
};
platformBrowserDynamic([{ provide: AppSettings, useValue: new AppSettings(data.config) }]).bootstrapModule(AppModule);
});
});
UPDATE:
If you need to stick some values based on your env in to index.html you might need to consider doing that on your build server. You can rather string replace the values or you can have index.[env-name].thml files so you just overwrite the index.html based on environment build.
Also check out this issues
- https://github.com/angular/angular-cli/issues/7506
- https://github.com/angular/angular-cli/issues/3855
I have an Angular, Node based app. It uses Jade for templating. I want to hide or show chunks of my page depending on the NODE_ENV variable that I use to start node with.
Something like this (not sure about how to check "else not" with ng-if):
div(ng-if="testingmode")
{{somescopevar}}
div(ng-if != "testingmode")
{{differentscopevar}}
The kicker is that testingmode is determined by what the NODE_ENV variable is. I can access that variable within my jade templates but don't know how to get it into my Angular scope.
tl;dr: How do I get NODE_ENV into my Angular scope?
Personally I would create an api call that returns that value, and then have angular grab it in a controller using $http.
In your node routes:
app.get('/nodeenv', function(req, res, next){
return res.json({ env: app.get('env') });
});
In your angular controller:
$http.get('/nodeenv').success(function(data){
$scope.myEnvironment = data.env;
});
UPDATE:
A new way I have been doing this with newer projects is generating my root index file as a template in node (eg. with doT templates). Then passing any constants into the template to be loaded directly into angular. Below is the way you could pass in the environment for example:
index.def
<html>
<head><!-- head stuff --></head>
<body>
<div>scripts and content go here</div>
<!-- initialise any templated constants -->
<script>
angular.module('mymodule').constant('globals', {
env: '{{=it.env}}'
});
</script>
</body>
</html>
As an alternative you could put this in your build system with gulp-replace. I'm sure grunt has a similar feature.
index.html
<script>
var ENV = 'NODE_ENV'; // replaced by gulp
</script>
gulpfile.js
gulp.task('setEnv', function(){
gulp.src(['index.html'])
.pipe(replace(/NODE_ENV/g, process.env.NODE_ENV))
.pipe(gulp.dest('build/index.html'));
});
Haven't test the exact code, but you get the gist.
I'm building a webapp using Sails.js and I'm wondering how to have different configurations on development and production modes. I thought that I just had to put a configuration key in config/local.js but it doesn't work. Here's an example of what I'm trying to do:
config: {
linkedIn_key: 'abcde',
linkedIn_secret: '13mcas',
linkedIn_url: 'http://localhost:1337/user/login'
}
I tried to access config in the UserController.js but I wasn't able to get the value. What is the right way to do it?
Best regards,
João
It should appear in sails.config. You can also try to create a new file: linkedin.js under the config folder and place your configurations there:
var linkedIn = {
key: 'abcde',
secret: '13mcas',
url: 'http://localhost:1337/user/login'
};
module.exports.linkedin = linkedIn;
and access it in your controller via
sails.config.linkedin
I have an application using node.js backend and require.js/backbone frontend.
My backend has a config/settings system, which depending on the environment (dev, production, beta) can do different things. I would like to propagate some of the variables to the client as well, and have them affect some template rendering (e.x change the Title or the URL of the pages).
What is the best way to achieve that?
I came up with a way to do it, and it seems to be working but I don't think its the smartest thing to do and I can't figure out how to make it work with requirejs optimizer anyway.
What I do is on the backend I expose an /api/config method (through GET) and on the client
I have the following module config.js:
// This module loads an environment config
// from the server through an API
define(function(require) {
var cfg = require('text!/api/config');
return $.parseJSON(cfg);
});
any page/module that needs config will just do:
var cfg = require('config');
As I said I am having problem with this approach, I can't compile/optimize my client code
with requirejs optimizer since /api/config file doesn't exist in offline during optimization. And I am sure there are many other reason my approach is a bad idea.
If you use use module bundlers such as webpack to bundle JavaScript files for usage in a browser, you can reuse your Node.js module for the client running in a browser. In other words, put your settings or configuration in Node.js modules, and share them between the backend and the client.
For example, you have the following settings in config.js:
Normal Node.js module: config.js
const MY_THIRD_PARTY_URL = 'https://a.third.party.url'
module.exports = { MY_THIRD_PARTY_URL }
Use the module in Node.js backend
const config = require('path-to-config.js')
console.log('My third party URL: ', config.MY_THIRD_PARTY_URL)
Share it in the client
import config from 'path-to-config.js'
console.log('My third party URL: ', config.MY_THIRD_PARTY_URL)
I do the following (note that this is Jade, i have never used require.js or backbone, however as long as you can pass variables from express into your templating language, you should be able to place JSON in data-* attributes on any element you want.)
// app.js
app.get('/', function(req, res){
var bar = {
a: "b",
c: Math.floor(Math.random()*5),
};
res.locals.foo = JSON.stringify(bar);
res.render('some-jade-template');
});
// some-jade-template.jade
!!!
html
head
script(type="text/javascript"
, src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js")
script(type="text/javascript")
$.ready(init);
function init(){
var json = $('body').attr('data-stackoverflowquestion');
var obj = JSON.parse(json);
console.log(obj);
};
body(data-stackoverflowquestion=locals.foo)
h4 Passing data with data-* attributes example