Angular, without a browser? - angularjs

Sometimes I want to experiment (in a local Node script) with some aspect of Angular - e.g. Services, DI, etc - stuff that has nothing to do with the Browser or the DOM. Is there a way to do that? i.e. load some base portion of the Angular Infrastructure? If I just require("angular") in a Node script, it complains:
ReferenceError: window is not defined
which makes sense because Angular lives for the Browser-window.
But it seems like some portions of Angular could be used for non-web applications - although that's not my reason for asking this. I'm just trying to improve my understanding Angular and sometimes want to do a little experiment while stripping away/ignore as much as possible.

Experimenting with Angular is best done in a browser, due to window and other API's Angular relies on.
However, if you're dead set on using Angular with node, you might look into the vm module which essentially lets you eval code with a specific stand-in object as a sort of proxy global object. e.g.:
const vm = require('vm');
const fs = require('fs');
const test = fs.readFileSync('./test.js', 'utf-8');
const windowProxy = {
document: {
createElement: function() {
return {
setAttribute: function() {},
pathname: ''
}
},
querySelector: function() {},
addEventListener: function() {}
},
location: {
href: ''
},
addEventListener: function() {}
};
windowProxy.window = windowProxy;
vm.createContext(windowProxy);
vm.runInContext(test, windowProxy);
will at least let you load Angular without complaining. Undoubtedly you would encounter more errors, and would have to polyfill the missing browser API's yourself.
You might also look into PhantomJS for a more robust testing environment, though that would no longer be Node.

Related

Restangular using app context inconsistently

I have an issue with Restangular using my sites context inconsistently. I have a site running at example.com/app-name. When making the below call I am expecting it to hit example.com/app-name/foo
angular.module('myApp').factory('File', File);
File.$inject = [ 'Restangular' ];
function File(Restangular) {
var service = {
postFiles : postFiles
};
return service;
function postFiles(files) {
return Restangular.all('foo').post(files);
}
}
However, it hits example.com/foo instead.
While not ideal, I figured I would set the base url for now to work through this issue and then just create a build step to handle the different environment app names.
angular.module('myApp').run(run);
run.$inject = [ 'Restangular' ];
function run(Restangular) {
Restangular.setBaseUrl('app-name');
}
After this though, it now hits example.com/app-name/app-name/foo.
My current solution to the problem is to do
Restangular.one('app-name').all('foo').post(files)
Which works but, again, will require some build tasks setup to make configurable which is less than ideal.
I've also tried setting the <base> tag to the following:
<base href="/app-name/" />
And the requests still go to example.com/foo instead of example.com/app-name/foo.
Is there something I'm missing in the setup or use of Restangular? Why is the app context being used differently in these two scenarios?

Expose object fron Angularjs App to Protractor test

I am writing end-to-end tests for my AngularJS-based application using Protractor. Some cases require using mocks to test - for example, a network connection issue. If an AJAX request to server fails, the user must see a warning message.
My mocks are registered in the application as services. I want them to be accessible to the tests to write something like this:
var proxy;
beforeEach(function() { proxy = getProxyMock(); });
it("When network is OK, request succeeds", function(done) {
proxy.networkAvailable = true;
element(by.id('loginButton')).click().then(function() {
expect(element(by.id('error')).count()).toEqual(0);
done();
});
});
it("When network is faulty, message is displayed", function(done) {
proxy.networkAvailable = false;
element(by.id('loginButton')).click().then(function() {
expect(element(by.id('error')).count()).toEqual(1);
done();
});
});
How do I implement the getProxyMock function to pass an object from the application to the test? I can store proxies in the window object of the app, but still do not know how to access it.
After some reading and understanding the testing process a bit better, it turned to be impossible. The tests are executed in NodeJS, and the frontend code in a browser - Javascript object instances cannot be truly shared between two different processes.
However, there is a workaround: you can execute a script inside browser.
First, your frontend code must provide some sort of service locator, like this:
angular.module('myModule', [])
.service('proxy', NetworkProxy)
.run(function(proxy) {
window.MY_SERVICES = {
proxy: proxy,
};
});
Then, the test goes like this:
it("Testing the script", function(done) {
browser.executeScript(function() {
window.MY_SERVICES.proxy.networkAvailable = false;
});
element(by.id('loginButton')).click().then(function() {
expect(element.all(by.id('error')).count()).toEqual(1);
done();
});
});
Please note that when you use executeScript, the function is serialized to be sent to browser for execution. This puts some limitations worth keeping in mind: if your script function returns a value, it is a clone of the original object from browser. Updating the returned value will not modify the original! For the same reason, you cannot use closures in the function.

