I have an AngularJS application in which my code looks something like this:
myApp = angular.module('myApp',
[
'ui.router',
'ngMaterial',
'ngMessages'
]
);
myApp.constant('CONSTANTS', (function() {
// Define your variable
return {
backend: {
baseURL: 'http://mybackend.com:3026'
}
};
})());
I run this application using http-server on port number 8000 like this:
% http-server -p 8000
I want to pass in a command-line argument for the backend.baseURL such that it over-rides the value specified in the code. How can I do it??
What you need is at least required http-server that supported dynamic content. while your http-server is supported only static content.
And in the comment you asking which server should you use. There are thousands of web-server that support dynamic content out there. but sinc you are currently using http-server I assumed you just want a small server for local-dev.
Unfortunately, I cannot find any server that support your need without modifying their code. So I suggest you to create your own server base on a library on npm.
This is and example server using live-server.
var liveServer = require("live-server");
var fs = require("fs")
var root = process.argv[2] || "."
var port = process.argv[3] || 8000
var replaceTextMiddleWare = function(req, res, next){
var file = process.argv[4]
var find = process.argv[5]
var replace = process.argv[6]
if(file && find){
if(req.url === file) {
fs.readFile( root + file, "utf-8", function(e, content){
res.end( content.replace(find, replace))
} )
return;
}
}
next();
}
var params = {
port: port, // Set the server port. Defaults to 8080.
host: "0.0.0.0", // Set the address to bind to. Defaults to 0.0.0.0 or process.env.IP.
root: root, // Set root directory that's being server. Defaults to cwd.
open: false, // When false, it won't load your browser by default.
ignore: 'scss,my/templates', // comma-separated string for paths to ignore
file: "index.html", // When set, serve this file for every 404 (useful for single-page applications)
wait: 1000, // Waits for all changes, before reloading. Defaults to 0 sec.
mount: [['/components', './node_modules']], // Mount a directory to a route.
logLevel: 2, // 0 = errors only, 1 = some, 2 = lots
middleware: [ replaceTextMiddleWare ] // Takes an array of Connect-compatible middleware that are injected into the server middleware stack
};
liveServer.start(params);
Then you can run your server by
nodejs myserver.js /mydocument/myproject/ 8000 config.js "http://mybackend.com:3026" "http://mydevserver.com:80"
The command accept parameters:
Path to serve content
Port
File name
Text to find
Text to replace
This server support only one dynamic file with simple find/replace.
From this point, I guess you can modify middleware to do whatever you want.
when Ive done this in production I use the build process for this, using gulp in this case,
var knownOptions = {
string: 'env',
default: { env: process.env.NODE_ENV || 'default' }
};
var options = minimist(process.argv.slice(2), knownOptions);
console.log("using config : " + chalk.blue(options.env));
we get an environment variable defaulting to default using minimist we can pass -env 'string'
then further in the code pushing a dynamic file onto app.js
//now we use options.env
appJS.push("env/"+options.env+".js");
env/[options.env].js here is an angular module that exports environment specific constants
looks like you are not using gulp but you are using node script from package.json. if you are using gulp then this should not be a problem you you use http-server via gulp.
one thing you can do in your current case is as part of your run command, set process.env.baseUrl="dynamic" and then roughly speaking, use this in your code like this
return {
backend: {
baseURL:process.env.baseUrl || 'http://fallbackurl'
}
Basically what I understand is that you want to customize http-server package for your won custom handling.
Here is small solution I found for you....
Go to node installation folder(In my case its local one)...
So path of file which we going to edit is like this...
../node_modules/http-server/lib/http-server.js
Import one package which serve you to get command line arguments...you don't need to install it, its already there.
var argv = require('optimist').boolean('cors').argv; // add this line at top after line number 7
Now
At line 60 and after code before.push(function (req, res) { add following code..
if( req.url === '/backend-url' ) {
return res.end(JSON.stringify(argv.x));
}
So before push function will look like::
before.push(function (req, res) {
if( req.url === '/backend-url' ) {
return res.end(JSON.stringify(argv.x));
}
if (options.logFn) {
options.logFn(req, res);
}
res.emit('next');
});
now we have configured our new command line argument x for http-server which will be return for url "/backend-url"
Now at front end
myApp.constant('CONSTANTS', ('$http', function($http) {
// Define your variable
**var backendURL = (function() {
return $http.get('/backend-url', function(bUrl) {
return bUrl;
})
})();**
return {
backend: {
baseURL: backendURL
}
};
})());
Done, now you add url like this: **http-server -p 8080 -x http://example.com**
I choose this approch as replacing file content i dont think good in you case
If you use only one instance of your app, you can start it with script, that accepts all necessary command line arguments, replaces appropriate placeholders (string between specific comments, for example) in your application js files and then launches http-server on necessary port.
Related
I'm working on AngularJS single page application that consume REST services. The front-end application is developed separately from the back-end and therefore during development we've to hardcode the domain name in the URLs for AJAX calls (we've enabled CORS). But in the case of production everything is running in the same domain and hence hardcoding the domain name looks little bad. Is there we can use the domain name in urls for ajax calls during development and in production don't hardcode the domain name? I'm using gulp.
An example of using gulp-ng-constant with an $http interceptor:
The following task in gulpfile.js will generate the file target/build/scripts/_config.js with the contents angular.module('globals', []).constant('apiContextPath', '...');:
gulp.task('app.ngconstant', [], function() {
var ngConstant = require('gulp-ng-constant'),
var rename = require('gulp-rename');
return
ngConstant({
constants: {
apiContextPath: '.' // TODO Devise a way to set per environment; eg command line
},
name: 'globals',
stream: true
})
.pipe(rename('_config.js'))
.pipe(gulp.dest('target/build/scripts'));
});
Obviously you need to include the generated file in your (packed/minified) code.
Something along the following code will configure the $httpProvider to prepend the apiContextPath to all requests that start with '/api/' (i.e. our REST endpoints):
angular.module(...).config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(['globals', function(globals) {
return {
'request': function(config) {
if( config.url.indexOf('/api/') === 0 ) {
config.url = globals.apiContextPath + config.url;
}
return config;
}
};
}]);
}]);
(There are quite a few other configuration options, so this is just an example from an older project I worked on.)
If you don't already, you could pass your gulp build a parameter to build in production or development mode.
Based on that flag you can set a baseUrl property in your gulpfile that is used to include a script (using gulp-insert) into your javascript build before all of your angular scripts:
'window.baseUrl = ' + baseUrl
Then you could have a constant in your application that your services use to get the baseUrl:
angular.module('YourModule').constant('baseUrl', window.baseUrl);
How to use socket.io in Mean.io stack?
First of all, Mean.io changes their folder structure very regularly.. So my question is where is the best place to configure socket.io ? or is it better to use express.io ?
Second I am still not quite sure where to look for to find code that tells mean.io to listen for port, I have found a port is defined in config folder in all.js file, but real problem is as soon as I define server.listen(port) app doesn't load. and if I don't app loads but socket.io doesn't work.
Also I have another question about /socket.io/socket-io.js file? I have copied that in index folder, but my app can't find it or says 404 error. I know it's not an actual file sitting on any such location as far as I have understood, also people suggested to put that line as 127.0.0.1/socket.io/socket-io.js but none made the js file available for the app to be able to run socket.io.
What is the proper way of defining socket.io in mean.io framework?
I also faced the same issue and took me about a week to finally get it right. I'll try to explain what I did:
app.js
In this file, I just invoke the code that creates and sets up a socket.io object for me, which is then passed to the routes module.
'use strict';
/*
* Defining the Package
*/
var Module = require('meanio').Module;
var MeanSocket = new Module('chat');
/*
* All MEAN packages require registration
* Dependency injection is used to define required modules
*/
MeanSocket.register(function(app, http) {
var io = require('./server/config/socketio')(http);
//We enable routing. By default the Package Object is passed to the routes
MeanSocket.routes(io);
return MeanSocket;
});
server/config/socketio.js
This file simply configures the socket.io object. Please note that I had to upgrade meanio module to version 0.5.26 for this work, as http object (express server) is not available in older meanio versions. Moreover, in case you want to use ssl, you can inject https instead of http.
'use strict';
var config = require('meanio').loadConfig(),
cookie = require('cookie'),
cookieParser = require('cookie-parser'),
socketio = require('socket.io');
module.exports = function(http) {
var io = socketio.listen(http);
io.use(function(socket, next) {
var data = socket.request;
if (!data.headers.cookie) {
return next(new Error('No cookie transmitted.'));
}
var parsedCookie = cookie.parse(data.headers.cookie);
var sessionID = parsedCookie[config.sessionName];
var parsedSessionID = cookieParser.signedCookie(parsedCookie[config.sessionName], config.sessionSecret);
if (sessionID === parsedSessionID) {
return next(new Error('Cookie is invalid.'));
}
next();
});
return io;
};
routes/chat.js
Finally, use the routes file to define the socket events, etc.
'use strict';
// The Package is passed automatically as first parameter
module.exports = function(MeanSocket, io) {
io.on('connection', function(socket) {
console.log('Client Connected');
socket.on('authenticate', function(data, callback) {
});
});
};
Hope this helps!
The simplest way would be to install the socket package...
mean install socket
I'm trying to make an angularjs app 12 factor compliant regarding config (http://12factor.net/config).
It should depend on the environment and I should not see the words development, test, production, etc. in the code.
Variables could be stored in bash env for example.
I could pass them to a webserver.
I already thought of an .erb template file to erb config.js.erb > config.js, but if I change a variable while the application is running I'd have to redo this.
I already found this article http://mindthecode.com/how-to-use-environment-variables-in-your-angular-application/
But it's a big lie and Grunt.js to do this, really... Anyway.
I know 12factor philosophy wasn't made for frontend application but my angular application could be deployed in many different servers across many environment and it won't harm anyone to try to do things properly :).
Thanks !
Edit:
The config parameters I'd like to use would be some stuff like :
app:
api:
url: "The url of the api server"
port: 8080
cdn:
images: "url of my images caching service"
google:
oauth:
"api_key": "The api key used for that deployment"
#other external frontend services
Other Edit:
This guy kinda went with an answer : http://bahmutov.calepin.co/inject-valid-constants-into-angular.html which I find kind of ugly and totally bound to angularjs; but it works !
Here is the solution I found, it's totally bound to angularjs but it works for me on Heroku and it's very simple. I just append my conf module to the generated code.
Everytime I restart the app a new version of the code is copied therefore the Append only happens once.
The Append just redefines an already existing configuration variable.
If someone find something more 'classy' I'd be happy to put it as the right solution !
var compression = require('compression');
var express = require('express');
var logfmt = require('logfmt');
var stdio = require('stdio');
var glob = require("glob");
var fs = require('fs');
// ------------------------
// Read config from args
var ops = stdio.getopt({
'url': {
key: 'u',
args: 1,
default: '',
description: 'Url of api server'
},
'port': {
key: 'p',
args: 1,
default: 5000,
description: 'Port on which to listen'
}
});
var port = ops.port || process.env.PORT;
ops.port = undefined;
// ------------------------
// Append config to js built file
var codeToAppend = 'angular.module("project.config",[]).constant("ApiConfig",' + JSON.stringify(ops) + ');';
glob(__dirname + '/dist/scripts/scripts.*.js', function(er, files) {
fs.appendFile(files[0], codeToAppend, function(err) {
if (err) {
throw err;
}
console.log('The "conf code" was appended to file!');
});
});
// ------------------------
// Start App :3
var app = express();
app.use(logfmt.requestLogger());
app.use(compression({
threshold: 512
}));
app.use(express.static(__dirname + '/dist'));
app.get('/config', function(req, res) {
res.json(ops);
});
app.listen(port);
I found a repository on github that hopefully help you Angular-Express-Train-Seed
I was referring to how angular js connects to mongo db. So while setting the url and port for the 'db' object, I found code like below:
var path = require('path'),
rootPath = path.normalize(__dirname + '/../..');
module.exports = {
root: rootPath,
port: process.env.PORT || 3000,
db: process.env.MONGOHQ_URL
}
Will someone please let me know what is this path here for? And also, what is the default value of MONGOHQ_URL here?
From the link http://docs.mongohq.com/languages/nodejs.html , I came to know mongo url can be set to :
var MONGOHQ_URL="mongodb://user:pass#server.mongohq.com:port_name/db_name"
Am I right?
Thanks,
Sabari
The MONGOHQ_URL in your code snippet comes from the shell environment. For example, in bash you would add that to your ~/.bash_profile:
export MONGOHQ_URL="mongodb://user:pass#server.mongohq.com:port_name/db_name"
... or include on your command line when starting the node app:
MONGOHQ_URL="mongodb://user:pass#server.mongohq.com:port_name/db_name" node app.js
Another common approach with Node.js is to use something like dotenv, which will load environment variables from a .env directory in your project.
And also, what is the default value of MONGOHQ_URL here?
There is no default; you need to define this if you want to connect to a MongoHQ instance.
I'm using Restangular in a project built using Gruntjs. Here is a snippet:
// scripts/app.js
angular.module('myApp', ['restangular'])])
.config(['RestangularProvider', function(RestangularProvider) {
/* this is different on dev, test, prod environments */
var baseUrl = 'http://localhost:8080/sms-api/rest/management/';
RestangularProvider.setBaseUrl(baseUrl);
}])
I would like to have a different value for baseUrl if specified at cli or a default if not specified:
$ grunt server
Using default value for 'baseUrl'
$ grunt build --rest.baseUrl='http://my.domain.com/rest/management/'
Using 'http://my.domain.com/rest/management/' for 'baseUrl'
How can I do that?
It's possible with the aid of Grunt preprocess which is useful for replacing (and other things) templates inside files.
First add this to your .js code:
/* Begin insertion of baseUrl by GruntJs */
/* #ifdef baseUrl
var baseUrl = /* #echo baseUrl */ // #echo ";"
// #endif */
/* #ifndef baseUrl
var baseUrl = 'http://www.fallback.url';
// #endif */
/* End of baseUrl insertion */
Then in your grunt file, after installing grunt preprocess (i.e npm install grunt-preprocess --save-dev you add the following configuration:
preprocess: {
options: {
context: {
}
},
js: {
src: 'public/js/services.js',
dest: 'services.js'
}
},
obviously, you need to update the js file list accordingly to which ever files you use. important notice - if you are planning on updating the same file (and not to a new destination) you need to use the inline option
At last, in order to work with a command line config variable, add the following custom task to your grunt file as well:
grunt.registerTask('baseUrl', function () {
var target = grunt.option('rest.baseUrl') || undefined;
grunt.log.ok('baseUrl is set to', target);
grunt.config('preprocess.options.context.baseUrl', target);
grunt.task.run('preprocess');
});
Finally, run the baseUrl task like so:
grunt baseUrl --rest.baseUrl='http://some.domain.net/public/whatever'
Notice I put in a fallback url so you can also run grunt baseUrl and grunt will set your baseUrl to your defined one.