Send data to a front-end application via node? - angularjs

I have a node application that is hosting a self-contained front-end application in /public and a rest api. The front-end application communicates to the back-end exclusively via REST, but now I need to send some basic configuration information on the initial application load.
Rather than make an ajax request when the page loads, I would like to send down the required config information when /index.html is requested since it is necessary for the application to run.
What's the simplest way to setup some basic config?
Is there a way I can serve some script via node, and then parameterize it from the server?
Something like
/scripts/config.js
angular.constant('value1', '#{some parameterized server value}');
I'm using express.

You can just add a route that serves up the config.
Something like.
app.get('/scripts/config.js', function(req, res) {
var content = 'angular.module(\'config\', [])\n';
content += ' .constant(\'value1\', ' + someValue + ')';
res.set('Content-Type', 'text/javascript');
return res.send(content);
}

The only way to do it without making an Ajax request is include a config.json file in your index.html You can have node reading/writing your configuration properties to this file.
The order of execution in Angular is:
app.config()
app.run()
compile functions for directives
app.controller
directives linking
You can put your kickstarting logic in app.config() or app.run().
I believe, the best practice though is separate your config logic in a module and use $http to fetch the properties dynamically.

Related

How to integrate Nodejs running on AWS EC2 and static Angularjs content on AWS S3?

I have a Nodejs and Angularjs application running on a single AWS EC2 instance. The code structure of the application is as follows :
Code Structure
Currently, when a user makes the first call to the application, the call is handled by nodejs express framework and the call is directed to the index.html page within angular static directory, using the following code in server/server.js file
res.sendFile(path.resolve(__dirname + '/../web/dist/index.html'));
From index.html, various static components (JS, HTML, CSS etc) are invoked and all angularjs controllers / services make a call back to nodejs API for fulfilling user request.
This arrangement works fine in single instance. Now I want to move the angular static content (i.e. web directory to AWS S3). In doing so, do I have to change the following code in server.js to :
res.sendFile(path.resolve('**AWS S3 URL** +/web/dist/index.html'));
as my static files have now moved to AWS S3. Also, in doing so I have to modify all angular controllers and service to use absolute path to Nodejs API call. This means making lots of changes and introducing deployment configuration (which we have to anyways at a later date for flexibility).
Another approach that we can take is move index.html from web to server folder by making the following changes in server/server.js :
res.sendFile(path.resolve(__dirname + '/../server/index.html'));
then add the following code in server.js to handle redirects for static contents from Nodejs to S3 , something like this :
app.get('/assets/*', function(req, res){
var requestURL = req.url;
var redirectedURL = <<**Remote S3 base URL**>> + requestURL;
res.redirect(redirectedURL);
});
However, the concern is there will be lots of redirects and I am wondering, if that is a good design? I have read that search engines do not like applications with lots of redirects (HTTP 301, 304) and rank those pages lower. Hence I am trying to find out what is the best practice when deploying nodejs on AWS EC2 and static angularjs contents on S3. Any advice will be highly appreciated.
You can directly access your content from s3 by adding s3 urls of your files in to your index.html.
And if you want to serve index.html as well from s3 then you have to redirect index.html one to s3 and all other you can directly load in to browser from s3.
If you really want to serve public content fast i would suggest using cloudfront is an option where you can serve all your public content directly from cloudfront without coming to your node server and all api calls would come to your node server. But yes it depends upon the architecture of your application.

Blocking / Initialization service with angular.js

My apps are using many web services on the intranet, and url-s for those depend on the server environment.
My apps are hosted on IIS, which adds an HTTP response header like this: Environment: DEV, so every web app knows in which server environment it is running, and thus which intranet servers it must use to call all the services.
Each of my angular apps uses a service that issues a simple GET against the app's own root just to get any response with the environment name in it, and set configuration accordingly.
Question:
How should an angular app implement such a service that would execute as the very first thing in the application, and make sure that while it is getting that first response, nothing in the app tries to execute an HTTP request against other services, or even try to use any configuration provided by my environment service?
Is there a way to implement such a service in angular that could block every other service / factory in the application till it is done initializing itself?
I have many other services in the app, and none of them really know what to do till my environment service has finished its initialization.
UPDATE
Looking at it from another angle.... is it possible to implement such an interceptor in angular that could do the following?:
execute an HTTP request and block the app's execution till it gets a response
make information from the response available throughout the app as a service/factory/config.
Angular lifecycle could be one solution. Using the angular.config() phase you could peek at the headers of the HTTP service.
Create a factory called 'httpInterceptor'
function httpInterceptors(siteConfig, $q, $injector) {
return {
response: function(data, status, headers) {
siteConfig.setEnvironment(headers['Environment']);
return data;
}
};
)
Then in angular.config()
$httpProvider.interceptors.push('httpInterceptor');
If you truly want to block the other option is to use UI router resolve property to block routes loading until the request has been made https://github.com/angular-ui/ui-router/wiki you can add the resolve method to the root state.
Resolve
You can use resolve to provide your controller with content or data that > is custom to the state. resolve is an optional map of dependencies which > should be injected into the controller.
If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $stateChangeSuccess event is fired.

Pass environment variable through to Angular app

I'd like to configure my gulp webserver task to pass an environment variable into the angular app. Each team member has his own VM running the API and the variable would instruct the Angular app of the base API url. I'm trying to eliminate the need for every team member to remember to edit the config file after every TFS update.
I thought of simply setting a response header via middleware, but javascript cannot see response headers for the current page - only those of XHR responses.
So I try initializing the config service by performing a HEAD request against the web root, but this requires resolving a $http promise which requires adding a resolve to the route config to ensure it gets resolved before something tries to use it.
I tried just injecting a cookie via middleware and reading it with the $cookies service, but Internet Explorer apparently doesn't see 'localhost' as a valid domain name for cookies and does not read them.
So what other ways are there to allow an environment variable (or other form of local config) to be passed into the angular app?
We have solved this problem in many ways for different situations.
We don't use the base URL but just relative "/folder/resource".
We have an HttpHandler that resolves files with custom extension i.e. ".xyz". Then we have a file named "config.xyz" which is just a JavaScript file with something like:
{
baseUrl: [BASE_URL]
}
When the handler is asked to provide this file, the handler reads the file and does the replacements and then serve its content.
Use the one fake name for the server that serves the API. I.e: thisismylocalfake and then ask developers to configure their hosts file in system32
Have a gulp tasks that, when you compile the application, takes one config.js file and uses the machine name to replace a tag like in option 2.
I ended up adding middleware to the gulp-webserver to intercept a request for our config service file. Then I used gulp-replace and gulp-respond to inject the url from the environment variable directly in the stream. No files edited, technically and it works without having to have any sort of dev-specific code in the project.

Node.js and Angular routing - How to make REST API inaccessible by browser URL

I use Angulars $http service to call for data on the backend. Let's say JSON data. An example URL to do so would be something like:
/get/data
Doing this from within Angular nicely returns the requested data. No problem.
But even though I catch all other Angular routes using Angular UI Router with $urlRouterProvider.otherwise('/');, I can still go to my browser and type in the mydomain.com/get/data URL, which provides me with a page of JSON code.
How to I restrict back-end server calls to come just from Angular, NOT from my browser URL without user authentication?
N.B.
Using Express 4.X on Node, I also provided my app with a 'catch-all' route to default back to my front-end index.html page, like so:
router.get('*', function(req, res) {
res.sendFile(path.join(__dirname, '/index.html'));
});
Thanks!
My God! Spent whole frikin day fighting this problem, finally fixed it!
The dog is burried in headers - you have to specify one on Angular http request, then read it in node.
First of - routing setup is the same as in this guide: https://scotch.io/tutorials/setting-up-a-mean-stack-single-page-application
On frontend in Angular http request I specify one of the accepted header types to be json:
$http.get('/blog/article', {
headers: {
'Accept': 'application/json;'
}
}).success(function(data) {
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
On backend in Node I check if header includes json type. If it does, I serve back json data, so angular can receive the content. If it doesn't, I force node to load index.html, from which the controller runs the before mentioned http request with the header, ensuring you get your data then.
app.get('/blog/article', function(req, res) {
if(/application\/json;/.test(req.get('accept'))) {
//respond json
//console.log("serving json data...");
blogItemModel.find({ "_id" : req.query.id }, 'title full_text publish_date', function(err, blog_item){
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err) res.send(err);
res.json(blog_item);
});
} else {
//respond in html
//console.log('Request made from browser adress bar, not through Angular, serving index page...');
res.sendfile('./public/views/index.html');
}
});
Agree with #HankScorpio
Angular UI routing for Angular application paths and server application accessing URL paths are two different things.
Angular UI router allows you to navigate within a single page application as if you have a multi page application. This is in no way similar to accessing the actual server application endpoint.
All restrictions should be done on the web server and server web application end. Hence you will have to implement some authentication/authorisation strategy.
This isn't really an angular issue. When a user enters mydomain.com/get/data they never actually load up the angular app, so your solution must be done elsewhere.
For example, you could add this to your website's .htaccess file. It will redirect all traffic to the root of your domain.
Check out this answer here:
.htaccess Redirect based on HTTP_REFERER
You can't.
Your angular code is running on their machine, in their browser.
As such, one can spoof the environment, capture the data as the browser requests it, edit the JS of your app while it is in their browser, or various other methods.
Why do you want such a restriction anyway?

How to access config/env/all.js data in a client controller?

I have some configuration values(numbers) that I need while doing some computation in the angular code in the client controller. I general, how can one access the serverside config data in the clientside code?
If note is it possible to have a config file/folder in the public folder and easily access using angular code in client controller?
I'm not 100% sure what you are looking for here, but you have basically 3 options to handle this.
Expose An API Endpoint
You should be able to simply create an API endpoint to read the config data on the server and send it down to the client as JSON. Then you could access it in Angular like so:
$http.get('/api/config')
.success(function(configData){
//Do something with config data
});
Expose Config File Publicly
Warning - This may not be wise if your config contains sensitive data such as connection strings.
If you have a .json or a .xml file on the server with this info, then you could just make it available to HTTP GET requests and then the same code as above would apply. With the exception that if it is XML you will need to add a transform to parse the data.
$http.get('/config.json')
.success(function(configData){
//Do something with config data
});
Embed Config As Angular Constant/Value
Note - This won't work if your data is dynamic on the server.
An even simpler way is to simply put your configuration into a .js file and register it with Angular as a constant or a value.
angular.module('myModule')
.constant('config', {
foo:'bar',
blah:123
});
This way you can simply inject that anywhere in your app that you need access to it.

Resources