Angular js read environmental variables

I am trying to read environment variables in http get calls
$http.get('http://' + $location.host() + ':8080/api')
I want to be able to read the environmen variable and use it as the http rest server in teh above API call, as follows
$http.get('environmental_variable ':8080/api')
Note: I dont know the environment variable until runtime So I cannot have the value before hand to use it as a constant
There are lots of examples showing how you can put your settings into different files or constants. Most of these work, but miss the point.
Your configuration settings are not part of your code!
Apart from the 'Hello World' examples, your deployment should be carried out by a CI/CD server and this should be responsible for setting your configuration settings. This has a number of benefits:
1) You are deploying the same code to different environments. If you deploy code to a test environment, then you want to deploy the same code to your production environment. If your servers have to rebuild the code, to add the production configuration settings, you are deploying different code.
2) Code can be shared without giving away your API details, AWS settings and other secret information.
3) It allows new environments to be added easily.
There are lots of examples out there on how to do this. One example is www.jvandemo.com/how-to-configure-your-angularjs-application-using-environment-variables
There are no such things as environment variables in the browser.
The $location service is always going to get your current URL. I guess your API might live on a different host.
It's possible to simulate environment variables by storing configuration in an Angular constant.
app.constant('env', {
API_URL: "http://someurl.com:8080/api"
});
Then you can inject this constant into your other providers.
app.controller('MyController', function($http, env) {
$http.get(env.API_URL);
});
Here's a decent article on best practices with constants. The article favours not using constants, as it's useful to be able to modify the configuration without having to rebuild the code.
The way I normally handle this is to move all the instance configuration details out to a config.json file, then load it with $http when I bootstrap my application.
For instance, you might have a config file like this.
{
apiUrl: "http://someurl.com:8080/api"
}
Then an Angular service that loads it.
app.service('Config', function($http) {
return function() {
return $http.get('config.json');
};
});
Then other services can get hold of the promise, that will expose the config when resolved.
app.controller('MyController', function($http, Config) {
Config()
.then(function(config) {
$http.get(config.apiUrl);
});
});
I strongly suggest you to use a library for setting environment variables. You can use angular-environment plugin to do that: https://www.npmjs.com/package/angular-environment
Here's a example
angular.module('yourApp', ['environment']).
config(function(envServiceProvider) {
// set the domains and variables for each environment
envServiceProvider.config({
domains: {
development: ['localhost', 'acme.dev.local'],
production: ['acme.com', '*.acme.com', 'acme.dev.prod'],
test: ['test.acme.com', 'acme.dev.test', 'acme.*.com'],
// anotherStage: ['domain1', 'domain2']
},
vars: {
development: {
apiUrl: '//api.acme.dev.local/v1',
staticUrl: '//static.acme.dev.local',
// antoherCustomVar: 'lorem',
// antoherCustomVar: 'ipsum'
},
test: {
apiUrl: '//api.acme.dev.test/v1',
staticUrl: '//static.acme.dev.test',
// antoherCustomVar: 'lorem',
// antoherCustomVar: 'ipsum'
},
production: {
apiUrl: '//api.acme.com/v1',
staticUrl: '//static.acme.com',
// antoherCustomVar: 'lorem',
// antoherCustomVar: 'ipsum'
},
// anotherStage: {
// customVar: 'lorem',
// customVar: 'ipsum'
// },
defaults: {
apiUrl: '//api.default.com/v1',
staticUrl: '//static.default.com'
}
}
});
// run the environment check, so the comprobation is made
// before controllers and services are built
envServiceProvider.check();
});
Then you can get the right variable based on what environment your application is running:
var apiUrl = envService.read('apiUrl');
Cheers!

Loading relative templateUrl

I've been trying to find the best way to create a modular, scalable angular application. I really like the structure of projects like angular-boilerplate, angular-app, where all the related files are grouped together by feature for partials and directives.
project
|-- partial
| |-- partial.js
| |-- partial.html
| |-- partial.css
| |-- partial.spec.js
However, in all these examples, the template URL is loaded relative to the base url, not relative to the current file:
angular.module('account', [])
.config(function($stateProvider) {
$stateProvider.state('account', {
url: '/account',
templateUrl: 'main/account/account.tpl.html', // this is not very modular
controller: 'AccountCtrl',
});
})
This is not very modular, and could become difficult to maintain in large projects. I would need to remember to change the templateUrl path every time I moved any of these modules. It would be nice if there was some way to load the template relative to the current file like:
templateUrl: './account.tpl.html'
Is there any way to do something like this in angular?
The best way to do this now is using a module loader like browserify, webpack, or typescript. There are plenty of others as well. Since requires can be made from the relative location of the file, and the added bonus of being able to import templates via transforms or loaders like partialify, you don't even have to use template urls anymore. Just simply inline the Template via a require.
My old answered is still available below:
I wrote a post on exactly this subject and spoke on it at our local Angular Meetup. Most of us are now using it in production.
It is quite simple as long as your file structure is represented effectively in your modules. Here is a quick preview of the solution. Full article link follows.
var module = angular.module('myApp.things', []);
var all = angular.module('myApp.things.all', [
'myApp.things',
'things.someThing',
'things.someOtherThing',
'things.someOtherOtherThing',
]);
module.paths = {
root: '/path/to/this/thing/',
partials: '/path/to/this/thing/partials/',
sub: '/path/to/this/thing/sub/',
};
module.constant('THINGS_ROOT', module.paths.root);
module.constant('THINGS_PARTIALS', module.paths.partials);
module.constant('THINGS_SUB', module.paths.sub);
module.config(function(stateHelperProvider, THINGS_PARTIALS) {
stateHelperProvider.setNestedState({
name: 'things',
url: '/things',
templateUrl: THINGS_PARTIALS + 'things.html',
});
});
And then any sub modules or "relative" modules look like this:
var module = angular.module('things.someThing', ['myApp.things']);
var parent = angular.module('myApp.things');
module.paths = {
root: parent.paths.sub + '/someThing/',
sub: parent.paths.sub + '/someThing/sub/',
partials: parent.paths.sub + '/someThing/module/partials/',
};
module.constant('SOMETHING_ROOT', module.paths.root);
module.constant('SOMETHING_PARTIALS', module.paths.partials);
module.constant('SOMETHING_SUB', module.paths.sub);
module.config(function(stateHelperProvider, SOMETHING_PARTIALS) {
stateHelperProvider.setNestedState({
name: 'things.someThing',
url: "/someThing",
templateUrl: SOMETHING_PARTIALS + 'someThing.html',
});
});
Hope this helps!
Full Article: Relative AngularJS Modules
Cheers!
I think you'll eventually find that maintaining the paths relative to the js file will be harder, if even possible. When it comes time to ship, you are most likely going to want to concatenate all of your javascript files to one file, in which case you are going to want the templates to be relative to the baseUrl. Also, if you are fetching the templates via ajax, which Angular does by default unless you pre-package them in the $templateCache, you are definitely going to want them relative to the baseUrl, so the server knows where to find them once your js file has already been sent to the browser.
Perhaps the reason that you don't like having them relative to the baseUrl in development is because you aren't running a server locally? If that's the case, I would change that. It will make your life much easier, especially if you are going to work with routes. I would check out Grunt, it has a very simple server that you can run locally to mimic a production setup called Grunt Connect. You could also checkout a project like Yeoman, which provides a pre-packaged front end development environment using Grunt, so you don't have to spend a lot of time getting setup. The Angular Seed project is a good example of a Grunt setup for local development as well.
I've been chewing on this issue for a while now. I use gulp to package up my templates for production, but I was struggling to find a solution that I was happy with for development.
This is where I ended up. The snippet below allows any url to be rewired as you see fit.
angular.module('rm').config(function($httpProvider) {
//this map can be defined however you like
//one option is to loop through script tags and create it automatically
var templateMap = {
"relative.tpl.html":"/full/path/to/relative.tpl.html",
etc...
};
//register an http interceptor to transform your template urls
$httpProvider.interceptors.push(function () {
return {
'request': function (config) {
var url = config.url;
config.url = templateMap[url] || url;
return config;
}
};
});
});
Currenly it is possible to do what you want using systemJs modules loader.
import usersTemplate from './users.tpl';
var directive = {
templateUrl: usersTemplate.name
}
You can check good example here https://github.com/swimlane-contrib/angular1-systemjs-seed
I had been using templateUrl: './templateFile.tpl.html but updated something and it broke. So I threw this in there.
I've been using this in my Gruntfile.js html2js object:
html2js: {
/**
* These are the templates from `src/app`.
*/
app: {
options: {
base: '<%= conf.app %>',
rename: function( templateName ) {
return templateName.substr( templateName.lastIndexOf('/') );
}
},
src: [ '<%= conf.app %>/**/{,*/}<%= conf.templatePattern %>' ],
dest: '.tmp/templates/templates-app.js'
}
}
I know this can lead to conflicts, but that is a smaller problem to me than having to edit /path/to/modules/widgets/wigdetName/widgetTemplate.tpl.html in every file, every time, I include it in another project.

