Code Monkey home page Code Monkey logo

angular2-es5-router-post's Introduction

Angular 2 Routing with JavaScript

As I sit here and write this, Angular 2 is in alpha 26 which means it is early days for the framework. The existing examples are sparse and typically written in TypeScript. As far as I can tell, there are actually no examples that employ both the router and JavaScript.

Note: The Angular 2 docs refer to code that runs in the browser as "ES5." That's crap. The code that runs in a Web browser is called JavaScript.

Optional: Serving Static Files

Let's get started with an empty directory so you can see that we have nothing up our sleeves. No build steps, no web server, just an empty directory and a few static files.

  • make the folder ng2-js-routing
  • within that folder create
    • index.html
    • index.js
ng2-js-routing
├── index.html
└── index.js

For this next step, we're going to throw some basic markup in there just to make sure we're looking at the right file.

index.html

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    This file is located in <em>ng2-js-routing/index.html</em>
  </body>
</html>

Open index.html in a browser and you should see something like this.

Groovy. Now for something simple from that JavaScript file

index.html

<!DOCTYPE html>
<html>
  <head>
    <script src="index.js"></script>  <!-- highlight -->
  </head>
  <body>
    This file is located in <strong>ng2-js-routing/index.html</strong>
  </body>
</html>

index.js

console.log('ng2-js-routing/index.js loaded')

Back in the browser, open up the JavaScript console and refresh the page. You should see something like:

Great, now we know that we're looking at the right files in the browser. I've wasted so many hours being frustrated by an unchanging webpage only to realize later that I'm editing my development files but refreshing the production website.

