- Angularlicious GitHub Search
The objective of this application is to use the GitHub search API.
- use GitHub public search API
- search for GitHub users
- display search results in a grid with paging
- show user details
- include navigation to GitHub for result item
- pagination with previous and next
- include total count of results
- Deploy application
- Provide access to the GitHub repository for review
The application will make use of external libraries/packages to provide features and/or infrastructure and cross-cutting concerns for the application.
- will use Angularlicious cross-cutting concern libraries for:
- configuration
- logging
- security
- firebase
Create a new workspace for the application and required library projects. Use the Nrwl.io Nx Workspace.
create-nx-workspace workspace
Creating a sandbox with the CLI and Nx Schematics...
added 368 packages from 256 contributors and audited 15266 packages in 20.559s
found 0 vulnerabilities
ng new "workspace" --collection=@nrwl/schematics
? What is the npm scope you would like to use for your Nx Workspace? angularlicious
? Which Package Manager would you like to use for your workspace? npm
CREATE workspace/.prettierrc (25 bytes)
CREATE workspace/angular.json (307 bytes)
CREATE workspace/karma.conf.js (1012 bytes)
CREATE workspace/nx.json (205 bytes)
CREATE workspace/package.json (2469 bytes)
CREATE workspace/README.md (1861 bytes)
CREATE workspace/tsconfig.json (423 bytes)
CREATE workspace/tslint.json (2307 bytes)
CREATE workspace/.editorconfig (245 bytes)
CREATE workspace/.gitignore (503 bytes)
CREATE workspace/apps/.gitkeep (1 bytes)
CREATE workspace/libs/.gitkeep (0 bytes)
CREATE workspace/tools/tsconfig.tools.json (254 bytes)
CREATE workspace/tools/schematics/.gitkeep (0 bytes)
Create a new web project to implement the GitHub Search UI features.
ng generate application github-search-web --routing --style=scss --dry-run
? In which directory should the application be generated?
? Which Unit Test Runner would you like to use for the application? Karma [ https://karma-runner.github.io ]
? Which E2E Test Runner would you like to use for the application? Protractor [ https://www.protractortest.org ]
? Which tags would you like to add to the application? (used for linting)
CREATE apps/github-search-web-e2e/protractor.conf.js (752 bytes)
CREATE apps/github-search-web-e2e/tsconfig.e2e.json (248 bytes)
CREATE apps/github-search-web-e2e/src/app.e2e-spec.ts (313 bytes)
CREATE apps/github-search-web-e2e/src/app.po.ts (219 bytes)
CREATE apps/github-search-web/browserslist (388 bytes)
CREATE apps/github-search-web/karma.conf.js (494 bytes)
CREATE apps/github-search-web/tsconfig.app.json (230 bytes)
CREATE apps/github-search-web/tsconfig.spec.json (293 bytes)
CREATE apps/github-search-web/tslint.json (269 bytes)
CREATE apps/github-search-web/src/favicon.ico (5430 bytes)
CREATE apps/github-search-web/src/index.html (324 bytes)
CREATE apps/github-search-web/src/main.ts (372 bytes)
CREATE apps/github-search-web/src/polyfills.ts (3234 bytes)
CREATE apps/github-search-web/src/test.ts (642 bytes)
CREATE apps/github-search-web/src/styles.scss (80 bytes)
CREATE apps/github-search-web/src/assets/.gitkeep (0 bytes)
CREATE apps/github-search-web/src/environments/environment.prod.ts (51 bytes)
CREATE apps/github-search-web/src/environments/environment.ts (662 bytes)
CREATE apps/github-search-web/src/app/app.module.ts (485 bytes)
CREATE apps/github-search-web/src/app/app.component.html (602 bytes)
CREATE apps/github-search-web/src/app/app.component.spec.ts (1106 bytes)
CREATE apps/github-search-web/src/app/app.component.ts (233 bytes)
CREATE apps/github-search-web/src/app/app.component.scss (0 bytes)
UPDATE angular.json (4655 bytes)
UPDATE package.json (2469 bytes)
UPDATE nx.json (313 bytes)
Verify the new web project can build and run.
ng serve
Add a launch.json
configuration for the project. Update the port of the localhost to use: 4200
. Run the debugger
to launch the application.
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch localhost",
"url": "http://localhost:4200",
"webRoot": "${workspaceFolder}"
}
]
}
Create a set of library projects for cross-cutting concerns. Use the --npm-scope
switch to scope all of the
library projects to angularlicious
.
ng generate library configuration --publishable --module --npm-scope=angularlicious
ng generate library logging --publishable --module --npm-scope=angularlicious
ng generate library errorHandling --publishable --module --npm-scope=angularlicious
ng generate library firebase --publishable --module --npm-scope=angularlicious
ng generate library contentful --publishable --module --npm-scope=angularlicious
ng generate library http-service --publishable --module --npm-scope=angularlicious
ng generate library foundation --publishable --module --npm-scope=angularlicious
ng generate library actions --publishable --module --npm-scope=angularlicious
ng generate library rules-engine --publishable --module --npm-scope=angularlicious
Create a new library project to manage the configuration for the application. There will be other libraries and features that will require configuration.
- error handling
- logging
- firebase
- content
ng generate library configuration --publishable --module --npm-scope=angularlicious
? In which directory should the library be generated?
? Which module should import the library?
? Would you like to add a routing configuration to the library? No
? Will this library be lazy loaded? No
? Which tags would you like to add to the library? (used for linting)
? Which Unit Test Runner would you like to use for the library? Karma
CREATE libs/configuration/karma.conf.js (490 bytes)
CREATE libs/configuration/ng-package.json (163 bytes)
CREATE libs/configuration/package.json (190 bytes)
CREATE libs/configuration/tsconfig.lib.json (746 bytes)
CREATE libs/configuration/tsconfig.spec.json (265 bytes)
CREATE libs/configuration/tslint.json (269 bytes)
CREATE libs/configuration/src/test.ts (700 bytes)
CREATE libs/configuration/src/index.ts (61 bytes)
CREATE libs/configuration/src/lib/configuration.module.ts (258 bytes)
CREATE libs/configuration/src/lib/configuration.module.spec.ts (461 bytes)
UPDATE angular.json (5766 bytes)
UPDATE package.json (2598 bytes)
UPDATE nx.json (360 bytes)
UPDATE tsconfig.json (514 bytes)
The application environment
configurations will need to be updated with:
- application
- appConfig: reference the actual
json
configuration file
Each environment has the potential of a distinct configuration based on the target appConfig
file that is referenced.
export const environment = {
name: 'development',
application: 'angularlicious-github-search',
production: false,
appConfig: 'assets/config/configuration.development.json'
};
Add (2) configuration files to the application (development and production). The initialization process of the application will use the settings defined in these configuration files to setup the application services and cross-cutting concerns. The SharedModule
contains the application's initialization mechanisms.
Note: These configuration files should be ignored and NOT put into your code repository. They typically contain API information that you do not want to leak out into the public.
{
"logging": {
"applicationName": "Angularlicious-GitHub-Search",
"version": "1.0",
"isProduction": false
},
"errorHandling": {
"name": "Angularlicious-GitHub-Search",
"includeDefaultErrorHandling": true
},
"loggly": {
"applicationName": "Angularlicious-GitHub-Search",
"version": "1.0",
"isProduction": false
},
"firebase": {},
"contentful": {
"spaceId": "YOUR-SPACE-ID-HERE",
"token": "YOUR-TOKEN-HERE"
}
}
ng generate library logging --publishable --module --npm-scope=angularlicious
? In which directory should the library be generated?
? Which module should import the library?
? Would you like to add a routing configuration to the library? No
? Will this library be lazy loaded? No
? Which tags would you like to add to the library? (used for linting)
? Which Unit Test Runner would you like to use for the library? Karma
CREATE libs/logging/karma.conf.js (484 bytes)
CREATE libs/logging/ng-package.json (157 bytes)
CREATE libs/logging/package.json (184 bytes)
CREATE libs/logging/tsconfig.lib.json (740 bytes)
CREATE libs/logging/tsconfig.spec.json (259 bytes)
CREATE libs/logging/tslint.json (269 bytes)
CREATE libs/logging/src/test.ts (700 bytes)
CREATE libs/logging/src/index.ts (55 bytes)
CREATE libs/logging/src/lib/logging.module.ts (252 bytes)
CREATE libs/logging/src/lib/logging.module.spec.ts (431 bytes)
UPDATE angular.json (6817 bytes)
UPDATE package.json (2598 bytes)
UPDATE nx.json (401 bytes)
UPDATE tsconfig.json (594 bytes)
The logging library requires the following package.
npm install -S ngx-loggly-logger@6.0.0
ng generate library errorHandling --publishable --module --npm-scope=angularlicious
? In which directory should the library be generated?
? Which module should import the library?
? Would you like to add a routing configuration to the library? No
? Will this library be lazy loaded? No
? Which tags would you like to add to the library? (used for linting)
? Which Unit Test Runner would you like to use for the library? Karma
CREATE libs/error-handling/karma.conf.js (491 bytes)
CREATE libs/error-handling/ng-package.json (164 bytes)
CREATE libs/error-handling/package.json (191 bytes)
CREATE libs/error-handling/tsconfig.lib.json (747 bytes)
CREATE libs/error-handling/tsconfig.spec.json (266 bytes)
CREATE libs/error-handling/tslint.json (269 bytes)
CREATE libs/error-handling/src/test.ts (700 bytes)
CREATE libs/error-handling/src/index.ts (62 bytes)
CREATE libs/error-handling/src/lib/error-handling.module.ts (258 bytes)
CREATE libs/error-handling/src/lib/error-handling.module.spec.ts (462 bytes)
UPDATE angular.json (8999 bytes)
UPDATE package.json (2598 bytes)
UPDATE nx.json (491 bytes)
UPDATE tsconfig.json (770 bytes)
ng generate library firebase --publishable --module --npm-scope=angularlicious
? In which directory should the library be generated?
? Which module should import the library?
? Would you like to add a routing configuration to the library? No
? Will this library be lazy loaded? No
? Which tags would you like to add to the library? (used for linting)
? Which Unit Test Runner would you like to use for the library? Karma
CREATE libs/firebase/karma.conf.js (485 bytes)
CREATE libs/firebase/ng-package.json (158 bytes)
CREATE libs/firebase/package.json (185 bytes)
CREATE libs/firebase/tsconfig.lib.json (741 bytes)
CREATE libs/firebase/tsconfig.spec.json (260 bytes)
CREATE libs/firebase/tslint.json (269 bytes)
CREATE libs/firebase/src/test.ts (700 bytes)
CREATE libs/firebase/src/index.ts (56 bytes)
CREATE libs/firebase/src/lib/firebase.module.ts (253 bytes)
CREATE libs/firebase/src/lib/firebase.module.spec.ts (436 bytes)
UPDATE angular.json (7878 bytes)
UPDATE package.json (2598 bytes)
UPDATE nx.json (443 bytes)
UPDATE tsconfig.json (676 bytes
The Firebase library requires the following packages.
npm install -S firebase@5.7.3
npm install -S @angular/fire@5.1.1
ng generate library contentful --publishable --module --npm-scope=angularlicious
? In which directory should the library be generated?
? Which module should import the library?
? Would you like to add a routing configuration to the library? No
? Will this library be lazy loaded? No
? Which tags would you like to add to the library? (used for linting)
? Which Unit Test Runner would you like to use for the library? Karma
CREATE libs/contentful/karma.conf.js (487 bytes)
CREATE libs/contentful/ng-package.json (160 bytes)
CREATE libs/contentful/package.json (187 bytes)
CREATE libs/contentful/tsconfig.lib.json (743 bytes)
CREATE libs/contentful/tsconfig.spec.json (262 bytes)
CREATE libs/contentful/tslint.json (269 bytes)
CREATE libs/contentful/src/test.ts (700 bytes)
CREATE libs/contentful/src/index.ts (58 bytes)
CREATE libs/contentful/src/lib/contentful.module.ts (255 bytes)
CREATE libs/contentful/src/lib/contentful.module.spec.ts (446 bytes)
UPDATE angular.json (10080 bytes)
UPDATE package.json (2598 bytes)
UPDATE nx.json (535 bytes)
UPDATE tsconfig.json (856 bytes)
Contentful requires the following package.
npm install -S contentful@7.0.4
npm install -S marked@0.6.0
ng generate library http-service --publishable --module --npm-scope=angularlicious
? In which directory should the library be generated?
? Which module should import the library?
? Would you like to add a routing configuration to the library? No
? Will this library be lazy loaded? No
? Which tags would you like to add to the library? (used for linting)
? Which Unit Test Runner would you like to use for the library? Karma
CREATE libs/http-service/karma.conf.js (489 bytes)
CREATE libs/http-service/ng-package.json (162 bytes)
CREATE libs/http-service/package.json (189 bytes)
CREATE libs/http-service/tsconfig.lib.json (745 bytes)
CREATE libs/http-service/tsconfig.spec.json (264 bytes)
CREATE libs/http-service/tslint.json (269 bytes)
CREATE libs/http-service/src/test.ts (700 bytes)
CREATE libs/http-service/src/index.ts (60 bytes)
CREATE libs/http-service/src/lib/http-service.module.ts (256 bytes)
CREATE libs/http-service/src/lib/http-service.module.spec.ts (452 bytes)
UPDATE angular.json (11181 bytes)
UPDATE package.json (2598 bytes)
UPDATE nx.json (581 bytes)
UPDATE tsconfig.json (946 bytes)
ng generate library foundation --publishable --module --npm-scope=angularlicious
? In which directory should the library be generated?
? Which module should import the library?
? Would you like to add a routing configuration to the library? No
? Will this library be lazy loaded? No
? Which tags would you like to add to the library? (used for linting)
? Which Unit Test Runner would you like to use for the library? Karma
CREATE libs/foundation/karma.conf.js (487 bytes)
CREATE libs/foundation/ng-package.json (160 bytes)
CREATE libs/foundation/package.json (187 bytes)
CREATE libs/foundation/tsconfig.lib.json (743 bytes)
CREATE libs/foundation/tsconfig.spec.json (262 bytes)
CREATE libs/foundation/tslint.json (269 bytes)
CREATE libs/foundation/src/test.ts (700 bytes)
CREATE libs/foundation/src/index.ts (58 bytes)
CREATE libs/foundation/src/lib/foundation.module.ts (255 bytes)
CREATE libs/foundation/src/lib/foundation.module.spec.ts (446 bytes)
UPDATE angular.json (12262 bytes)
UPDATE package.json (2598 bytes)
UPDATE nx.json (625 bytes)
UPDATE tsconfig.json (1032 bytes)
ng generate library actions --publishable --module --npm-scope=angularlicious
? In which directory should the library be generated?
? Which module should import the library?
? Would you like to add a routing configuration to the library? No
? Will this library be lazy loaded? No
? Which tags would you like to add to the library? (used for linting)
? Which Unit Test Runner would you like to use for the library? Karma
CREATE libs/actions/karma.conf.js (484 bytes)
CREATE libs/actions/ng-package.json (157 bytes)
CREATE libs/actions/package.json (184 bytes)
CREATE libs/actions/tsconfig.lib.json (740 bytes)
CREATE libs/actions/tsconfig.spec.json (259 bytes)
CREATE libs/actions/tslint.json (269 bytes)
CREATE libs/actions/src/test.ts (700 bytes)
CREATE libs/actions/src/index.ts (55 bytes)
CREATE libs/actions/src/lib/actions.module.ts (252 bytes)
CREATE libs/actions/src/lib/actions.module.spec.ts (431 bytes)
UPDATE angular.json (13313 bytes)
UPDATE package.json (2598 bytes)
UPDATE nx.json (666 bytes)
UPDATE tsconfig.json (1112 bytes)
ng generate library rules-engine --publishable --module --npm-scope=angularlicious
? In which directory should the library be generated?
? Which module should import the library?
? Would you like to add a routing configuration to the library? No
? Will this library be lazy loaded? No
? Which tags would you like to add to the library? (used for linting)
? Which Unit Test Runner would you like to use for the library? Karma
CREATE libs/rules-engine/karma.conf.js (489 bytes)
CREATE libs/rules-engine/ng-package.json (162 bytes)
CREATE libs/rules-engine/package.json (189 bytes)
CREATE libs/rules-engine/tsconfig.lib.json (745 bytes)
CREATE libs/rules-engine/tsconfig.spec.json (264 bytes)
CREATE libs/rules-engine/tslint.json (269 bytes)
CREATE libs/rules-engine/src/test.ts (700 bytes)
CREATE libs/rules-engine/src/index.ts (60 bytes)
CREATE libs/rules-engine/src/lib/rules-engine.module.ts (256 bytes)
CREATE libs/rules-engine/src/lib/rules-engine.module.spec.ts (452 bytes)
UPDATE angular.json (14414 bytes)
UPDATE package.json (2598 bytes)
UPDATE nx.json (712 bytes)
UPDATE tsconfig.json (1202 bytes)
The rule engine has a required dependency on the following package.
npm install -S typescript-dotnet-commonjs@4.10.0
The SharedModule
is provides imports for 3rd-party and common modules that are not part of the domain of the application. Many of these modules participate or require configuration. Therefore, this module is responsible for managing the configuration of the applicaton using Angular APP_INITIALIZER
providers.
ng g module modules/shared --project=github-search-web
CREATE apps/github-search-web/src/app/modules/shared/shared.module.ts (190 bytes)
Note: Make sure to add the
SharedModule
to the import section of theAppModule
.
import { NgModule, NO_ERRORS_SCHEMA, ErrorHandler, APP_INITIALIZER } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { AngularliciousFoundationModule, HttpBaseService } from '@angularlicious/foundation';
import { HttpClientModule } from '@angular/common/http';
import { ErrorHandlingModule, ErrorHandliciousService } from '@angularlicious/error-handling';
import { AngularliciousLoggingModule, AngularliciousLoggingService, ConsoleWriter, LogglyWriter } from '@angularlicious/logging';
import { ConfigurationModule, ConfigurationService } from '@angularlicious/configuration';
import { FirebaseModule, AuthService } from '@angularlicious/firebase';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ContentfulModule } from '@angularlicious/contentful';
import { MaterialDesignModule } from '../material-design/material-design.module';
/**
* Shared Layout concerns;
*/
// tslint:disable-next-line: nx-enforce-module-boundaries
import { environment } from 'apps/github-search-web/src/environments/environment';
/**
* The factory function to initialize the configuration service for the application.
* @param configService
*/
export function initializeConfiguration(configService: ConfigurationService) {
console.log(`Initializing firebase configuration from [AppModule]`);
configService.loadConfiguration();
return () => {
return configService;
}
}
/**
* The factory function to initialize the logging service and writer for the
* application.
*
* @param loggingService
* @param consoleWriter
*/
export function initializeLogWriter(loggingService: AngularliciousLoggingService, consoleWriter: ConsoleWriter) {
console.log(`Initializing [Console Writer] from [AppModule]`);
return () => {
return consoleWriter;
}
}
/**
* Use module to reference the common components, directives, and pipes. Use and reference
* this module to share common references.
*/
@NgModule({
imports: [
AngularliciousFoundationModule,
AngularliciousLoggingModule,
BrowserAnimationsModule,
ConfigurationModule.forRoot({ filePath: environment.appConfig}),
ContentfulModule,
ErrorHandlingModule,
FirebaseModule,
FormsModule,
MaterialDesignModule,
ReactiveFormsModule,
RouterModule
],
declarations: [],
exports: [
ContentfulModule,
FormsModule,
HttpClientModule,
MaterialDesignModule,
ReactiveFormsModule,
RouterModule
],
providers: [
{
provide: ErrorHandler,
useClass: ErrorHandlingModule
},
ConfigurationService,
AngularliciousLoggingService,
AuthService,
{
provide: APP_INITIALIZER,
useFactory: initializeConfiguration,
deps: [ConfigurationService],
multi: true
},
{
provide: APP_INITIALIZER,
useFactory: initializeLogWriter,
deps: [AngularliciousLoggingService, ConsoleWriter, LogglyWriter],
multi: true
},
ConsoleWriter,
LogglyWriter,
{
provide: ErrorHandler,
useClass: ErrorHandliciousService,
deps: [ConfigurationService, AngularliciousLoggingService]
},
HttpBaseService, // Angularlicious; REQUIRED BASE FOR HTTP API COMMUNICATION TO WEB API END POINTS;
],
schemas: [NO_ERRORS_SCHEMA]
})
export class SharedModule {
}
ng g module modules/core --project=github-search-web
CREATE apps/github-search-web/src/app/modules/core/core.module.ts (188 bytes)
Note: Make sure to import the
CoreModule
into theAppModule
. TheCoreModule
is a singleton - there should only be one instance of this module for the entire application.
import {
NgModule,
Optional,
SkipSelf
} from '@angular/core';
import { CommonModule } from '@angular/common';
/** APPLICATION MODULES:
* Use this section to import application specific modules that are [NOT]
* 3rd-party and/or Angular specific.
*
* Importing this module is required to allow access to the public accessible
* members of the imported module (i.e., PageLayoutModule contains a
* CardComponent).
*/
import { SiteModule } from './../site/site.module';
import { SharedModule } from '../shared/shared.module';
/** SERVICES:
* Import the [service] and add to the [providers] list. This will make
* the specified service globally available to the application as a
* singleton/single instance.
*/
/**
* Use the @NgModule attribute to configure the module members:
*
* - imports: import routes or modules;
* - declarations: a list of declarable items belonging to the module (components, directives, or pipes).
* - exports: list of components, directives, or pipes to make public to application;
* - providers: initializes a single instance of the specified service providers;
* - bootstrap (use for root module only): N/A
*/
@NgModule({
imports: [
CommonModule,
SharedModule,
SiteModule
],
declarations: [],
exports: [
SharedModule,
SiteModule
],
providers: []
})
export class CoreModule {
/**
* Use the check to determine if the [CoreModule] has been loaded in the parentModule (AppModule root).
*/
constructor(
@Optional()
@SkipSelf()
parentModule: CoreModule
) {
if (parentModule) {
throw new Error(
`CoreModule is already loaded. Import it in the AppModule only.`
);
}
}
}
Create a module to reference the many elements of Angular Material Design (i.e., modules, components, etc.).
ng add @angular/material
ng g module modules/material-design --project=github-search-web
CREATE apps/github-search-web/src/app/modules/material-design/material-design.module.ts (198 bytes)
Use the Angular Material schematic to add Material Design to the project. The project will attempt to use:
- form inputs
- table with paginator
- buttons
- cards
- progress spinners
The add
schematic wil update the index.html
file with stylesheet
references.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>GithubSearchWeb</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<angularlicious-root></angularlicious-root>
</body>
</html>
The main.ts
is updated to include a reference to hammerjs
.
import 'hammerjs';
The application's styles.scss
is updated to include Material Design style information.
/* You can add global styles to this file, and also import other style files */
html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
Also, BrowserAnimationsModule
is added to the AppModule
.
The angular.json
project configuration is updated to include style references.
"styles": [
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
"apps/github-search-web/src/styles.scss"
],
The MaterialDesignModule references all of the modules for the different elements. This provides a single point of reference for anything Material Design. The module is imported in the SharedModule
for convenience.
import { NgModule } from '@angular/core';
import {
MatButtonModule,
MatButtonToggleModule,
MatSidenavModule,
MatToolbarModule,
MatIconModule,
MatListModule,
MatCardModule,
MatDividerModule,
MatTableModule,
MatDialogModule,
MatInputModule,
MatSelectModule,
MatPaginatorModule,
MatProgressBarModule,
MatProgressSpinnerModule,
MatSortModule,
MatMenuModule,
MatTabsModule,
MatFormFieldModule,
MatDatepickerModule,
MatNativeDateModule,
MatCheckboxModule,
MatRadioModule,
MatTooltipModule,
MAT_DIALOG_DEFAULT_OPTIONS
} from '@angular/material';
@NgModule({
imports: [
MatButtonModule,
MatButtonToggleModule,
MatSidenavModule,
MatToolbarModule,
MatIconModule,
MatListModule,
MatCardModule,
MatDividerModule,
MatTableModule,
MatDialogModule,
MatInputModule,
MatSelectModule,
MatPaginatorModule,
MatProgressBarModule,
MatProgressSpinnerModule,
MatSortModule,
MatMenuModule,
MatTabsModule,
MatFormFieldModule,
MatDatepickerModule,
MatNativeDateModule,
MatCheckboxModule,
MatRadioModule,
MatTooltipModule
],
exports: [
MatButtonModule,
MatButtonToggleModule,
MatSidenavModule,
MatToolbarModule,
MatIconModule,
MatListModule,
MatCardModule,
MatDividerModule,
MatTableModule,
MatDialogModule,
MatInputModule,
MatSelectModule,
MatPaginatorModule,
MatProgressBarModule,
MatProgressSpinnerModule,
MatSortModule,
MatMenuModule,
MatTabsModule,
MatFormFieldModule,
MatDatepickerModule,
MatNativeDateModule,
MatCheckboxModule,
MatRadioModule,
MatTooltipModule
],
providers: [
]
})
export class MaterialDesignModule {}
The application will take advantage of a professional theme/template - it will use the Creative Tim - Material Dashboard Angular. The next steps will be to add displayable items and their required dependencies.
The application will require a default route and other routes to view details of the search and/or the of the GitHub user and repositories.
ng generate module modules/appRouting --project=github-search-web
CREATE apps/github-search-web/src/app/modules/app-routing/app-routing.module.ts (194 bytes)
ng g module modules/site
CREATE apps/github-search-web/src/app/modules/site/site.module.ts (188 bytes)
Add components for the site.
~~ng~~ g component modules/site/navbar
CREATE apps/github-search-web/src/app/modules/site/navbar/navbar.component.html (25 bytes)
CREATE apps/github-search-web/src/app/modules/site/navbar/navbar.component.spec.ts (628 bytes)
CREATE apps/github-search-web/src/app/modules/site/navbar/navbar.component.ts (280 bytes)
CREATE apps/github-search-web/src/app/modules/site/navbar/navbar.component.css (0 bytes)
UPDATE apps/github-search-web/src/app/modules/site/site.module.ts (264 bytes)
ng g component modules/site/sidebar
CREATE apps/github-search-web/src/app/modules/site/sidebar/sidebar.component.html (26 bytes)
CREATE apps/github-search-web/src/app/modules/site/sidebar/sidebar.component.spec.ts (635 bytes)
CREATE apps/github-search-web/src/app/modules/site/sidebar/sidebar.component.ts (284 bytes)
CREATE apps/github-search-web/src/app/modules/site/sidebar/sidebar.component.css (0 bytes)
UPDATE apps/github-search-web/src/app/modules/site/site.module.ts (346 bytes)
ng g component modules/site/footer
CREATE apps/github-search-web/src/app/modules/site/footer/footer.component.html (25 bytes)
CREATE apps/github-search-web/src/app/modules/site/footer/footer.component.spec.ts (628 bytes)
CREATE apps/github-search-web/src/app/modules/site/footer/footer.component.ts (280 bytes)
CREATE apps/github-search-web/src/app/modules/site/footer/footer.component.css (0 bytes)
UPDATE apps/github-search-web/src/app/modules/site/site.module.ts (424 bytes)
Note: Make sure to export the components in the
SiteModule
to allow them to be consumed.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NavbarComponent } from './navbar/navbar.component';
import { SidebarComponent } from './sidebar/sidebar.component';
import { FooterComponent } from './footer/footer.component';
@NgModule({
imports: [
CommonModule
],
declarations: [NavbarComponent, SidebarComponent, FooterComponent],
exports: [
FooterComponent,
NavbarComponent,
SidebarComponent
]
})
export class SiteModule { }
A SearchLayoutModule
will be the container for the search feature of the application. This module will require
its own routing and will be lazy-loaded.
ng generate module layouts/searchLayout --project=github-search-web --routing
CREATE apps/github-search-web/src/app/layouts/search-layout/search-layout-routing.module.ts (255 bytes)
CREATE apps/github-search-web/src/app/layouts/search-layout/search-layout.module.ts (304 bytes)
Note: Import the
SearchLayoutModule
into the applicationCoreModule
.
Add the target component.
ng generate component layouts/search-layout/search --project=github-search-web
CREATE apps/github-search-web/src/app/layouts/search-layout/search/search.component.html (25 bytes)
CREATE apps/github-search-web/src/app/layouts/search-layout/search/search.component.spec.ts (628 bytes)
CREATE apps/github-search-web/src/app/layouts/search-layout/search/search.component.ts (280 bytes)
CREATE apps/github-search-web/src/app/layouts/search-layout/search/search.component.css (0 bytes)
UPDATE apps/github-search-web/src/app/layouts/search-layout/search-layout.module.ts (380 bytes)
Now that there is a domain module and a target component, the AppRoutingModule
can be configured with a
default route. The default will use the SearchComponent
.
import { NgModule } from '@angular/core';
import { CommonModule, } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { Routes, RouterModule } from '@angular/router';
import { SearchComponent } from '../../layouts/search-layout/search/search.component';
const routes: Routes =[
{
path: '',
redirectTo: 'dashboard',
pathMatch: 'full',
}, {
path: '',
component: SearchComponent,
}
];
@NgModule({
imports: [
CommonModule,
BrowserModule,
RouterModule.forRoot(routes)
],
exports: [
],
})
export class AppRoutingModule { }
Update the AppModule
to allow the routes to be loaded.
Note: the
CoreModule
was moved to the last entry in theimports
list.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { NxModule } from '@nrwl/nx';
import { RouterModule } from '@angular/router';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { SharedModule } from './modules/shared/shared.module';
import { CoreModule } from './modules/core/core.module';
import { AppRoutingModule } from './modules/app-routing/app-routing.module';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
NxModule.forRoot(),
// RouterModule.forRoot([], { initialNavigation: 'enabled' }),
RouterModule,
AppRoutingModule,
BrowserAnimationsModule,
SharedModule,
CoreModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
Need to add resources and assets to the application project.
//TODO: Copy the contents (css, img, and scss folders) of the template's
asset
folder to the asset folder of the application project.
npm install -S perfect-scrollbar@1.1.0
npm install -S jquery@3.2.1
npm install -S popper.js@1.14.3
npm install -S bootstrap-material-design@4.1.1
npm install -S arrive@2.4.1
npm install -S moment@2.22.1
npm install -S bootstrap-notify@3.1.3
npm install -S chartist@0.11.0
npm install -S bootstrap@4.1.0
npm install -S @ng-bootstrap/ng-bootstrap
Use the template's angular.json
configuration for the current app.
Update the assets, styles, and scripts. You may need to modify the paths.
"assets": [
"apps/github-search-web/src/favicon.ico",
"apps/github-search-web/src/assets"
],
"styles": [
"./node_modules/perfect-scrollbar/css/perfect-scrollbar.css",
"apps/github-search-web/src/assets/scss/material-dashboard.scss",
"apps/github-search-web/src/assets/css/demo.css",
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
"apps/github-search-web/src/styles.scss"
],
"scripts": [
"node_modules/jquery/dist/jquery.js",
"node_modules/popper.js/dist/umd/popper.js",
"node_modules/bootstrap-material-design/dist/js/bootstrap-material-design.min.js",
"node_modules/arrive/src/arrive.js",
"node_modules/moment/moment.js",
"node_modules/perfect-scrollbar/dist/perfect-scrollbar.min.js",
"node_modules/bootstrap-notify/bootstrap-notify.js",
"node_modules/chartist/dist/chartist.js"
]
Create a new service module to implement the GitHub Search API.
ng g module services/githubSearch --project=github-search-web
CREATE apps/github-search-web/src/app/services/github-search/github-search.module.ts (196 bytes)
Add a service to the module. The service will provide endpoints for the application components to consume.
ng g service services/github-search/githubSearch --project=github-search-web
CREATE apps/github-search-web/src/app/services/github-search/github-search.service.spec.ts (364 bytes)
CREATE apps/github-search-web/src/app/services/github-search/github-search.service.ts (141 bytes)
Note: Add the module to the
CoreModule
and make sure to provide the service.
@NgModule({
imports: [
CommonModule,
GithubSearchModule,
SearchLayoutModule,
SharedModule,
SiteModule
],
declarations: [],
exports: [
GithubSearchModule,
SearchLayoutModule,
SharedModule,
SiteModule
],
providers: [
GithubSearchService // CREATES A GLOBAL INSTANCE OF THE SEARCH SERVICE; INJECTABLE;
]
})
export class CoreModule {...}
Create an interface to define the search criteria that the search service will use. The interface
should match the values collected by the search form.
export interface SearchCriteria {
owner: string;
fullName: string;
}
The Search form is a basic Reactive Form with validation information.
- text inputs
- validation based on the form/control and validator status
- submit button disabled until form is valid
- form display the
value
of the form in JSON format (debugging purposes).
<h2>GitHub Search: </h2>
<p>Use the search to find GitHub repository and owner information.</p>
<form novalidate (ngSubmit)="onSubmit(searchCriteria)" [formGroup]="searchCriteria">
<div>
<div class="error" *ngIf="searchCriteria.get('owner').hasError('required') && searchCriteria.get('owner').touched">
Owner name is required.
</div>
<div class="error" *ngIf="searchCriteria.get('owner').hasError('minlength') && searchCriteria.get('owner').touched">
Minimum of 2 characters
</div>
Owner: <input type="text" placeholder="buildmotion" formControlName="owner">
</div>
<div>
Full Name: <input type="text" placeholder="Matt Vaughn" formControlName="fullName">
</div>
<div>
<button type="submit" [disabled]="searchCriteria.invalid">Sign up</button>
</div>
<p *ngIf="searchCriteria.valid && ">
Form Value: {{ searchCriteria.value | json }}
</p>
</form>
The form is implemented with ReactiveFormsModule
.
- using FormBuilder to create a FormGroup
- adding validation to the input controls.
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms';
import { ComponentBase } from '@angularlicious/foundation';
import { AngularliciousLoggingService, Severity } from '@angularlicious/logging';
import { Router } from '@angular/router';
// tslint:disable-next-line: nx-enforce-module-boundaries
import { GithubSearchService } from 'apps/github-search-web/src/app/services/github-search/github-search.service';
@Component({
selector: 'angularlicious-search-form',
templateUrl: './search-form.component.html',
styleUrls: ['./search-form.component.css']
})
export class SearchFormComponent extends ComponentBase implements OnInit {
searchCriteria: FormGroup;
constructor(
loggingService: AngularliciousLoggingService,
router: Router,
private formBuilder: FormBuilder,
private searchService: GithubSearchService
) {
super('SearchFormComponent', loggingService, router);
}
ngOnInit() {
this.searchCriteria = this.formBuilder.group(
{
owner: new FormControl('', [
Validators.required,
Validators.minLength(2)
]),
fullName: new FormControl('')
}
);
}
onSubmit(criteria) {
this.loggingService.log(this.componentName, Severity.Information, `Preparing to process search criteria: ${JSON.stringify(criteria.value)}`)
// retrieve search criteria values and search GitHub using API
const searchCriteria = {...criteria.value};
this.searchService.searchByOwner(searchCriteria);
}
}
Update the GithubSearchService
with the following:
- extend the class using the base service class
- inject a logging service into the constructor
- indicate the name of the service in the constructor
- create a
searchGithub
method- the input is defined by the
SearchCriteria
interface - the method returns an Observable of type
ServiceResponse
- the input is defined by the
import { Injectable } from '@angular/core';
import { ServiceBase, ServiceResponse } from '@angularlicious/foundation';
import { AngularliciousLoggingService } from '@angularlicious/logging';
import { Observable } from 'rxjs';
import { SearchCriteria } from '../../layouts/search-layout/models/i-search-criteria.model';
@Injectable({
providedIn: 'root'
})
export class GithubSearchService extends ServiceBase {
constructor(
loggingService: AngularliciousLoggingService
) {
super(loggingService);
this.serviceName = 'GithubSearchService';
}
searchGithub(searchCriteria: SearchCriteria): Observable<ServiceResponse> {
throw new Error("Method not implemented.");
}
}
The domain service is the entry-point|end-point|API for the application. Components in the application will be consumers of the APIs provided by the domain services. Each domain service will encapsulate the business logic using a dedicated business logic layer
. This layer has a coordinator of business logic called a BusinessProvider
- it is an injectable service, but it provides the abstraction between the service and business layer.
We want the services to have the responsibility of accepting requests and delivering response. The actual work involved in the processing of a request and creating a response is delegated to the business layer.
This application will use the v3
API from GitHub. Documentation here.
The following is required in the Accept
header to explicitly request version 3.
Accept: application/vnd.github.v3+json
ng g component layouts/search-layout/ownerResults
CREATE apps/github-search-web/src/app/layouts/search-layout/owner-results/owner-results.component.html (32 bytes)
CREATE apps/github-search-web/src/app/layouts/search-layout/owner-results/owner-results.component.spec.ts (671 bytes)CREATE apps/github-search-web/src/app/layouts/search-layout/owner-results/owner-results.component.ts (307 bytes)
CREATE apps/github-search-web/src/app/layouts/search-layout/owner-results/owner-results.component.css (0 bytes)UPDATE apps/github-search-web/src/app/layouts/search-layout/search-layout.module.ts (871 bytes)
https://github.com/search?utf8=%E2%9C%93&q=user%3Aangularlicious+language%3ATypeScript&type=Repositories&ref=advsearch&l=&l=TypeScript
https://github.com/search?utf8=%E2%9C%93&q=user%3Aangularlicious+language%3ATypeScript&type=Repositories&ref=advsearch&l=&l=TypeScript
The following shows the version of Angular used for the project.
ng v
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 7.0.1
Node: 10.0.0
OS: win32 x64
Angular: 7.2.9
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.10.7
@angular-devkit/build-angular 0.10.7
@angular-devkit/build-optimizer 0.10.7
@angular-devkit/build-webpack 0.10.7
@angular-devkit/core 7.0.7
@angular-devkit/schematics 7.0.1
@angular/cli 7.0.1
@ngtools/webpack 7.0.7
@schematics/angular 7.0.1
@schematics/update 0.10.1
rxjs 6.3.3
typescript 3.1.6
webpack 4.19.1
This project was generated with Angular CLI using Nrwl Nx.
create-nx-workspace angularlicious-github-search
Nx is an open source toolkit for enterprise Angular applications.
Nx is designed to help you create and build enterprise grade Angular applications. It provides an opinionated approach to application project structure and patterns.
Watch a 5-minute video on how to get started with Nx.
Run ng generate app myapp
to generate an application. When using Nx, you can create multiple applications and libraries in the same CLI workspace. Read more here.
Run ng serve --project=myapp
for a dev server. Navigate to http://localhost:4200/
. The app will automatically reload if you change any of the source files.
Run ng generate component component-name --project=myapp
to generate a new component. You can also use ng generate directive|pipe|service|class|guard|interface|enum|module
.
Run ng build --project=myapp
to build the project. The build artifacts will be stored in the dist/
directory. Use the --prod
flag for a production build.
Run ng test
to execute the unit tests via Karma.
Run ng e2e
to execute the end-to-end tests via Protractor.
Before running the tests make sure you are serving the app via ng serve
.
To get more help on the Angular CLI use ng help
or go check out the Angular CLI README.