Unable to access NameSpace.app variables with ST2?

I'm using the Sencha Command Line 3 tools with a newly generated Sencha Touch 2 application.
Assuming my app.js file looks like this:
Ext.application({
name: "CA",
event_code: "test123",
launch: function() {
console.log("application launched!");
}
});
My views and object stores depend on generating a URL based on CA.app.event_code equaling "test123";
During development in the browser, everything works fine, CA.app returns the variables I need.
When I compile my application with sencha app build and try to run the minified version in the browser, I get an error like this:
Error evaluating http://localhost:8888/app.js with message: TypeError: Cannot read property 'event_code' of undefined localhost:11
I'm not entirely sure why this is happening or how I can fix it. I am open to any and all ideas or suggestions, any pointers in the right direction will be greatly appreciated.
Ran into the exact same issue. You have no access to the namespaced app within the views... really sucks that they let you in development and not when built. Anyway, I got around it by adding a static helper class and using that all over my app:
In /app/util/Helper.js:
Ext.define('MyApp.util.Helper', {
singleton: true,
alternateClassName: 'Helper',
config: {
foo: "bar",
bat: "baz"
},
staticFunction: function() {
// whatever you need to do...
}
});
Then in your view or controller:
Ext.define('MyApp.view.SomeView', {
...
requires: ['Events.util.Helper'],
...
someViewFunction: function() {
var someValue = Helper.staticFunction();
// and you can use Helper.foo or Helper.bat in here
}
});
For reference, here's some documentation on Sencha Singletons. And one important note: make sure that your Helper singleton is in it's own file! If it's small, you may be inclined to put it at the bottom of your app.js, and things will work at first, and the build process will work, but the code will not. Don't worry, the build process puts all of your JS code in one big, compressed file anyway.

Resources