This project was generated with Angular CLI version 1.7.4 and created to demonstrate implementations of a few recently encountered technologies.
These include Commitizen CLI, standard-version, Angular Universal, GraphQL, GraphQL CLI, Apollo, Apollo codegen.
Following section titles can be used to search commit history for changes specific to each feature.
This project also includes comments to help explain what some sections of code do and how they relate to each of the below features.
Commitizen CLI used to enforce commit message structure and conventional changelog standard version used to bump versions and auto generate a meaningful changelog.
These tools were installed locally (commitizen, standard-version) and are executed via npm scripts.
This setup should make projects easier to approach for new team members.
This project also roughly follows GitFlow although not a highlighted feature of this project.
The Angular CLI is used to it's fullest to generate project files (modules, components, service etc).
The file and folder structure generally follow: resource (e.g Pokemon) > pages (list and detail) > nested components (Pokemon name).
This results in a folder for each resource (e.g Pokemon). Within these folders there is one module, one routing module and as many components used exclusively by pages focused on that resource (e.g Pokemon detail view, name and number components).
Routed components (e.g Pokemon detail) can house nested components in their folders and will be used for data fetching and distributing.
Server side rendering is where a client requests a route and the related view is rendered on the server (as opposed to by the client) and returned as the server's response.
This is achieved by creating an express web-server and using an Angular templating engine which takes a route and a bundled Angular application and returns a rendered view.
For the most part this guide can be followed (and referred to for more detail), however this project has implemented server side rendering slightly differently:
- Install tsloader v3.5.0 to avoid errors.
- No need to edit services.
- An
AppBrowserModule
is used and bootstrapped inmain.ts
. initialNavigation
disabled inAppRoutingModule
to prevent body of app refreshing when client takes over from a server rendered view.- Npm scripts renamed from
universal
tossr
.
Once the above is completed npm run build:ssr
and npm run serve:ssr
can be run and when https://localhost:4000/
is visited the initial request in chrome dev tools should contain a fully rendered HTML page as opposed to a pre Angular bootstrap one.
It is possible to inspect a GraphQL endpoint for it's schema with GraphQL CLI. This schema can then be used by GraphQL for VScode to lint and autocomplete gql tagged queries. Furthermore, given these correctly typed queries, it is possible to generate response types with Apollo codegen.
The above types can also be used to type check low level component mock data which can then be composed for routed component mock data.
- Install GraphQL for VScode and copy this project's `.gqlconfig' to loosen the GraphQL linter.
- Run
npm i --save graphql-tag
and create fragments for each nested component and queries for each routed component (see this project's Pokemon fragments and queries) - Use the GraphQL CLI to download GraphQL schema. Run
npm i --save-dev graphql-cli
, add"schema:gql": "graphql get-schema -e https://graphql-pokemon.now.sh -o ./schema.graphql",
to yourpackage.json
and runnpm run schema:gql
. Remove all comments from the generated schema file. - The GraphQL for VScode plugin should now provide some linting and autocompletion for gql tagged strings. If not, check
schema.graphql
from errors and restart VScode. - Use Apollo codegen to create query response types based on an introspection schema and gql tagged strings.
- Use fragment types to type check mock data at component level (e.g pokemon name) and query types compose this mock data at the routed component level.
For the most part this guide was followed with the following deviations / enhancements:
"esnext.asynciterable"
added to thelib
array intsconfig.json
to prevent the build from breaking after the required npm packages have been installed.- Apollo client initialisation logic broken out into it's own service to allow for more control over when the client is initialised (import when pairing Apollo with an Angular
APP_INITIALIZER
for runtime configuration. fetch-introspection-result-data.js
script used to introspect GraphQL endpoint to allow for more complex Apollo queries.- Basic loading styling and logic has been implemented at component level as pages and components will render before data is present and to prevent loading states from flickering.
Angular state transfer allows responses received by Angular to be stored in a key value object at the bottom of it's rendered html pages. This means responses received by Angular when running as a template engine on the server can be stored and used by Angular when that same route is run on the browser. This prevents duplicate requests (once on the server and again on the browser).
- Import
ServerTransferStateModule
intoAppServerModule
. This should result in an empty state object at the bottom of pages rendered by the server. Build, serve and inspect initial server html responses from the server to view the object. The Angular guide recommends to also importBrowserTransferStateModule
however it doesn't seem to be required for this app. npm i --save @nguniversal/common
and importTransferHttpCacheModule
in app module to allow access to state in Angular application. More info.- In
ApolloClientService
either transfer Apollo cache to Angular state or Angular state to Apollo cache depending on wether on server or on browser. This will result in data being visible at bottom of ssr pages. - Finally we need to ensure client app bootstraps after the DOM has loaded (and hence state at bottom of html docs). Add a
document.loaded
event listener inmain.ts
to achieve this.
Runtime config allows key app configuration options (like API urls) to be decided at runtime as opposed to compile time. This means the app can be compiled and packaged with different config files for different deployments (e.g dev and prod).
This project stores app config as a .json
file in the assets folder and uses node fs
or Angular HTTP client (server or browser apps respectfully) to load the app's config pre app bootstrap.
- Create an
AppConfigService
. - Create an
app-config.json
. - Create a
ConfigLoaderServer
which usesfs
to read theapp-config.json
and store it's config object in the state transfer object. - Use
ConfigLoaderServer
in aAPP_INITIALIZER
in theServerModule
to get the config object and store it on theAppConfigService
. - Create a
ConfigLoaderBrowser
which checks for the config object in the state transfer object and if not present uses Angular'sHttpCLient
to retrieveapp-config.json
. - Use
ConfigLoaderBrowser
in aAPP_INITIALIZER
in theBrowserModule
to get the config object and store it on theAppConfigService
. - Use the
AppConfigService
config object in theApolloClientService
to create thehttpLink
.
Run ng serve
for a dev server. Navigate to https://localhost:4200/
. The app will automatically reload if you change any of the source files.
Run ng generate component component-name
to generate a new component. You can also use ng generate directive|pipe|service|class|guard|interface|enum|module
.
Run ng build
to build the project. The build artefacts will be stored in the dist/
directory. Use the -prod
flag for a production build.
Run npm run build:ssr
to:
- build the project for server to be used as an express templating engine
- build the project for browser to be shipped to the client.
- build the typescript express server into an executable JavaScript file
The build artefacts will be stored in the dist/
directory.
Run npm run server:ssr
to execute the JavaScript express server in the /dist
directory. Navigate to https://localhost:4200/
. The express server will use the server side app as a templating engine and return a fully formed HTML document directly from the server.
Create git branches as you normally would. Run npm run commit
to create a commit with commitizen. Run npm run release
to use standard version to auto generate a CHANGELOG and auto increment project version numbers.
Run npm run schema:graphql
to download the Pokemon GraphQL schema. Run npm run apollo:gen
to introspect the Pokemon GraphQL endpoint and generate response types based on gql
tagged strings.
To get more help on the Angular CLI use ng help
or go check out the Angular CLI README.