Skip to content

Instantly share code, notes, and snippets.

@apetro
Created May 31, 2015 16:48
Show Gist options
  • Select an option

  • Save apetro/ff0b71766ea40a8f630b to your computer and use it in GitHub Desktop.

Select an option

Save apetro/ff0b71766ea40a8f630b to your computer and use it in GitHub Desktop.
Notes about AngularJS explained using marketplace

AngularJS explained using Marketplace as end-to-end example.

A cartoonishly simplified history of the Web

Files

Hyperlinks

Dynamic Content

Forms

Web applications

Servlets

CSS

JavaScript

JSON

Modern architecture

Every web application is at least two web applications : a server-side application concerned with business logic, data integrity, transactions, access control; and a client-side application concerned with presentation and user experiences.

Servers shouldn’t generate markup, they should deliver true data and services.

Clients shouldn’t implement business logic, they should implement great user experiences for interacting with the data and services the server is providing.

Everyone gets to do what they’re good at using tools that are good at it.

Hurray!

AngularJS concepts as implmented in Marketplace

Okay, enough sermonizing, let’s look at some code.

Routing and getting there

First, we need to tell AngularJS how to map paths in the URL to functionality. Server-side, we did things like servlet-mappings in web.xml and naming JSPs and handler mappings in Spring Web MVC annotations and so forth.

But hey! We don’t have a server side, remember? We’re client side. So we need some client-side technology within the “single page application”.

HTML5 mode

HTML5 mode means pretty URLs the way they ought to be.

However, they create a goofy edge case where if a user follows such a URL cold without already having the single page app loaded, well, then they are going to issue a real HTTP request for that path. So you do need your server to be prepared to rewrite that request to point to the front page, thereby bootstrapping the single page application.

Routing

So, Marketplace isn’t our only module, so there’s a layer of indirection to contend with here. We tell the overall application what routes exist; we configure what those routes mean within the modules.

So, in main.js

  app.config([‘$routeProvider’, ‘$locationProvider’, function($routeProvider, $locationProvider) {
        $locationProvider.html5Mode(true);
        $routeProvider.
            when(‘/apps’, marketplaceRoutes.main).
            when(‘/apps/details/:fname’, marketplaceRoutes.details).
            when(‘/apps/search/:initFilter’, marketplaceRoutes.main).
            when(‘/features’, featuresRoute).
            …

And then in marketplace/routes.js

define([‘require’], function(require){

    return {
        main: {
            templateUrl: require.toUrl(‘./partials/marketplace.html’)
        },
        details: {
            templateUrl: require.toUrl(‘./partials/marketplace-details.html’), controller:’MarketplaceDetailsController’
        }
    }

});

Partials

Great, let’s follow that main route, that looks simple.

so my.wisc.edu/web/apps maps to marketplaceRoutes.main which maps to invoking the marketplace.html partial.

What’s a partial? It’s a chunk of html which makes up a part of the overall page.

This allows you to write markup templates in plain old HTML with “natural language template” features which is a fancy way of saying that when you look at the partial itself in a web browser, with no server or AngularJS fanciness or anything, it looks like something. (Contrast JSPs and for that matter XSLT.)

It looks like something. It’s valid HTML, at least inasmuch as a valid part of an HTML file. That doesn’t mean it looks good.

Still, you’re not far from slapping some CSS imports on this and hacking away on the HTML even if you’re a front end developer who knows absolutely nothing about AngularJS.

Human readable. Human editable. Directly renderable.

Token replacement

You don’t have to know AngularJS to read this, but there are some awesome templating features in here to take advantage of.

The first is token replacement. Got something dynamic to say? Of course you do. Here you go.

{{NAMES.title}}

Filtering

Showing {{ (portlets | filter:searchTermFilter | showApplicable:showAll | showCategory:categoryToShow).length }} of {{ portlets.length }} apps.
<div class=“portlet-container” ng-repeat=“portlet in portlets | filter:searchTermFilter | showApplicable:showAll | showCategory:categoryToShow | orderBy:sortParameter | limitTo:searchResultLimit” ng-class=“{portlet_hover: hover}” ng-mouseenter=“hover = true” ng-mouseleave=“hover = false” ng-click=“showDetails = !showDetails”>

Controllers

Okay, but where is this dynamic data coming from?

Well, in the good old days of server-side development, in say Spring Web MVC, you’re have written a Controller, in the Model View Controller architecture.

In AngularJS, you’ve still got a controller, it’s client-side.

<div ng-controller=“MarketplaceController as marketplaceCtrl” …```

So, here’s the portlets:

`marketplace/controllers.js`

app.controller(‘MarketplaceController’, [ ‘$sessionStorage’, …, ‘mainService’, function($sessionStorage,…, mainService) {

$scope.portlets = [];

marketplaceService.getPortlets().then(function(data) { $scope.portlets = data.portlets; $scope.categories = data.categories; });


Here’s a key insight: the set of portlet directory entries available to the user doesn’t change when the user clicks a different category.  What category the user wants to look at right now is an entirely user experience, client-side, display, view layer concern.  The data didn’t change.  So we don’t need a server round-trip to go ask the server for another filtering of this modest amount of data — we can do this filtering client-side.  That’s why you need client-side technology doing this.

### Services

Okay, okay, but at some point you *did* have to get the data from the server.  

We could go get that data in the controller (pro tip: don’t do that) but it would be nice to do that once and only once, with appropriate caching, across controllers.

So let’s abstract that out into a “service”.

What we said here:

app.controller(‘MarketplaceController’, [ …, ‘marketplaceService’, …, function(…, marketplaceService, …) {


is that `MarketplaceController` depends on marketplaceService.

`marketplace/services.js`



…


### Directives

Okay, so Services are one way to abstract out   
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment