Skip to main content
Ben Nadel at cf.Objective() 2009 (Minneapolis, MN) with: Curt Gratz
Ben Nadel at cf.Objective() 2009 (Minneapolis, MN) with: Curt Gratz

AngularJS 2 Beta 1: Hello World With ES5 And RequireJS

By
Published in Comments (10)

For the last week, I've been doing almost nothing in my spare time but reading up on the AngularJS 2.0 Beta. As a long-time lover of AngularJS, I am very excited about the AngularJS 2 future; but, learning it has been a very humbling experience. I feel like I am starting over from zero - like nothing I know has remained relevant. After many hours of reading, feeling depressed, crying in the corner, getting angry on Twitter, and messing with code, I was finally able to cobble together an ES5 "hello world" demo for AngularJS 2 using RequireJS.

Run this demo in my JavaScript Demos project on GitHub.

First, a HUGE THANKS to Pascal Precht, Christoph Burgdorf, Torgeir Helgevold, and Jesus Rodriguez, who have already put a ton of great AngularJS 2.0 content out into the wild; and, without whom I probably would have given up a few days ago!

And of course, another huge thanks to the AngularJS team and to Ward Bell and John Pappa who I believe have been working tirelessly on the documentation (sorry if I missed anyone obvious - my brain is fried).

Ok, platitudes aside, my goal with this adventure was to try and to do the following:

  • Create a one-page AngularJS 2 demo (in which all the code is present in one file).
  • Write the code in ES5 (since there won't be an opportunity to transpile code in a one-page demo).
  • Define the classes in the order that is most readable.

I wanted to create a one-page demo because, frankly, that's usually how I write my demoes. I did it with AngularJS 1.x; I did it with ReactJS (and the runtime JSX transpiler); and, I'd like to do it with AngularJS 2.x. For me - and this may only be for me - I find code easier to follow when it's all in one file. When I have to start jumping back and forth between multiple files, when trying to learn something new, I get befuddled very quickly.

Writing ES5 is sort of a byproduct of the previous goal. But also, I think ES5 is going to be a helpful tool in demystifying how AngularJS 2 apps are actually wired together. Let's face it, when you learn AngularJS 2, you're not just learning AngularJS 2 - you're also learning ES6, TypeScript, and some sort of module loader. For me, it's easy to lose sight of which technology is handling which concern. By forcing myself to use ES5 (for at least a while), I need to remove all the syntactic sugar and see how AngularJS 2 components are truly defined.

And lastly, I wanted to be able to write my components in any order. In AngularJS 1.x, the only thing that mattered, for the most part (looking at you .decorator()), was that all the things were defined before the bootstrapping. In AngularJS 2, however, with a one-page demo, this is much harder because the constructors themselves are used as the tokens for dependency-injection (it's no longer string-parsing magic). As such, it's easy to run into a situation in which you are defining a dependency using a component reference that is undefined at the time. To get around this problem, I'm using AlmondJS (which is basically RequireJS) to manage the referential integrity. It also implicitly defers bootstrapping until all the dependencies are loaded (the way I'm using it), which is player.

In the end, I was able to get it to work. But, as you are about to see, it ain't pretty! While we gain a lot of flexibility in AngularJS 2, we lose a ton of the facilitation that was built into AngularJS 1.x. The AngularJS 2 Beta does have some nice ES5 methods, no doubt; but, I think there is definitely some room for improvement.

The demo is rather simple. It's a root component (AppComponent), a sub component (FriendComponent), and a service that loads friend data (FriendService):

<!doctype html>
<html>
<head>
	<meta charset="utf-8" />

	<title>
		AngularJS 2 Beta 1: Hello World With ES5 And RequireJS
	</title>
</head>
<body>

	<my-app>
		Loading...
	</my-app>

	<!-- Load demo scripts. -->
	<script type="text/javascript" src="./es6-shim.min.js"></script>
	<script type="text/javascript" src="./Rx.umd.min.js"></script>
	<script type="text/javascript" src="./angular2-polyfills.min.js"></script>
	<script type="text/javascript" src="./angular2-all.umd.min.js"></script>
	<!--
		Putting Almond.js after the external libraries since I am not managing their
		loading through Almond. If I move this library up, AngularJS attempts to use
		define() (I think) which I am not configured for in this demo.
		--
		NOTE: AlmondJS is just a simplified version of RequireJS.
	-->
	<script type="text/javascript" src="./almond.js"></script>
	<script type="text/javascript">

		// In order to have all my code in one page and to not have to care about the
		// order in which things are defined, I am using AlmondJS (ie, RequireJS) to
		// manage the registration of "class files" (so to speak). This is more aligned
		// with how a class loader would work.
		requirejs(
			[ "AppComponent", "FriendService" ],
			function( AppComponent, FriendService ) {

				// When bootstrapping the application, we have to configure the root
				// injector to be able to provide the given dependencies.
				ng.platform.browser.bootstrap(
					AppComponent,
					[
						FriendService
					]
				);

			}
		);


		// --------------------------------------------------------------------------- //
		// --------------------------------------------------------------------------- //


		define(
			"AppComponent",
			[ "FriendService", "Friend" ],
			function( FriendService, Friend ) {

				// Configure the App component definition.
				var AppComponent = ng.core
					.Component({
						selector: "my-app",
						directives: [ Friend ],
						template:
						`
							<h1>
								My First AngularJS 2 App!
							</h1>

							<div *ngIf="! isLoading">

								<h2>
									You Have {{ friends.length }} Friends!
								</h2>

								<ul>
									<li *ngFor="#friend of friends">

										<friend
											[id]="friend.id"
											[name]="friend.name">
										</friend>

									</li>
								</ul>

							</div>
						`
					})
					.Class({
						constructor: AppController,
						ngOnInit: function noop() {} /* Will be discussed in another post. */
					})
				;

				// We need to add meta-data to the component definition so that the
				// AngularJS injector knows how to map injectables on to the Controller
				// constructor arguments.
				AppComponent.parameters = [ new ng.core.Inject( FriendService ) ];

				return( AppComponent );


				// I control the app component.
				function AppController( friendService ) {

					var vm = this;

					// I determine if the data is being loaded.
					vm.isLoading = true;

					// I hold the collection of friends.
					vm.friends = null;

					// Expose public methods.
					vm.ngOnInit = ngOnInit;


					// ---
					// PUBLIC METHODS.
					// ---


					// I get called once, after the component has been instantiated and
					// after the inputs have been bound.
					function ngOnInit() {

						friendService.getFriends().then(
							function handleResolve( friends ) {

								vm.isLoading = false;
								vm.friends = friends;

							}
						);

					};

				}

			}
		);


		// --------------------------------------------------------------------------- //
		// --------------------------------------------------------------------------- //


		define(
			"Friend",
			function() {

				// Configure the friend component definition.
				var FriendComponent = ng.core
					.Component({
						selector: "friend",
						inputs: [ "id", "name" ],
						template:
						`
							<span data-id="{{ id }}">
								{{ name }} is awesome!
							</span>
						`
					})
					.Class({
						constructor: FriendController
					})
				;

				return( FriendComponent );


				// I control the friend component.
				function FriendController() {

					// ... nothing to do here, really. Just let the inputs "work".

				}

			}
		);


		// --------------------------------------------------------------------------- //
		// --------------------------------------------------------------------------- //


		define(
			"FriendService",
			function() {

				// Configure the friend service definition.
				var FriendServiceClass = ng.core.Class({
					constructor: FriendService
				});

				return( FriendServiceClass );


				// I provide access to the friend repository.
				function FriendService() {

					// Build the internal cache of friends.
					var friends = buildFriends( "Sarah", "Joanna", "Tricia" );

					// Return the public API.
					return({
						getFriends: getFriends
					});


					// ---
					// PUBLIC API.
					// ---


					// I get all the friends. Returns a promise.
					function getFriends() {

						return( Promise.resolve( friends ) );

					}


					// ---
					// PRIVATE API.
					// ---


					// I turn the given list of names into a collection of friend objects.
					function buildFriends( /* ...names */ ) {

						var id = 0;

						var collection = Array.prototype.slice.call( arguments ).map(
							function iterator( name ) {

								return({
									id: ++id,
									name: name
								});

							}
						);

						return( collection );

					}

				}

			}
		);

	</script>

</body>
</html>

One thing that is likely to jump out at you immediately is the fact that I am still using AngularJS 1.x style-guide approaches, such as using "vm" to keep a reference to the Controller instance. Part of me is doing this because it's what I know. But, a bigger part of me is doing this in order to really explore how everything fits together. If all I do is follow the prototypal inheritance approach outlined in most demoes, I will very likely lose sight of the actual mechanics of it all. As with using ES5, I'm using the module patterns as "tool" to help me learn more better.

When we run this code, we get the following page output:

Hello world with AngularJS 2 Beta 1 using RequireJS.

It may be ugly, but it worked like a charm!

I'm not going to offer any further explanation in this blog post since I'm still basically naive to all things AngularJS 2.0. This was just to get my feet wet and to plant my flag in the AngularJS 2.0 community. Hopefully, in the future, I can actually write up some posts that provide more practical value.

Want to use code from this post? Check out the license.

Reader Comments

247 Comments

React is soooOooo much more straight forward. I do plan to spend time learning ng2 though, because I'm super interested in exploring the coupling of ng2+NativeScript.

15,902 Comments

@Fergus,

Ah, very cool - I didn't realize that was a possibility. The only drawback to that is that I lose all the color coding since "module" isn't something the IDE or the Gist is looking for. This is the same problem I had with the in-browser JXS compiler.

@Chris,

ReactJS is definitely simpler; but, at the same time, it is doing less. AngularJS has to handle all the dependency injection and dealing with things like properties vs. attribute interpolation; so I think it just has to do more heavy lifting.

That said, event AngularJS 1.x seems to have less "stuff" going on.

2 Comments

Hi Ben,

I have always been a follower since early Angular 1 posts. I love everything about what you do, the choice of the topics and how you add a special piece or asking a special question (Not many others talked about event propagation with Angular 1 for example), as well as the way you present them.

A small question though. You mentioned that the promise can be polyfilled, I get that, but how about the string literals you use for the `template` fields. I'm quite certain that these are ES6 string literals not the normal single-line ES5 literals.

What am I missing here?

Thanks a lot.

20 Comments

@Mohamed

You can't polyfill syntax (afaik). However, most evergreen browsers support many E56 features - template strings being one of the more ubiquitous.

http://kangax.github.io/compat-table/es6/#template_strings

Pity we can't rely on that in most production apps, hence transpilers.

2 Comments

@Fergos

That is my understanding as well. That it only worked because it was tested in a browser that supports this ES6 feature.

It doesn't make it ES5 though, which is what the blog post is about. That's why I thought maybe I'm missing something.

15,902 Comments

@Mohamed, @Fergus,

Thanks for the kinds words! And, great question. You are correct in that the `` notation cannot be polyfilled; at least, not without some sort of transpiler that compiles it down to string concatenation. Fortunately, it works in Chrome and Firefox... and I think even Safari.

I think the only one still out in the cold is IE - looking at the chart Fergus linked to, it looks like Edge supports it, but IE 11 did not. So, support it at least getting much better.

I hate to use things in the demo that aren't fully supported; but, with AngularJS 2, I am not sure there is any other way to keep it all in one page (something that is fairly important to me).

That said, I wonder if "ng-template" still works? I didn't see it in the documentation anywhere. Hmmmm.

20 Comments

> I am not sure there is any other way to keep it all in one page

If it's just the `` string templates that offend ES5, you can still use plain ole multiline "aaa" + "bbb" + .... (though obviously far less readable).

1 Comments

Hi Ben:

I'm following this example combined with your Rendering large data sets with angular and react (www.bennadel.com/blog/3016-rendering-large-datasets-with-angular-2-beta-3-and-reactjs-0-14-7.htm) i figured out the way to consume an API with the react example instead of calling the generateGrid function, but as i don't know much about requireJS it's getting me really difficult to call the http component in angular 2 can you help me please?

thanks in advance

1 Comments

How to run locally in firefox (My PC without server) without error
System.js could not bootstrap the app.
http://bennadel.github.io/JavaScript-Demos/demos /app-content-angular2, demos / block-animation-angular2 / without-state / index.htm, ...
Apps with typescript (.ts)

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel