ang-ssr-site-base
is a client project for a site, based on: https://github.com/ng-seed/universal
Most significant changes and improvements:
- Fixed state overloading after execution an each action.
- Changed effects to avoid redurance backend calls.
- Implemented real Login/Register with Authorization JWT token.
- Implemented auth and routing via ngrx.
- Implemented AuthInterceptor - adds the JWT token to the request's header.
- Implemented HttpErrorInterceptor - provides searate handling of client and server sides HTTP errors.
- Implemented TimeoutInterceptor - cancels HTTP requests after timeout.
- Fixed (implemented) Create, Edit Delete a componemt behavior.
- Changed application/ components layout for CSS grid usage.
- Implemented backend (look at the api-side base project) for debug the application.
Application organization, providing the following features:
- Providing a ang-ssr-site-base project using the Angular framework.
- Includes ngrx-powered CRUD feature tutorial.
- Compiling bundles for both browser (SPA) and server (Angular Universal) platforms.
- Rebased on Angular CLI to focus on features and development productivity, not on build tools.
- Customizable webpack configuration via @angular-builders.
- Using the modern UI components of Angular Material.
- Dynamic responsive layouts via flex-layout.
- Built-in Hot Module Replacement to save valuable development time.
- Development, staging and production modes.
- Performing AoT compilation for rapid page loads on staging/production builds.
- Tree-shaking and minifying the production builds using Angular Devkit.
- Cross-browser SCSS with autoprefixer and browserslist.
- stylelint-config-standard as configuration preset for stylelint and custom rules to standardize stylesheets.
- Transferring server responses on client bootstrap to prevent app flickering with native TransferState`.
- Deferring initialization of modules via Lazy loading.
- Uses ngrx/store for state management.
- Uses ngrx/entity state adapter to manipulate and query entity collections.
- Uses ngrx/effects side effect model to to model event sources as actions.
- Uses unionize for boilerplate-free functional sum types.
- Uses ngx-config for configuration management.
- Uses ngx-cache for application-wide caching.
- Uses ngx-translate for i18n support.
- Uses ngx-meta for SEO (title, meta tags, and Open Graph tags for social sharing).
- Uses ngx-perfect-scrollbar for scrollbars.
- Vendor-agnostic analytics via angulartics2.
- Unit tests with Jest, including code coverage.
- End-to-end (integration) tests with Nightmare.
- Seamless integration with CircleCI continuous integration and delivery platform.
- angular-tslint-rules as configuration preset for TSLint and codelyzer.
Built with
Angular v7.x.x
, bundled withAngular CLI
.
You can find the project documentation here.
- ANG-SSR-SITE-BASE
Packages in this project depend on @angular v7.x.x
. Older versions contain outdated dependencies, might produce errors.
Also, please ensure that you are using Typescript v3.1.x
or higher.
You can install ang-ssr-site-base
by simply forking the repo:
# clone the repo
$ git clone https://github.com/ekarpovs/ang-ssr-site-base.git
$ cd ang-ssr-site-base
Once you have cloned the repo, you can follow these steps to allow sync changes made in this repo with your fork:
# set up `origin`
$ git remote set-url origin [your-fork-repo]
# set up `upstream` to sync future changes
$ git remote add upstream https://github.com/ekarpovs/ang-ssr-site-base.git
# verify the upstream repo specified for your fork
$ git remote -v
origin https://github.com/YOUR_USERNAME/[your-fork-repo].git (fetch)
origin https://github.com/YOUR_USERNAME/[your-fork-repo].git (push)
upstream https://github.com/ekarpovs/ang-ssr-site-base.git (fetch)
upstream https://github.com/ekarpovs/ang-ssr-site-base.git (push)
# initial push for the fork
$ git push
Now, you can create a new directory (ex: src/app/shared
) to build your codebase out, while benefiting from the
client framework located at the src/app/framework
directory.
In order to merge the latest upstream changes, simply follow:
# fetch the latest upstream
$ git fetch upstream
# merge the upstream changes
$ git merge upstream/master
then handle any conflicts, and go on with building your app.
Below are the scripts to dev, build, and test this project:
# use `yarn` to install the dependencies
$ yarn
# dev server
$ ng serve
# dev server (HMR-enabled)
$ yarn start:hmr
# dev server (AoT compilation)
$ yarn start:prod
# dev server (SSR)
$ yarn start:ssr
# dev server (SSR & AoT compilation)
$ yarn start:ssr:prod
# development build
$ ng build
# production build
$ ng build --prod
# development build (SSR)
$ yarn build:ssr
# production build (SSR)
$ yarn build:ssr:prod
The build artifacts will be stored in the dist/
directory.
# run unit tests
$ yarn test
# run e2e tests
$ yarn e2e
The project currently performs CLI scaffolding using the official @schematics/angular
collection and @ngrx/schematics
collection.
@schematics/angular
blueprints :
-_ class -_ component -_ directive -_ enum -_ guard -_ interface -_ module -_ pipe -* service
# add module `domain`
$ ng g module domain
# create src/app/domain/domain.module.ts (183 bytes)
@ngrx/schematics
blueprints :
-_ action -_ container -_ effect -_ entity -_ feature -_ reducer -* store
# add store module
$ ng g m store --m app.module.ts
# CREATE src/app/store/store.module.ts (189 bytes)
# UPDATE src/app/app.module.ts (3525 bytes)
# add root state interface
# ng g i store/state
# CREATE src/app/store/state.ts (27 bytes)
# add module
$ ng g m store/domain/domain --flat
# CREATE src/app/store/domain/domain.module.ts (199 bytes)
# add entity
$ ng g en store/domain/domain/domain -m ../../domain/domain.module.ts
# CREATE src/app/store/domain/domain/domain.actions.ts (2254 bytes)
# CREATE src/app/store/domain/domain/domain.model.ts (42 bytes)
# CREATE src/app/store/domain/domain/domain.reducer.ts (1818 bytes)
# CREATE src/app/store/domain/domain/domain.reducer.spec.ts (326 bytes)
#UPDATE src/app/store/domain/domain.module.ts (355 bytes)
# add effects
$ ng g ef store/domain/domain/domain -m store/domain/domain.module.ts
# CREATE src/app/store/domain/domain/domain.effects.ts (185 bytes)
# CREATE src/app/store/domain/domain/domain.effects.spec.ts (589 bytes)
# UPDATE src/app/store/domain/domain.module.ts (506 bytes)
# add service
$ ng g s store/domain/domain/domain
# CREATE src/app/store/domain/domain/domain.service.spec.ts (333 bytes)
# CREATE src/app/store/domain/domain/domain.service.ts (135 bytes)
# add module `+domain/domain`
$ ng g m +domain/domain --flat
# CREATE src/app/+domain/domain.module.ts (188 bytes)
ng g m +domain/domain --flat
# add container component `+domain/domain/domain-container`
$ ng g co +domain/domain/domain-container --flat --state ../../store/domain/domain/domain.reducer.ts
# CREATE src/app/+domain/domain/domain-container.component.html (33 bytes)
# CREATE src/app/+domain/domain/domain-container.component.ts (432 bytes)
# CREATE src/app/+domain/domain/domain-container.component.scss (0 bytes)
# CREATE src/app/+domain/domain/domain-container.component.spec.ts (884 bytes)
# UPDATE src/app/+domain/domain.module.ts (829 bytes)
# add child component `+domain/domain`
$ ng g c +domain/domain -c OnPush
# CREATE src/app/+domain/domain/domain.component.html (23 bytes)
# CREATE src/app/+domain/domain/domain.component.spec.ts (614 bytes)
# CREATE src/app/+domain/domain/domain.component.ts (262 bytes)
# CREATE src/app/+domain/domain/domain.component.scss (0 bytes)
# UPDATE src/app/+domain/domain.module.ts (829 bytes)
# add container component `+domain/domain/domain-detail/domain-detail-container`
$ ng g co +domain/domain/domain-detail/domain-detail-container --flat --state ../../../store/domain/domain/domain.reducer.ts
# CREATE src/app/+domain/domain/domain-detail/domain-detail-container.component.html (40 bytes)
# CREATE src/app/+domain/domain/domain-detail/domain-detail-container.component.ts (462 bytes)
# CREATE src/app/+domain/domain/domain-detail/domain-detail-container.component.scss (0 bytes)
# CREATE src/app/+domain/domain/domain-detail/domain-detail-container.component.spec.ts (927 bytes)
# UPDATE src/app/+domain/domain.module.ts (946 bytes)
# add child component `+domain/domain-detail`
$ ng g c +domain/domain/domain-detail -c OnPush
# CREATE src/app/+domain/domain/domain-detail/domain-detail.component.html (30 bytes)
# CREATE src/app/+domain/domain/domain-detail/domain-detail.component.spec.ts (657 bytes)
# CREATE src/app/+domain/domain/domain-detail/domain-detail.component.ts (289 bytes)
# CREATE src/app/+domain/domain/domain-detail/domain-detail.component.scss (0 bytes)
# UPDATE src/app/+domain/domain.module.ts (946 bytes)
We use the component approach in this project, which is a standard for developing Angular apps and also a great way to ensure maintainable code by encapsulation of our behavior logic.
A component is basically a self contained app usually in a single file or a directory with each concern as a file: style, template, specs, and component class.
As an old convention, we use the
+
prefix for lazy-loaded modules. Please keep in mind that it does not change the router behavior, neither makes the directory unworkable. It's just a handy method to identify lazy-loaded modules by having a straight look at the directory structure.
universal/
├──.cache/ * cache directory for ngx-cache
|
├──.circleci/
| └──config.yml * CircleCI config
|
├──.github/ * issue & pr templates
├──coverage/ * test coverage reports
|
├──dist/ * output directory to extract bundles
| ├──browser/ * browser bundles
| └──server/ * server bundles
|
├──node_modules/ * dependencies
|
├──src/
| ├──app/ * application code
| | ├──+lazy-module/ * some LAZY module (attn to the `+` prefix for lazy-loaded modules)
| | | ...
| | ├──framework/ * client framework
| | ├──layout/ * layout (app module)
| | ├──library/ * application library (models, services, state management, etc.)
| | ├──login/ * login (app module)
| | ├──shared/ * shared codebase
| | └──store/ * state (ngrx) module
| └──assets/ * static assets (scss, img, json, etc.)
| └──environments/ * environment settings
|
├──tools/
| ├──build/ * build config and scripts (webpack, etc.)
| ├──config/ * config files for static-assets (stylelint, etc.)
| └──test/ * test config
|
├──.gitignore * GIT settings
├──.jshintrc * jshint config
├──angular.json * Angular CLI config
├──CHANGELOG.md * change log
├──CODE_OF_CONDUCT.md * code of conduct
├──CONTRIBUTING.md * contributing info
├──LICENSE * software license
├──package.json * deps management
├──README.md * project information
├──server.ts * server code
├──stylelint.config.js * stylelint config locator
├──test-report.xml * JUNIT test results
├──tsconfig.json * typescript config
├──tsconfig.server.json * typescript config (for server build)
├──tsconfig.server-compile.json * typescript config (for server compilation)
├──tsconfig.spec.json * typescript config (for unit/e2e tests)
├──tslint.json * tslint config
└──yarn.lock * deps lockfile
The MIT License (MIT)
Copyright (c) 2019 [Evgeny Karpovsky]
- ngx-cache/fs-storage Error: EEXIST: file already exists, mkdir 'D:\Projects\vs-site.cache' at Object.mkdirSync (fs.js:768:3) at new FsStorageService (D:\Projects\vs-site\nodemodules@ngx-cache\fs-storage\bundles\ngx-cache-fs-storage.umd.js:131:20) at _createClass (D:\Projects\vs-site\node_modules@angular\core\bundles\core.umd.js:21284:24) at _createProviderInstance (D:\Projects\vs-site\node_modules@angular\core\bundles\core.umd.js:21256:30) at resolveNgModuleDep (D:\Projects\vs-site\node_modules@angular\core\bundles\core.umd.js:21220:25) at NgModuleRef.get (D:\Projects\vs-site\node_modules@angular\core\bundles\core.umd.js:21928:20) at new FsCacheService (D:\Projects\vs-site\node_modules@ngx-cache\platform-server\bundles\ngx-cache-platform-server.umd.js:41:39) at _createClass (D:\Projects\vs-site\node_modules@angular\core\bundles\core.umd.js:21286:24) at _createProviderInstance (D:\Projects\vs-site\node_modules@angular\core\bundles\core.umd.js:21256:30) at resolveNgModuleDep (D:\Projects\vs-site\node_modules@angular\core\bundles\core.umd.js:21220:25)