Q: should we add a explanation of document.addEventListener('DOMContentLoaded' ?

Optional: File Load Order

Let's add the Angular 2 source and bootstrapping boilerplate. First, the source.Let's change index.html's console.log message to show us the angular global object.

index.js error condition

console.log('ng2-js-routing/index.js loaded')
console.log(angular)

Let's correct that now.

As expected, we see an error because we've not included angular in our web app. If it's an object we have it'll look more like this:

I want to verify that angular isn't included in the web app at all rather than it just not being in the app at the time that index.js is run, so I'm going to ask our console to display the angular object one more time.

Yup, that's the error I was hoping for. It's important to not only see the errors we expect but see the errors for the reason we expect them. Case in point, let's add angular to our app, but we'll mess it up and include it after index.js.

index.html error condition

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    This file is located in <strong>ng2-js-routing/index.html</strong>
    <!-- FIXME: angular needs to be loaded before we may reference it. -->
    <script src="index.js"></script>
    <script src="https://code.angularjs.org/2.0.0-alpha.26/angular2.sfx.dev.js"></script>
  </body>
</html>

Resulting in

Same error, different reason. Let's ask the JavaScript console for the angular object again.

If you are getting a refrence error in one of your JavaScript files for a global object like angular, but you don't see the same error in the JavaScript console, then you have a load order issue. I mention this because Angular 1 helps the developer out by providing the angular.module method. In contrast, Angular 2 provides no help with file load order which might come as a shock to developers that are planning on using the new framework with vanillia JavaScript and minimal tooling.

Let's fix the error by swapping the order of our script tags.

index.html

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    This file is located in <strong>ng2-js-routing/index.html</strong>

    <script src="https://code.angularjs.org/2.0.0-alpha.26/angular2.sfx.dev.js"></script>
    <script src="index.js"></script>
  </body>
</html>

Which clears the error condition.

Zach: Looking at this now, I think the optional file load order section should happen when people are likely to see it, when actually loading Angular 2 components. The secion in its current state is nice in that it's clear, file load order isn't being taught at the same time as anything else. It has the downside of not being realistic, though. People are unlikely to see the load order issue this early therefore this example doesn't ring true.

Onto bootstrapping the app.

Bootstrapping the app

ng2-js-routing
├── app
│   ├── app.css
│   ├── app.html
│   └── app.js
├── index.html
└── index.js

index.html

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <app>
    </app>
    
    <script src="https://code.angularjs.org/2.0.0-alpha.26/angular2.sfx.dev.js"></script>
    <script src="app/app.js"></script>
    <script src="index.js"></script>
  </body>
</html>

app.html

This file is located at <strong>app/app.html</strong>

app.js

function AppComponent() {}

AppComponent.annotations = [
  new angular.ComponentAnnotation({
    selector: 'app'
  }),
  new angular.ViewAnnotation({
    templateUrl: 'app/app.html'
  })
];

index.js

document.addEventListener('DOMContentLoaded', function() {
  console.log(AppComponent)
});

document.addEventListener('DOMContentLoaded', function() {
  angular.bootstrap(AppComponent);
});

server

app.css

app .container {
  display: -webkit-flex;
  display: flex;
}
app nav {
  width: 200px;
}
app .main {
  -webkit-flex: 1;
  flex: 1;
}

app.html

<div class="container">
  <nav>
    <ul>
      <li>
        <a href="">Home</a>
      </li>
      <li>
        <a href="">About</a>
      </li>
    </ul>
  </nav>

  <div class="main">
  </div>
</div>

Inject the Router

index.js

document.addEventListener('DOMContentLoaded', function() {
  angular.bootstrap(AppComponent, [
    angular.router.routerInjectables
  ]);
});

app.js

function AppComponent(router) {
  console.log(router)
}

AppComponent.parameters = [[angular.router.Router]]

AppComponent.annotations = [
  new angular.ComponentAnnotation({
    selector: 'app',
    injectables: [angular.router.Router]
  }),
  new angular.ViewAnnotation({
    templateUrl: 'app/app.html'
  })
];

Configure The Router

ng2-js-routing
├── app
│   ├── app.css
│   ├── app.html
│   ├── app.js
│   └── home
│       └── home.js
├── index.html
└── index.js

app.js

function AppComponent(router) {
  router.config([{
    path: '/',
    component: Home,
    as: 'home'
  }])
}

Notice that in home.js we need to add a ComponentAnnotation, otherwise Angular will complain that the given constructor isn't a component. In some ways, the ComponentAnnotation is the only thing distinguishing an Angular component from any other plain old JavaScript constuctor function.

home.js

function HomeComponent() {
  console.log('HomeComponent instantiated')
}

HomeComponent.annotations = [
  new angular.ComponentAnnotation({
  })
]

Now let's add another route and another component.

.
├── app
│   ├── about
│   │   └── about.js
│   ├── app.css
│   ├── app.html
│   ├── app.js
│   └── home
│       └── home.js
├── index.html
└── index.js

app/about.js

function AboutComponent() {
  console.log('AboutComponent instantiated')
}

AboutComponent.annotations = [
  new angular.ComponentAnnotation()
]

Now we'll configure the about route.

app.js

function AppComponent(router) {
  router.config([{
    path: '/',
    component: HomeComponent,
    as: 'home'
  },{
    path: '/about',
    component: AboutComponent,
    as: 'about'
  }])
)
}

Note that we're using a hash in the about route's path so we can avoid configuring the server for push state.

And since we're simply linking static js files, we need to modify index.html to load about.js

index.html

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <app>
    </app>

    <script src="https://code.angularjs.org/2.0.0-alpha.26/angular2.sfx.dev.js"></script>
    <script src="app/about/about.js"></script>
    <script src="app/home/home.js"></script>
    <script src="app/app.js"></script>
    <script src="index.js"></script>
  </body>
</html>

Now when we point the browser to http://localhost:8080/#/about we see Angular has loaded the AboutComponent.

Note: I've not actually been successful in this yet. Theoretical.

Add Outlet

Now we're going to add an outlet so we can see what happens when the link changes. First, we'll inject the Outlet directive.

app.js

function AppComponent(router) {
 router.config([{
    path: '/',
    component: HomeComponent,
    as: 'home'
  },{
    path: '/about',
    component: AboutComponent,
    as: 'about'
  }])
}

AppComponent.parameters = [[angular.router.Router]]

AppComponent.annotations = [
  new angular.ComponentAnnotation({
    selector: 'app',
    injectables: [angular.router.Router]
  }),
  new angular.ViewAnnotation({
    templateUrl: 'app/app.html',
    directives: [
      angular.router.RouterOutlet
    ]
  })
];

Now we'll add the outlet to our HTML.

app.html

<div class="container">
  <nav>
    <ul>
      <li><a href="">Home</a></li>
      <li><a href="">About</a></li>
    </ul>
  </nav>

  <div class="main">
    <router-outlet></router-outlet>
  </div>
</div>

Finally, we'll add 2 html files.

├── app
│   ├── about
│   │   ├── about.html
│   │   └── about.js
│   ├── app.css
│   ├── app.html
│   ├── app.js
│   └── home
│       ├── home.html
│       └── home.js
├── index.html
└── index.js

app/home/home.html

This file is located at <strong>app/home/home.html</strong>

app/about/about.html

This file is located at <strong>app/about/about.html</strong>

Now we need to tell the home component where to get its template.

app/home/home.js

function HomeComponent () {
}

HomeComponent.annotations = [
  new angular.ComponentAnnotation({
  }),
  new angular.ViewAnnotation({
    templateUrl: 'app/home/home.html'
  })
]
function AboutComponent() {
}

AboutComponent.annotations = [
  new angular.ComponentAnnotation()
  new angular.ViewAnnotation({
    templateUrl: 'app/about/about.html'
  })
]

Router-Link

Next we need to be able to link to our routes. To do this we'll inject and then use the router-link directive.

app.js

function AppComponent(router) {
 router.config([{
    path: '/',
    component: HomeComponent,
    as: 'home'
  },{
    path: '/about',
    component: AboutComponent,
    as: 'about'
  }])
}

AppComponent.parameters = [[angular.router.Router]]

AppComponent.annotations = [
  new angular.ComponentAnnotation({
    selector: 'app',
    injectables: [angular.router.Router]
  }),
  new angular.ViewAnnotation({
    templateUrl: 'app/app.html',
    directives: [
      angular.router.RouterLink,
      angular.router.RouterOutlet
    ]
  })
];

app.html

<div class="container">
  <nav>
    <ul>
      <li><a router-link="home">Home</a></li>
      <li><a router-link="about">About</a></li>
    </ul>
  </nav>

  <div class="main">
    <router-outlet></router-outlet>
  </div>
</div>

Basic Route Params

Create 2 new files. app/posts/post.js and app/posts/post.html.

.
├── app
│   └── posts
│       ├── post.html
│       └── post.js

Add posts.js to index.html

index.html

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <app>
    </app>

    <script src="https://code.angularjs.org/2.0.0-alpha.27/angular2.sfx.dev.js"></script>
    <script src="app/posts/post.js"></script>
    <script src="app/about/about.js"></script>
    <script src="app/home/home.js"></script>
    <script src="app/app.js"></script>
    <script src="index.js"></script>
  </body>
</html>

Add the new route to app.js

function AppComponent(router) {
  router.config([{
    path: '/',
    component: HomeComponent,
    as: 'home'
  },{
    path: '/about',
    component: AboutComponent,
    as: 'about'
  },{
    path: '/posts/:id',
    component: PostComponent,
    as: 'post'
  }])

Now we're going to add the minimum JavaScript to make our PostComponent

app/posts/post.js

function PostComponent() {}

PostComponent.annotations = [
  new angular.ComponentAnnotation()
]

Add router-links with params to our template.

app/app.html

<div class="container">
  <nav>
    <ul>
      <li><a router-link="home">Home</a></li>
      <li><a router-link="about">About</a></li>
      <li><a router-link="post" [router-params]="{id: 1}">Post 1</a></li>
      <li><a router-link="post" [router-params]="{id: 2}">Post 2</a></li>
    </ul>
  </nav>

  <div class="main">
    <router-outlet></router-outlet>
  </div>
</div>

Then inject RouteParams into the PostComponent.

app/posts/post.js

function PostComponent(routeParams) {
  console.log(routeParams.params)
}


PostComponent.parameters = [[angular.router.RouteParams]]

PostComponent.annotations = [
  new angular.ComponentAnnotation({
    injectables: [angular.router.RouteParams]
  })
]

angular2-es5-router-post's People

Contributors

theotherzach avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.