Commit 86139425 authored by Gerrit Hübbers's avatar Gerrit Hübbers 🃏
Browse files

Add walkthrough through AngularJS frontend app.

parent b7b40210
......@@ -226,6 +226,51 @@ The`yeoman-maven-plugin` runs the following commands in *this* project's root di
* `usemin`: this [grunt-usemin](https://github.com/yeoman/grunt-usemin#the-usemin-task) task investigates all HTML files within the `/src/main/webapp/dist/` directory (previously copied there during the `copy:dist` target execution). The task will find references to unconcatenanted, unrevved assets (JS, CSS, images), then replace these references with the concatenated single-bundles-and-revved filenames.
* `htmlmin`: this [`grunt-contrib-htmlmin`](https://github.com/gruntjs/grunt-contrib-htmlmin) task takes all `/src/main/webapp/dist/*.html` files and html-minifies them in-place.
# Walkthroughs and architectures
## Front-end AngularJS walkthrough
The Spring backend serves out, via HTTP, the (processed) contents of directory `src/main/webapp/`, where `index.html` is located. `index.html` in turn references all (minified and concatenated) JavaScript assets, including file `app.js`.
### `app.js`
`app.js` creates a new module, `ddaApp`, and provides all of its dependent modules. The `ddaApp` module:
* configures the *cache buster* service and *$httpProvider*'s CSRF token name.
* configures the abstract *root* state named `site`.
* configures the AngularUI view `navbar@` for the [root unnamed template `index.html`](https://github.com/angular-ui/ui-router/wiki/Multiple-Named-views#view-names---relative-vs-absolute-names). Also, `$stateProvider` is configured in such a way to `resolve`-inject a dependency named `authorize` - an alias for `Auth` service's `authorize()` method.
* configures `$httpProvider` interceptors which
* in error conditions emit an error event on `$rootScope` (`errorhandler.interceptor.js`);
* redirect to login and retry on CSRF-missing responses (`auth.interceptor.js`); and
* trigger the `AlertService` if an `X-ddaApp-alert` response header is present (`notification.interceptor.js`).
Once all these modules have been loaded and configured, it runs an initialization function:
It sets `$rootScope`s `ENV` and `VERSION` (as generated to file `app.constant.js`).
It registered an AngularUI state change listener, which sets the requested `toState` in the `$rootScope` and introduces a router hook for further AngularUI route manipulation: The hook will check if the user is authenticated or not, probably route the user differently (e.g., redirecting a logged-in user from requested `login` state to `home` state), and possibly routing the user to `accessdenied` state in case they are missing the required authorization. Also, the application's routing behavior is augmented so that the window's title reflects the current route's `data.pageTitle`; and unknown routes get redirected to `/`.
AngularUI's router is configured by configuring the `$stateProvider`. The jHipster convention is to have a single `x.js` file for each unique state `x`. That `x.js` file then calls module `ddaApp` `config(..)` injector to set up this state.
This convention is used for example in file `main.js`: it configures a state `home` (inheriting from state `site`).
As state `home` configures its `url` to be `/` (or synonymously `index.html`), this state is the initial state.
`index.html` provides two AngularUI view placeholders: `navbar` and `content`. The `home` state fills the `content` view with `main.html` and uses the `MainController`. This controller adds to its `$scope` the user's account data promise and the `isAuthenticated()` method. All children scopes will inherit these properties.
`main.html` provides an *a href* to `#/login`. Clicking this link will command the AngularUI to go to state `login`. Also this route is conventionally configured in file `login.js`.
State `login`'s ancestors are `account -> site`. State `login` adds as [*custom state data*](https://github.com/angular-ui/ui-router/wiki#attach-custom-data-to-state-objects) an empty `authorities` array and a custom `pageTitle`. Also, the `login` state fills into the `content` view `login.html` with the `LoginController`.
What's up with that `authorities` array?
In jHipster's convention, each state can have as state data an array `data.authorities`, containing entries for all authority roles that each is permitted to access this state. This rule got activated when `app.js` configured the AngularUI router to call `Auth.authorize()` whenever an `$stateChangeStart` event is received. If the `data.authorities` array is empty, then this means that no authorities are required - therefore `login` can always be accessed for unauthenticated visitors.
Let's assume we have successfully logged in and we are back at the route `/`. The `main` state configures for the `content@` view `templateUrl=main.html`; and (transitively via the parent `site` (`app.js`) state) for the `navbar@` view `templateUrl=navbar.html`.
Let's have a look at `navbar.html`: `NavbarController` provides to its scope the methods for checking `Principal.isAuthenticated()` and `Auth.logout()`. Therefore, these methods can be referenced within `navbar.html`, e.g. with the attributes `ng-click="logout()"` or `ng-switch="isAuthenticated()"`. Depending on the `isAuthenticated()` result, specific DOM elements are added or removed from the DOM. So for instance, only if `isAuthenticated()` evaluates to `false` will the *Sign in* and *Register* entries appear in the *Account* navbar section.
When logged in, the *Entities* navbar section will show only those entity types for which the currently logged-in user has permissions to interact with. This behavior is defined by the `has-authority` attribute directive (file `authority.directive.js`). That directive registers a listener (`scope.$watch(..)`). That listener is fired everytime a *digest cycle* is triggered by the AngularJS framework. Whenever `Principal.isAuthenticated()`'s evaluation result changes between two consecutive *digest cycles* (so either going from `true` to false; or from `false` to `true`) will that directive's behavior be executed (i.e., add or remove the `hidden` class).
Let's now assume we are logged in, we are currently on the root `home` state, then are about to select the *Bundles source* entry. Let's further assume we have a valid access-granting authority.
`navbar.html` has for the *Bundles source* declared the attribute `ui-sref="bundlesSource"`. That means that as soon as we click on *Bundles source*, the AngularUI router will start moving the router to state `bundlesSource`.
State `bundlesSource` is defined in file `bundlesSource.js`, with a `url=/bundlesSources`. The state hierarchy is `bundlesSource -> entity -> site`. Also this state fills the `content@` view with its own template `bundlesSources.html` and controller `BundlesSourceController`. That controller interacts with the `BundlesSource` $resource REST service (`bundlesSource.service.js`) - e.g., everytime this controller is activated, it will call `$scope.loadAll()`, concurrently populating the `$scope.bundlesSources` array with data returned from the remote REST endpoint.
[JHipster]: https://jhipster.github.io/
[Node.js]: https://nodejs.org/
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment