Skip to content
This repository has been archived by the owner on Dec 30, 2019. It is now read-only.

Bump angular to 2.4.x #3

Merged
merged 7 commits into from
Dec 21, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -28,6 +28,10 @@ npm install -g yarn
Alternatively, you might use good old `npm`, if you REALLY want to. If that is the case, just replace the `yarn` part of the commands listed below with `npm`.

## Project structure
The intended project structure, how to work with it and possibly extend it is documented in the [docs folder](https://github.com/DorianGrey/ng2-webpack-template/tree/master/docs).

- [General structure](https://github.com/DorianGrey/ng2-webpack-template/blob/master/docs/general_structure.md)
- [The application state and how to extend it](https://github.com/DorianGrey/ng2-webpack-template/blob/master/docs/app_state.md)

-- TODO --

92 changes: 92 additions & 0 deletions docs/app_state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# The application state
The application's globally relevant state is stored using a store from the [ngrx/store](https://github.com/ngrx/store) library. It's content is described in `src/app.store.ts`, in the interface `AppState`:

export interface AppState {
todos: List<Todo>;
watchTime: number;
}

It is not required to define an interface for describing your application state, but it simplifies type safe injection of the application's store. E.g., in `src/todos/todo.service.ts`, you will see an injection like this:

store: Store<AppState>

Without defining an explicit interface, it would be required to use `any` here.

To properly work with your application state during runtime, you need to define a set of `reducers` for each of its entries. In our case, this is also defined in `src/app.store.ts`:

const reducers = {
todos: todosReducer,
watchTime: watchTimeReducer
};

This hash of reducers gets combined to a single root reducer using the `combineReducers` helper function from `ngrx/store`.

You might have recognized that the name of the keys used in the reducers object and the interface definition is equivalent. This is *intended*, since it simplifies to understand how an entry of the interface is mapped to its counterpart in the reducers objects. We strongly recommend to keep follow this convention.

Please read the source documentation in `src/app.store.ts` to get more detailed information about the values and structures used in there.

# How to extend it
Extending the application's state is easier than it appears - we'll go through this process in this section.

First, you should create a `[component-or-module-name].store.ts` along the component or module file that this part of the application state belongs to or is primarily used by. State parts that do not refer to a particular component or module should be added to the global definitions in `src/app.store.ts`.

To properly deal with the state itself, you need to define a list of actions that may alter that state. It most cases, it is sufficient to use an enum or a hash with string fields inside. E.g., for the `todos` page, we've use the latter version like:

export const ACTION_TYPES = {
ADD_TODO: "ADD_TODO"
};

In case of the hash strategy, don't forget to properly freeze this object to prevent its accidental modification:

Object.freeze(ACTION_TYPES);

This is not required for (const) enums, since they cannot be modified during runtime. However, this might need some more `.toString()` calls, since the actions dispatched by the stored have to be identified by strings.

Next, you need to define an initial value for your state. This value will be used when your application gets started, before the first dispatch is executed. In the example above, we've used an empty list of `Todo` entries:

const initialTodoList = List.of<Todo>();

For those who argued: This template uses [immutable-js](https://facebook.github.io/immutable-js/), since it simplifies working with immutable data structures. This comes in handy when defining the proper state mutation for states using non-trivial types.

Go ahead with defining a proper `reducer` for your state. A `reducer` receives two parameters:
- The current state for the particular entry.
- The requested action. This parameters contains two fields:
- `type` is one of your defined actions names. In the example above, `"ADD_TODO"` would be the only possible value. Take care that you define your initial state as the default value of this parameter to get things to properly work on startup and hot reload.
- `payload` is an optional value referring to this action. In the example above, this would be a `Todo` instance that should be added to the list of todos.

The reducer is responsible for properly evaluating these parameters and - in case it accepts the provided action type - returning a new application state. If you want things to work in a reasonable manner, you should take care of two aspects:
1. **Never** alter the state that is provided here!
2. **Always** return a new object containing your state!

If you want to omit at least one of these aspects... don't tell anyone you've not been warned.

In the example mentioned above, the reducer looks like:

export const todosReducer: ActionReducer<any> = (state: List<Todo> = initialTodoList, action: Action) => {
switch (action.type) {
case ACTION_TYPES.ADD_TODO:
return state.push(action.payload);
default:
return state;
}
};

As the last step, add your state part to the global `AppState` definition and the corresponding reducer to the global one. Oh, and take care that your reducer and your set of action types is properly exported, to be usable outside of the definition file.

Once you did all this stuff, you are ready to select your new state part from the injected `Store` instance:

store.select(state => state.todos)

# Optional: Use action creators

While exploring the file that we picked the examples from, you might have recognized a so-called `ActionCreator`:

export class TodoActionCreator {
add: (todo: Todo) => Action = todo => {
return {type: ACTION_TYPES.ADD_TODO, payload: todo};
};
}

First of all, using these constructs is entirely optional. However, it simplifies the process of creating mutation actions for your stated, since it hides the concrete structure of the action that gets dispatched by the store. Also, it adds some expressiveness.

If you decide to use these, don't forget to add them as providers, so that you can properly inject them. Alternatively, since the action creators themselves do not contain any kind of state, you can boil them down to simple helper functions placed in your module.
26 changes: 26 additions & 0 deletions docs/general_structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# General template structure
We'll only go through the most important files and folders here, so that you can get an idea of how the template is structured and how you might adopt it to your particular requirements.

## Files
- **.stylelintrc** contains the configuration used for [stylelint](https://github.com/stylelint/stylelint). We've picked up a useful set of defaults for `.scss` files.
- **karma.conf.js** contains the configuration used for [karma](https://github.com/karma-runner/karma). Note that the particular configuration differs between development and production mode, which is handled by the script itself by evaluating the `NODE_ENV` environment variable.
- **tsconfig.json** and **tsconfig.aot.json** contain the TypeScript configurations for JiT and AoT mode. While reading it, you might recognize that we've enabled a rather strict set of compiler options, like `noImplicitAny`, `noImplicitReturns` and `noImplicitThis`. This might appear extremely restrictive at first, but we've had good experience with this setup. It assists in keeping track and working around several issues, inconsistencies and curious behaviors that especially developers with just a few or even no experience with TypeScript are rather often facing early.
- **tslint.json** contains the configuration for [tslint](https://github.com/palantir/tslint). Once again, a rather strict one, but for the same reasons as we had for the TypeScript configs.

## Folders
- **dev** contains the utility libs used by this template, e.g. for linting the sources and compiling the translations.
- **bin** contains the binary counterpart to these utilities, so that they can be used by `npm` scripts.
- **docs** is obviously the folder containing this documentation.
- **example-dist-server** contains an exemplary production server using [express](http://expressjs.com) and a corresponding proxy config. You might use this as an example, or as basis for setting up a frontend server for your project.
- **src** contains the application source itself.
- **styles** is intended for all styles that affect the application's style globally.
- **app** contains the modules used by our application; one folder per module. Folders with a `+` prefix indicate modules that are loaded on demand via routing.
- **webpack** contains the various webpack config files for the different build modes.

## Temporary folders (not tracked via git)
- **.awcache** is used by [awesome-typescript-loader](https://github.com/s-panferov/awesome-typescript-loader) for caching its build output.
- **.tmp** gets created during development. It contains the webpack DLLs generated for vendor and polyfill libraries.
- **dist** gets created by using `yarn run dist` or `yarn run dist-server` and contains the generated bundles, stylesheets and the particular `index.html` file.
- **dist-aot** contains the same as **dist**, but is the destination folder when using AoT (i.e. `yarn run dist:aot` or `yarn run dist-server:aot`).
- **test-results** contains the output of the `coverage` and `junit` reporters.
- **src/generated** is the output folder of the `translations` tasks and contains a typescript source file exporting all translations that are defined for the applications as a default-exported object with a top-level key for each language.
36 changes: 18 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
@@ -38,14 +38,14 @@
"translations:dev": "npm run translations:dist -- -w"
},
"devDependencies": {
"@angular/compiler-cli": "2.2.4",
"@angular/platform-server": "2.2.4",
"@angular/compiler-cli": "^2.4.0",
"@angular/platform-server": "^2.4.0",
"@angularclass/hmr": "1.2.2",
"@angularclass/hmr-loader": "3.0.2",
"@ngtools/webpack": "^1.1.9",
"@ngtools/webpack": "^1.2.1",
"@types/core-js": "^0.9.35",
"@types/jasmine": "2.5.38",
"@types/lodash": "^4.14.42",
"@types/lodash": "4.14.42",
"@types/node": "6.0.51",
"angular2-template-loader": "0.6.0",
"autoprefixer": "6.5.3",
@@ -64,16 +64,16 @@
"js-yaml": "3.7.0",
"karma": "1.3.0",
"karma-coverage": "1.1.1",
"karma-jasmine": "1.0.2",
"karma-jasmine": "^1.1.0",
"karma-junit-reporter": "^1.2.0",
"karma-mocha-reporter": "2.2.1",
"karma-phantomjs-launcher": "1.0.2",
"karma-remap-coverage": "0.1.2",
"karma-sourcemap-loader": "0.3.7",
"karma-webpack": "1.8.0",
"log4js": "1.0.1",
"node-sass": "3.13.0",
"phantomjs-prebuilt": "2.1.13",
"node-sass": "^4.0.0",
"phantomjs-prebuilt": "^2.1.14",
"postcss-loader": "1.2.0",
"raw-loader": "0.5.1",
"rimraf": "2.5.4",
@@ -89,26 +89,26 @@
"webpack": "2.1.0-beta.27",
"webpack-bundle-analyzer": "2.1.1",
"webpack-dev-server": "2.1.0-beta.12",
"webpack-merge": "^1.0.2"
"webpack-merge": "^1.1.1"
},
"dependencies": {
"@angular/common": "2.2.4",
"@angular/compiler": "2.2.4",
"@angular/core": "2.2.4",
"@angular/forms": "2.2.4",
"@angular/http": "2.2.4",
"@angular/platform-browser": "2.2.4",
"@angular/platform-browser-dynamic": "2.2.4",
"@angular/router": "3.2.4",
"@angular/common": "^2.4.0",
"@angular/compiler": "^2.4.0",
"@angular/core": "^2.4.0",
"@angular/forms": "^2.4.0",
"@angular/http": "^2.4.0",
"@angular/platform-browser": "^2.4.0",
"@angular/platform-browser-dynamic": "^2.4.0",
"@angular/router": "^3.4.0",
"@ngrx/core": "1.2.0",
"@ngrx/store": "2.2.1",
"angular-router-loader": "0.4.0",
"core-js": "2.4.1",
"immutable": "3.8.1",
"lodash": "4.17.2",
"ng2-translate": "^4.2.0",
"rxjs": "5.0.0-rc.4",
"zone.js": "0.6.26"
"rxjs": "^5.0.1",
"zone.js": "^0.7.4"
},
"engines": {
"node": ">=6.9",
4 changes: 3 additions & 1 deletion src/index.template.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<base href="<%= htmlWebpackPlugin.options.baseHref %>">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<base href="<%= htmlWebpackPlugin.options.baseHref %>">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
Loading