├── .editorconfig
├── .gitignore
├── README.md
├── angular-cli-build.js
├── angular-cli.json
├── config
├── environment.dev.ts
├── environment.js
├── environment.prod.ts
├── karma-test-shim.js
├── karma.conf.js
└── protractor.conf.js
├── e2e
├── app.e2e-spec.ts
├── app.po.ts
├── tsconfig.json
└── typings.d.ts
├── package.json
├── public
└── .npmignore
├── src
├── app
│ ├── app.component.css
│ ├── app.component.html
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── app.routing.ts
│ ├── chapter.service.ts
│ ├── chapters-master
│ │ ├── chapters-master.component.css
│ │ ├── chapters-master.component.html
│ │ ├── chapters-master.component.ts
│ │ └── index.ts
│ ├── db
│ │ ├── chapter-members.ts
│ │ ├── chapters.ts
│ │ ├── defense-measures.ts
│ │ ├── guilds.ts
│ │ ├── people.ts
│ │ └── version.ts
│ ├── domain
│ │ ├── chapter-location.ts
│ │ ├── chapter-member.ts
│ │ ├── chapter.ts
│ │ ├── customer.ts
│ │ ├── guild.ts
│ │ ├── http-response.ts
│ │ ├── location.ts
│ │ └── person.ts
│ ├── environment.ts
│ ├── guild.service.ts
│ ├── guilds-detail
│ │ ├── guilds-detail.component.css
│ │ ├── guilds-detail.component.html
│ │ ├── guilds-detail.component.ts
│ │ └── index.ts
│ ├── guilds-master
│ │ ├── guilds-master.component.css
│ │ ├── guilds-master.component.html
│ │ ├── guilds-master.component.ts
│ │ └── index.ts
│ ├── home
│ │ ├── home.component.css
│ │ ├── home.component.html
│ │ ├── home.component.ts
│ │ └── index.ts
│ ├── in-memory-data.service.ts
│ ├── index.ts
│ ├── main-navigation
│ │ ├── index.ts
│ │ ├── main-navigation.component.css
│ │ ├── main-navigation.component.html
│ │ ├── main-navigation.component.spec.ts
│ │ └── main-navigation.component.ts
│ ├── member.service.ts
│ ├── members-master
│ │ ├── index.ts
│ │ ├── members-master.component.css
│ │ ├── members-master.component.html
│ │ └── members-master.component.ts
│ ├── sandbox
│ │ ├── chapter-list
│ │ │ ├── chapter-list.component.css
│ │ │ ├── chapter-list.component.html
│ │ │ ├── chapter-list.component.ts
│ │ │ └── index.ts
│ │ ├── chapter-member-list
│ │ │ ├── chapter-member-list.component.css
│ │ │ ├── chapter-member-list.component.html
│ │ │ ├── chapter-member-list.component.ts
│ │ │ └── index.ts
│ │ ├── chapter-reactive-form
│ │ │ ├── chapter-reactive-form.component.css
│ │ │ ├── chapter-reactive-form.component.html
│ │ │ ├── chapter-reactive-form.component.ts
│ │ │ └── index.ts
│ │ ├── guild-list
│ │ │ ├── guild-list.component.css
│ │ │ ├── guild-list.component.html
│ │ │ ├── guild-list.component.ts
│ │ │ └── index.ts
│ │ ├── sandbox.module.ts
│ │ ├── sandbox.routing.ts
│ │ └── sandbox.service.ts
│ ├── shared
│ │ └── index.ts
│ ├── vadacl
│ │ ├── interfaces.ts
│ │ ├── messages
│ │ │ └── messages-en.ts
│ │ ├── patterns
│ │ │ └── patterns-en.ts
│ │ ├── vadacl.ts
│ │ └── validation-methods.ts
│ └── version
│ │ ├── index.ts
│ │ ├── version.component.html
│ │ ├── version.component.spec.ts
│ │ ├── version.component.ts
│ │ ├── version.service.spec.ts
│ │ └── version.service.ts
├── favicon.ico
├── index.html
├── main.ts
├── system-config.ts
├── tsconfig.json
└── typings.d.ts
├── tslint.json
└── typings.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.md]
13 | max_line_length = 0
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 |
7 | # dependencies
8 | /node_modules
9 | /bower_components
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | *.launch
16 | .settings/
17 |
18 | # misc
19 | /.sass-cache
20 | /connect.lock
21 | /coverage/*
22 | /libpeerconnection.log
23 | npm-debug.log
24 | testem.log
25 | /typings
26 |
27 | # e2e
28 | /e2e/*.js
29 | /e2e/*.map
30 |
31 | #System Files
32 | .DS_Store
33 | Thumbs.db
34 |
35 | #Scratch
36 | /scratch
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # angular2-sandbox-guildrunner
2 | The GuildRunner web application is a sandbox project for learning how to build Angular 2 web applications through
3 | research and experimentation. Blog posts concerning the evolution of the project can be found at
4 | [http://www.thoughtdelimited.org/thoughts](http://www.thoughtdelimited.org/thoughts/)
5 |
6 | ## Angular / Angular CLI Version Notes
7 |
8 | As of release 0.0.3, this application is built around Angular 2 release candidate 5 (RC5) and Angular CLI version beta.10. So it does not
9 | utilize Webpack as its module loader (it uses SystemJS), but it does use the ngModule feature introduced in Angular 2 RC5.
10 |
11 | ## Installation Instructions
12 |
13 | To download and execute this web application on your own system, perform the following steps:
14 |
15 | 1. Install [node.js](https://nodejs.org/en/) on your system if you don't already have it.
16 |
17 | 2. Perform a global install the latest version of [Angular CLI](https://cli.angular.io/) that uses SystemJS as its module loader.
18 | 1. The current version of Angular CLI still uses SystemJS, so you can perform the install with the following
19 | command: **npm install -g angular-cli**.
20 | 2. In the near future, Angular CLI will use Webpack as its module loader, at which point you would need to
21 | install the last SystemJS version rather than the current CLI version.
22 |
23 | 3. Download the repo to a project folder on your system.
24 |
25 | 4. Open a terminal/command prompt in the project folder and execute the following commands:
26 | 1. **npm install** to download and install the necessary node modules (this will take at least a few minutes)
27 | 2. **ng serve**, which will create a "dist" folder in the project and will serve the application
28 |
29 |
30 | ## Release Highlights
31 |
32 | ### 0.0.7
33 |
34 | * Revised the sandbox Chapter edit form to use the [vadacl](https://github.com/bcswartz/vadacl) validation library,
35 | which allows developers to define validation rules at both the data object and component level.
36 |
37 | ### 0.0.6
38 |
39 | * Created a sandbox version of a Chapter edit form using reactive form classes (FormGroup, FormArray, and FormControl)
40 | and Validators.
41 |
42 | ### 0.0.5
43 |
44 | * Removed Address and Member domain classes; added Person, Location, Chapter, ChapterLocation, and ChapterMember classes; refactored Guild class
45 | * Created new approach to setting domain class property defaults and setting property values in the constructor
46 | * Created sandbox views and master list views for Chapters and ChapterMembers
47 | * Utilized Promise.all() to resolve multiple promises in component methods that need data from multiple service methods
48 |
49 | ### 0.0.4
50 |
51 | * Added "incorporationYear" and "email" properties to the Guild data and domain class
52 | * Refactored the Guild domain class to allow it to be instantiated without data
53 | * Created basic add/edit form for guild objects
54 | * Created first draft of proof-of-concept for validating a pre-RC6 template-driven form
55 | * Created HttpResponse object for transporting HTTP response data from the service to the component
56 |
57 | ### 0.0.3
58 |
59 | * Upgraded Angular modules to release candidate 5 (RC5)
60 | * Refactored routing code to use latest routing setup and syntax
61 | * Refactored multiple files to utilize ngModule introduced in RC5
62 | * Moved sandbox feature files into separate module and routing file
63 |
64 | ### 0.0.2
65 |
66 | * Added Bootstrap navigation bar
67 | * Added routing
68 | * Created guild, address, and member data and domain classes
69 | * Created "sandbox" area of application to hold experimental/diagnostic features
70 | * Created guild master list component to display data from instances of the Guild domain class
71 |
72 | ### 0.0.1
73 |
74 | * Basic application foundation established
75 | * Added [in-memory web API](https://angular.io/docs/ts/latest/guide/server-communication.html#!#in-mem-web-api) to application to make use of mock data
76 | * Created "db" folder to hold mock data exports
77 |
78 |
79 |
--------------------------------------------------------------------------------
/angular-cli-build.js:
--------------------------------------------------------------------------------
1 | // Angular-CLI build configuration
2 | // This file lists all the node_modules files that will be used in a build
3 | // Also see https://github.com/angular/angular-cli/wiki/3rd-party-libs
4 |
5 | /* global require, module */
6 |
7 | var Angular2App = require('angular-cli/lib/broccoli/angular2-app');
8 |
9 | module.exports = function(defaults) {
10 | return new Angular2App(defaults, {
11 | vendorNpmFiles: [
12 | 'systemjs/dist/system-polyfills.js',
13 | 'systemjs/dist/system.src.js',
14 | 'zone.js/dist/**/*.+(js|js.map)',
15 | 'es6-shim/es6-shim.js',
16 | 'reflect-metadata/**/*.+(ts|js|js.map)',
17 | 'rxjs/**/*.+(js|js.map)',
18 | '@angular/**/*.+(js|js.map)',
19 | 'angular2-in-memory-web-api/*.+(js)'
20 | ]
21 | });
22 | };
23 |
--------------------------------------------------------------------------------
/angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "project": {
3 | "version": "1.0.0-beta.10",
4 | "name": "guildrunner"
5 | },
6 | "apps": [
7 | {
8 | "main": "src/main.ts",
9 | "tsconfig": "src/tsconfig.json",
10 | "mobile": false
11 | }
12 | ],
13 | "addons": [],
14 | "packages": [],
15 | "e2e": {
16 | "protractor": {
17 | "config": "config/protractor.conf.js"
18 | }
19 | },
20 | "test": {
21 | "karma": {
22 | "config": "config/karma.conf.js"
23 | }
24 | },
25 | "defaults": {
26 | "prefix": "app",
27 | "sourceDir": "src",
28 | "styleExt": "css",
29 | "prefixInterfaces": false,
30 | "lazyRoutePrefix": "+"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/config/environment.dev.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: false
3 | };
4 |
--------------------------------------------------------------------------------
/config/environment.js:
--------------------------------------------------------------------------------
1 | // Angular-CLI server configuration
2 | // Unrelated to environment.dev|prod.ts
3 |
4 | /* jshint node: true */
5 |
6 | module.exports = function(environment) {
7 | return {
8 | environment: environment,
9 | baseURL: '/',
10 | locationType: 'auto'
11 | };
12 | };
13 |
--------------------------------------------------------------------------------
/config/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/config/karma-test-shim.js:
--------------------------------------------------------------------------------
1 | // Test shim for Karma, needed to load files via SystemJS
2 |
3 | /*global jasmine, __karma__, window*/
4 | Error.stackTraceLimit = Infinity;
5 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
6 |
7 | __karma__.loaded = function () {
8 | };
9 |
10 | var distPath = '/base/dist/';
11 | var appPaths = ['app']; //Add all valid source code folders here
12 |
13 | function isJsFile(path) {
14 | return path.slice(-3) == '.js';
15 | }
16 |
17 | function isSpecFile(path) {
18 | return path.slice(-8) == '.spec.js';
19 | }
20 |
21 | function isAppFile(path) {
22 | return isJsFile(path) && appPaths.some(function(appPath) {
23 | var fullAppPath = distPath + appPath + '/';
24 | return path.substr(0, fullAppPath.length) == fullAppPath;
25 | });
26 | }
27 |
28 | var allSpecFiles = Object.keys(window.__karma__.files)
29 | .filter(isSpecFile)
30 | .filter(isAppFile);
31 |
32 | // Load our SystemJS configuration.
33 | System.config({
34 | baseURL: distPath
35 | });
36 |
37 | System.import('system-config.js').then(function() {
38 | // Load and configure the TestComponentBuilder.
39 | return Promise.all([
40 | System.import('@angular/core/testing'),
41 | System.import('@angular/platform-browser-dynamic/testing')
42 | ]).then(function (providers) {
43 | var testing = providers[0];
44 | var testingBrowser = providers[1];
45 |
46 | testing.setBaseTestProviders(testingBrowser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
47 | testingBrowser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);
48 | });
49 | }).then(function() {
50 | // Finally, load all spec files.
51 | // This will run the tests directly.
52 | return Promise.all(
53 | allSpecFiles.map(function (moduleName) {
54 | return System.import(moduleName);
55 | }));
56 | }).then(__karma__.start, __karma__.error);
57 |
--------------------------------------------------------------------------------
/config/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '..',
7 | frameworks: ['jasmine'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher')
11 | ],
12 | customLaunchers: {
13 | // chrome setup for travis CI using chromium
14 | Chrome_travis_ci: {
15 | base: 'Chrome',
16 | flags: ['--no-sandbox']
17 | }
18 | },
19 | files: [
20 | { pattern: 'dist/vendor/es6-shim/es6-shim.js', included: true, watched: false },
21 | { pattern: 'dist/vendor/zone.js/dist/zone.js', included: true, watched: false },
22 | { pattern: 'dist/vendor/reflect-metadata/Reflect.js', included: true, watched: false },
23 | { pattern: 'dist/vendor/systemjs/dist/system-polyfills.js', included: true, watched: false },
24 | { pattern: 'dist/vendor/systemjs/dist/system.src.js', included: true, watched: false },
25 | { pattern: 'dist/vendor/zone.js/dist/async-test.js', included: true, watched: false },
26 | { pattern: 'dist/vendor/zone.js/dist/fake-async-test.js', included: true, watched: false },
27 |
28 | { pattern: 'config/karma-test-shim.js', included: true, watched: true },
29 |
30 | // Distribution folder.
31 | { pattern: 'dist/**/*', included: false, watched: true }
32 | ],
33 | exclude: [
34 | // Vendor packages might include spec files. We don't want to use those.
35 | 'dist/vendor/**/*.spec.js'
36 | ],
37 | preprocessors: {},
38 | reporters: ['progress'],
39 | port: 9876,
40 | colors: true,
41 | logLevel: config.LOG_INFO,
42 | autoWatch: true,
43 | browsers: ['Chrome'],
44 | singleRun: false
45 | });
46 | };
47 |
--------------------------------------------------------------------------------
/config/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/docs/referenceConf.js
3 |
4 | /*global jasmine */
5 | var SpecReporter = require('jasmine-spec-reporter');
6 |
7 | exports.config = {
8 | allScriptsTimeout: 11000,
9 | specs: [
10 | '../e2e/**/*.e2e-spec.ts'
11 | ],
12 | capabilities: {
13 | 'browserName': 'chrome'
14 | },
15 | directConnect: true,
16 | baseUrl: 'http://localhost:4200/',
17 | framework: 'jasmine',
18 | jasmineNodeOpts: {
19 | showColors: true,
20 | defaultTimeoutInterval: 30000,
21 | print: function() {}
22 | },
23 | useAllAngular2AppRoots: true,
24 | beforeLaunch: function() {
25 | require('ts-node').register({
26 | project: 'e2e'
27 | });
28 | },
29 | onPrepare: function() {
30 | jasmine.getEnv().addReporter(new SpecReporter());
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { GuildrunnerPage } from './app.po';
2 |
3 | describe('guildrunner App', function() {
4 | let page: GuildrunnerPage;
5 |
6 | beforeEach(() => {
7 | page = new GuildrunnerPage();
8 | });
9 |
10 | it('should display main header text', () => {
11 | page.navigateTo();
12 | expect(page.getMainHeaderText()).toEqual('GuildRunner');
13 | });
14 |
15 | it('should display the version number preceded by a label', () => {
16 | page.navigateTo();
17 | expect(page.getVersionText()).toEqual('Version: 0.0.7');
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | export class GuildrunnerPage {
2 | navigateTo() {
3 | return browser.get('/');
4 | }
5 |
6 | getMainHeaderText() {
7 | return element(by.css('app-root h1')).getText();
8 | }
9 |
10 | getVersionText() {
11 | return element(by.css('app-root app-version p')).getText();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "declaration": false,
5 | "emitDecoratorMetadata": true,
6 | "experimentalDecorators": true,
7 | "mapRoot": "",
8 | "module": "commonjs",
9 | "moduleResolution": "node",
10 | "noEmitOnError": true,
11 | "noImplicitAny": false,
12 | "rootDir": ".",
13 | "sourceMap": true,
14 | "sourceRoot": "/",
15 | "target": "es5"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/e2e/typings.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "guildrunner",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "angular-cli": {},
6 | "scripts": {
7 | "start": "ng serve",
8 | "postinstall": "typings install",
9 | "lint": "tslint \"src/**/*.ts\"",
10 | "test": "ng test",
11 | "pree2e": "webdriver-manager update",
12 | "e2e": "protractor"
13 | },
14 | "private": true,
15 | "dependencies": {
16 | "@angular/common": "2.0.0-rc.5",
17 | "@angular/compiler": "2.0.0-rc.5",
18 | "@angular/core": "2.0.0-rc.5",
19 | "@angular/forms": "0.3.0",
20 | "@angular/http": "2.0.0-rc.5",
21 | "@angular/platform-browser": "2.0.0-rc.5",
22 | "@angular/platform-browser-dynamic": "2.0.0-rc.5",
23 | "@angular/router": "3.0.0-rc.1",
24 | "angular2-in-memory-web-api": "0.0.15",
25 | "es6-shim": "0.35.1",
26 | "reflect-metadata": "0.1.3",
27 | "rxjs": "5.0.0-beta.6",
28 | "systemjs": "0.19.27",
29 | "zone.js": "0.6.12"
30 | },
31 | "devDependencies": {
32 | "angular-cli": "1.0.0-beta.10",
33 | "codelyzer": "0.0.20",
34 | "ember-cli-inject-live-reload": "1.4.0",
35 | "jasmine-core": "2.4.1",
36 | "jasmine-spec-reporter": "2.5.0",
37 | "karma": "0.13.22",
38 | "karma-chrome-launcher": "0.2.3",
39 | "karma-jasmine": "0.3.8",
40 | "protractor": "3.3.0",
41 | "ts-node": "0.5.5",
42 | "tslint": "3.11.0",
43 | "typescript": "1.8.10",
44 | "typings": "1.3.1"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/public/.npmignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bcswartz/angular2-sandbox-guildrunner/c57c5ff1083b1b6ae5a1b6d5ae93adc9e547efa8/public/.npmignore
--------------------------------------------------------------------------------
/src/app/app.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bcswartz/angular2-sandbox-guildrunner/c57c5ff1083b1b6ae5a1b6d5ae93adc9e547efa8/src/app/app.component.css
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{title}}
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 |
3 | import { addProviders, async, inject } from '@angular/core/testing';
4 | import { AppComponent } from './app.component';
5 |
6 | describe('App: Guildrunner', () => {
7 | beforeEach(() => {
8 | addProviders([AppComponent]);
9 | });
10 |
11 | it('should create the app',
12 | inject([AppComponent], (app: AppComponent) => {
13 | expect(app).toBeTruthy();
14 | }));
15 |
16 | it('should have as title \'app works!\'',
17 | inject([AppComponent], (app: AppComponent) => {
18 | expect(app.title).toEqual('GuildRunner');
19 | }));
20 | });
21 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | moduleId: module.id,
5 | selector: 'app-root',
6 | templateUrl: 'app.component.html',
7 | styleUrls: ['app.component.css']
8 | })
9 |
10 | export class AppComponent {
11 | title = 'GuildRunner';
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | //Modules and routing imports
2 | import { NgModule } from '@angular/core';
3 | import { BrowserModule } from '@angular/platform-browser';
4 | import { HttpModule } from '@angular/http';
5 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
6 | import { SandboxModule } from './sandbox/sandbox.module';
7 | import { routing } from './app.routing';
8 |
9 | //Declarations (components, directives, and pipes)
10 | import { AppComponent } from './app.component';
11 | import { VersionComponent } from './version/version.component';
12 | import { MainNavigationComponent } from './main-navigation/main-navigation.component';
13 | import { HomeComponent } from "./home/home.component";
14 | import { GuildsMasterComponent } from "./guilds-master/guilds-master.component";
15 | import { GuildsDetailComponent } from "./guilds-detail/guilds-detail.component";
16 | import { ChaptersMasterComponent } from "./chapters-master/chapters-master.component";
17 | import { MembersMasterComponent } from "./members-master/members-master.component";
18 |
19 | //Providers (services)
20 | import { VersionService } from './version/version.service';
21 | import { GuildService } from './guild.service';
22 | import { ChapterService } from "./chapter.service";
23 | import { MemberService } from "./member.service";
24 |
25 | // Imports for loading & configuring the in-memory web api
26 | import { XHRBackend } from '@angular/http';
27 | import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api';
28 | import { InMemoryDataService } from './in-memory-data.service';
29 |
30 |
31 | @NgModule({
32 | imports: [
33 | BrowserModule,
34 | HttpModule,
35 | FormsModule,
36 | ReactiveFormsModule,
37 | routing,
38 | SandboxModule
39 | ],
40 |
41 | declarations: [
42 | AppComponent,
43 | VersionComponent,
44 | MainNavigationComponent,
45 | HomeComponent,
46 | GuildsMasterComponent,
47 | GuildsDetailComponent,
48 | ChaptersMasterComponent,
49 | MembersMasterComponent
50 | ],
51 |
52 | providers: [
53 | { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server
54 | { provide: SEED_DATA, useClass: InMemoryDataService }, // in-mem server data
55 | VersionService,
56 | GuildService,
57 | ChapterService,
58 | MemberService
59 | ],
60 |
61 | bootstrap: [
62 | AppComponent
63 | ],
64 | })
65 |
66 | export class AppModule {}
67 |
68 |
--------------------------------------------------------------------------------
/src/app/app.routing.ts:
--------------------------------------------------------------------------------
1 | import { Routes, RouterModule } from '@angular/router';
2 |
3 | import { GuildsMasterComponent } from "./guilds-master/guilds-master.component";
4 | import { HomeComponent } from "./home/home.component";
5 | import { GuildsDetailComponent } from "./guilds-detail/guilds-detail.component";
6 | import { ChaptersMasterComponent } from "./chapters-master/chapters-master.component";
7 | import { MembersMasterComponent } from "./members-master/members-master.component";
8 |
9 | const appRoutes: Routes = [
10 | { path: 'home', component: HomeComponent },
11 | { path: 'guilds', component: GuildsMasterComponent },
12 | { path: 'guilds/:id', component: GuildsDetailComponent}, //Edit route
13 | { path: 'guild', component: GuildsDetailComponent }, //Add route
14 | { path: 'chapters', component: ChaptersMasterComponent},
15 | { path: 'members', component: MembersMasterComponent },
16 | { path: '', redirectTo: '/home', pathMatch: 'full' }
17 | ];
18 |
19 | export const routing = RouterModule.forRoot( appRoutes );
20 |
--------------------------------------------------------------------------------
/src/app/chapter.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Http, Headers } from '@angular/http';
3 |
4 | import { HttpResponse } from './domain/http-response';
5 | import { Chapter } from './domain/chapter';
6 |
7 | import 'rxjs/add/operator/toPromise';
8 |
9 | @Injectable()
10 | export class ChapterService {
11 |
12 | private chapterUrl = 'app/chapters';
13 |
14 | constructor( private http: Http ) { }
15 |
16 | getChapters() {
17 | return this.http.get( this.chapterUrl )
18 | .toPromise()
19 | .then( response => {
20 | let serviceResponse = new HttpResponse( response );
21 | serviceResponse.data = response.json().data;
22 | return serviceResponse;
23 | })
24 | .catch( error => {
25 | let serviceResponse = new HttpResponse( error );
26 | return serviceResponse;
27 | });
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/app/chapters-master/chapters-master.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bcswartz/angular2-sandbox-guildrunner/c57c5ff1083b1b6ae5a1b6d5ae93adc9e547efa8/src/app/chapters-master/chapters-master.component.css
--------------------------------------------------------------------------------
/src/app/chapters-master/chapters-master.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Chapters
4 |
5 |
6 |
7 | ID |
8 | Name |
9 | Head Chapter? |
10 | Guild |
11 | Founded |
12 |
13 |
14 |
15 |
16 | {{chapter.id}} |
17 | {{chapter.name}} |
18 | {{chapter.headChapter ? 'Yes' : 'No'}} |
19 | {{guilds[ chapter.guildId ].name}} |
20 | {{chapter.founded}} |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/app/chapters-master/chapters-master.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { ChapterService } from '../chapter.service';
3 | import { GuildService } from '../guild.service';
4 | import { Chapter } from '../domain/chapter';
5 | import { HttpResponse } from '../domain/http-response';
6 |
7 | @Component({
8 | moduleId: module.id,
9 | selector: 'app-chapters-master',
10 | templateUrl: 'chapters-master.component.html',
11 | styleUrls: ['chapters-master.component.css']
12 | })
13 | export class ChaptersMasterComponent implements OnInit {
14 |
15 | chapters: Chapter[] = [];
16 | guilds: any = {};
17 |
18 | constructor(
19 | private chapterService: ChapterService,
20 | private guildService: GuildService
21 | ) { }
22 |
23 | ngOnInit() {
24 |
25 | Promise.all( [
26 | this.chapterService.getChapters(),
27 | this.guildService.getGuilds()
28 | ]
29 | ).then( (results:Promise[]) => {
30 | results[0]['data'].forEach(chapterData => {
31 | this.chapters.push(new Chapter(chapterData))
32 | });
33 | results[1]['data'].forEach(guildData => {
34 | this.guilds[guildData.id] = guildData;
35 | });
36 | });
37 |
38 | }
39 |
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/app/chapters-master/index.ts:
--------------------------------------------------------------------------------
1 | export * from './chapters-master.component';
2 |
--------------------------------------------------------------------------------
/src/app/db/chapter-members.ts:
--------------------------------------------------------------------------------
1 | import { people } from './people';
2 |
3 | function createChapterMember( id, chapterId, startDate, endDate, isActive ) {
4 | return {
5 | firstName: people[id].firstName,
6 | middleName: people[id].middleName,
7 | lastName: people[id].lastName,
8 | phone: people[id].phone,
9 | altPhone: people[id].altPhone,
10 | email: people[id].email,
11 | isUndead: people[id].isUndead,
12 | location: {
13 | address: people[id].location.address,
14 | address2: people[id].location.address2,
15 | city: people[id].location.city,
16 | stateProvince: people[id].location.stateProvince,
17 | postalCode: people[id].location.postalCode
18 | },
19 | id: id,
20 | chapterId: chapterId,
21 | startDate: startDate,
22 | endDate: endDate,
23 | isActive: isActive
24 | }
25 | }
26 |
27 | let chapterMembers = [
28 | createChapterMember( 1, 1, '2/3/1987', null, true ),
29 | createChapterMember( 2, 1, '7/8/1991', '11/15/15', false ),
30 | createChapterMember( 3, 1, '11/4/1993', null, true ),
31 | createChapterMember( 4, 1, '9/14/2002', null, true ),
32 | createChapterMember( 5, 2, '3/17/2001', null, true ),
33 | createChapterMember( 6, 2, '2/4/1997', null, true ),
34 | createChapterMember( 7, 2, '8/1/1988', null, true ),
35 | createChapterMember( 8, 3, '5/24/2003', null, true ),
36 | createChapterMember( 9, 3, '10/14/1991', '10/5/14', false ),
37 | createChapterMember( 10, 3, '2/11/1990', null, true ),
38 | createChapterMember( 11, 4, '3/4/1991', null, true ),
39 | createChapterMember( 12, 4, '8/14/1998', null, true ),
40 | createChapterMember( 13, 4, '9/3/1997', null, true ),
41 | createChapterMember( 14, 4, '4/27/2005', null, true ),
42 | createChapterMember( 15, 5, '10/23/1992', null, true ),
43 | createChapterMember( 16, 5, '12/4/1984', null, true ),
44 | createChapterMember( 17, 6, '10/13/1998', null, true ),
45 | createChapterMember( 18, 6, '3/19/2008', null, true ),
46 | createChapterMember( 19, 6, '9/13/2003', '12/1/15', false ),
47 | createChapterMember( 20, 7, '11/11/2009', null, true ),
48 | createChapterMember( 21, 7, '6/9/2000', null, true ),
49 | createChapterMember( 22, 7, '3/2/1998', null, true ),
50 | createChapterMember( 23, 8, '7/15/2011', null, true ),
51 | createChapterMember( 24, 8, '6/1/1994', null, true ),
52 | createChapterMember( 25, 8, '7/1/1991', null, true ),
53 | createChapterMember( 26, 9, '1/13/2003', null, true ),
54 | createChapterMember( 27, 9, '4/26/2004', '3/3/2016', false ),
55 | createChapterMember( 28, 9, '12/13/2012', null, true ),
56 | createChapterMember( 29, 9, '4/6/2008', null, true ),
57 | createChapterMember( 30, 9, '2/22/1999', null, true ),
58 | createChapterMember( 31, 10, '3/23/2010', null, true ),
59 | createChapterMember( 32, 10, '2/17/2001', null, true ),
60 | createChapterMember( 33, 10, '7/23/2002', null, true ),
61 | createChapterMember( 34, 10, '2/7/1997', null, true ),
62 | createChapterMember( 35, 10, '7/8/1995', '8/9/1999', false ),
63 | createChapterMember( 36, 10, '4/4/2001', null, true ),
64 | createChapterMember( 37, 10, '9/21/2004', null, true ),
65 | createChapterMember( 38, 1, '8/2/1996', null, true ),
66 | createChapterMember( 39, 1, '11/2/1988', null, true ),
67 | createChapterMember( 40, 2, '5/5/1993', null, true ),
68 | ];
69 |
70 | export { chapterMembers }
71 |
--------------------------------------------------------------------------------
/src/app/db/chapters.ts:
--------------------------------------------------------------------------------
1 | let chapters = [
2 | {
3 | id: 1,
4 | guildId: 1,
5 | name: 'West Central',
6 | headChapter: true,
7 | founded: '4/5/1878',
8 | email: 'bsmiths_wc@citywire.com',
9 | location: {
10 | address: '136 Chanter Circle',
11 | address2: null,
12 | city: 'New London',
13 | stateProvince: 'Melon',
14 | postalCode: '02001-0053',
15 | entryPoints: 2,
16 | floors: 2,
17 | hasBasement: true,
18 | rooftopAccess: false
19 | },
20 | defenses: [ 1, 2, 5, 6 ]
21 | },
22 | {
23 | id: 2,
24 | guildId: 1,
25 | name: 'Royal District',
26 | headChapter: false,
27 | founded: '11/21/1931',
28 | email: 'bsmiths_rd@citywire.com',
29 | location: {
30 | address: '2379 Kings Way',
31 | address2: null,
32 | city: 'New London',
33 | stateProvince: 'Aristo',
34 | postalCode: '05001-1100',
35 | entryPoints: 1,
36 | floors: 3,
37 | hasBasement: true,
38 | rooftopAccess: true
39 | },
40 | defenses: [ 3, 7, 8 ]
41 | },
42 | {
43 | id: 3,
44 | guildId: 1,
45 | name: 'Fountain District',
46 | headChapter: false,
47 | founded: '10/13/1899',
48 | email: 'bsmiths_fd@citywire.com',
49 | location: {
50 | address: '34 Stellar Blvd',
51 | address2: null,
52 | city: 'New London',
53 | stateProvince: 'Alcott',
54 | postalCode: '01001-1100',
55 | entryPoints: 3,
56 | floors: 1,
57 | hasBasement: false,
58 | rooftopAccess: false
59 | },
60 | defenses: [ 1, 2, 4 ]
61 | },
62 | {
63 | id: 4,
64 | guildId: 2,
65 | name: 'Eastern Market',
66 | headChapter: true,
67 | founded: '6/16/1934',
68 | email: 'eastercontractkill@citywire.com',
69 | location: {
70 | address: '6870 Halfwit Ave.',
71 | address2: null,
72 | city: 'New London',
73 | stateProvince: 'Melon',
74 | postalCode: '02001-0000',
75 | entryPoints: 2,
76 | floors: 2,
77 | hasBasement: false,
78 | rooftopAccess: true
79 | },
80 | defenses: [ 1, 2, 3, 5, 8 ]
81 | },
82 | {
83 | id: 5,
84 | guildId: 2,
85 | name: 'Government Center',
86 | headChapter: false,
87 | founded: '4/7/1955',
88 | email: 'politikill@citywire.com',
89 | location: {
90 | address: '28810 North Alley',
91 | address2: '2nd Floor',
92 | city: 'New London',
93 | stateProvince: 'Alcott',
94 | postalCode: '01001-0450',
95 | entryPoints: 1,
96 | floors: 1,
97 | hasBasement: false,
98 | rooftopAccess: false
99 | },
100 | defenses: [ 3, 4, 8 ]
101 | },
102 | {
103 | id: 6,
104 | guildId: 2,
105 | name: 'South End',
106 | headChapter: false,
107 | founded: '4/17/1975',
108 | email: 'takemout@citywire.com',
109 | location: {
110 | address: '24 North Cyon Str',
111 | address2: null,
112 | city: 'Kent',
113 | stateProvince: 'Dover',
114 | postalCode: '02001-0000',
115 | entryPoints: 1,
116 | floors: 3,
117 | hasBasement: false,
118 | rooftopAccess: true
119 | },
120 | defenses: [ 2, 3, 5 ]
121 | },
122 | {
123 | id: 7,
124 | guildId: 2,
125 | name: 'Underworld',
126 | headChapter: false,
127 | founded: '3/21/1989',
128 | email: 'deathtodeath@citywire.com',
129 | location: {
130 | address: '100 Brimstone Court',
131 | address2: null,
132 | city: 'Lower London',
133 | stateProvince: 'Sulfur',
134 | postalCode: '66602-9000',
135 | entryPoints: 4,
136 | floors: 1,
137 | hasBasement: true,
138 | rooftopAccess: false
139 | },
140 | defenses: [ 1, 5, 8 ]
141 | },
142 | {
143 | id: 8,
144 | guildId: 3,
145 | name: 'Financial District',
146 | headChapter: true,
147 | founded: '12/12/2001',
148 | email: 'robinhoods@citywire.com',
149 | location: {
150 | address: '101 Banker Lane',
151 | address2: null,
152 | city: 'New London',
153 | stateProvince: 'Alcott',
154 | postalCode: '01001-0040',
155 | entryPoints: 2,
156 | floors: 4,
157 | hasBasement: true,
158 | rooftopAccess: true
159 | },
160 | defenses: [ 1, 6 ]
161 | },
162 | {
163 | id: 9,
164 | guildId: 3,
165 | name: 'West Kent',
166 | headChapter: false,
167 | founded: '1/3/1965',
168 | email: 'kentcleptos@citywire.com',
169 | location: {
170 | address: '5466 Closing Court',
171 | address2: null,
172 | city: 'Kent',
173 | stateProvince: 'Dover',
174 | postalCode: '03001-3330',
175 | entryPoints: 3,
176 | floors: 2,
177 | hasBasement: false,
178 | rooftopAccess: true
179 | },
180 | defenses: [ 7, 8 ]
181 | },
182 | {
183 | id: 10,
184 | guildId: 3,
185 | name: 'North District',
186 | headChapter: false,
187 | founded: '8/25/2003',
188 | email: 'northerncrooks@citywire.com',
189 | location: {
190 | address: '234 Spruce Street',
191 | address2: null,
192 | city: 'New London',
193 | stateProvince: 'Melon',
194 | postalCode: '02001-4401',
195 | entryPoints: 2,
196 | floors: 2,
197 | hasBasement: false,
198 | rooftopAccess: true
199 | },
200 | defenses: [ 1, 2, 3, 4 ]
201 | },
202 | ];
203 |
204 | export { chapters }
205 |
206 |
--------------------------------------------------------------------------------
/src/app/db/defense-measures.ts:
--------------------------------------------------------------------------------
1 | let defenseMeasures = [
2 | { id: 1, label: 'Security cameras' },
3 | { id: 2, label: 'Window alarms' },
4 | { id: 3, label: 'Guard wolves' },
5 | { id: 4, label: 'Booby traps' },
6 | { id: 5, label: 'Safe room' },
7 | { id: 6, label: 'Professional guards' },
8 | { id: 7, label: 'Volunteer guards' },
9 | { id: 8, label: 'Sprinkler system' }
10 | ]
11 |
12 | export { defenseMeasures }
13 |
--------------------------------------------------------------------------------
/src/app/db/guilds.ts:
--------------------------------------------------------------------------------
1 | let guilds = [
2 | {
3 | id: 1,
4 | name: "Blacksmiths",
5 | incorporationYear: 1878,
6 | email: 'bsmithsinc@citywire.com',
7 | expenses: 10000,
8 | revenue: 15000,
9 | },
10 | {
11 | id: 2,
12 | name: "Assassins",
13 | incorporationYear: 1713,
14 | email: 'killercorp@gcitywire.com',
15 | expenses: 22000,
16 | revenue: 18500
17 | },
18 | {
19 | id: 3,
20 | name: "Thieves",
21 | incorporationYear: 1931,
22 | email: 'moneyshift@citywire.com',
23 | expenses: 7000,
24 | revenue: 14750
25 | }
26 | ];
27 |
28 | export { guilds }
29 |
--------------------------------------------------------------------------------
/src/app/db/people.ts:
--------------------------------------------------------------------------------
1 | let people = [
2 | {
3 | firstName: 'Fenrik',
4 | middleName: null,
5 | lastName: 'Thane',
6 | phone: '555-324-8987',
7 | altPhone: null,
8 | email: 'fthane@citywire.com',
9 | isUndead: false,
10 | location: {
11 | address: '101 Center Lane',
12 | address2: null,
13 | city: 'New London',
14 | stateProvince: 'Alcott',
15 | postalCode: '01001-0000'
16 | }
17 | },
18 | {
19 | firstName: 'Jalak',
20 | middleName: 'R.',
21 | lastName: 'Crellen',
22 | phone: '555-676-5453',
23 | altPhone: null,
24 | email: 'jcrell@citywire.com',
25 | isUndead: false,
26 | location: {
27 | address: '798 Walker Lane',
28 | address2: null,
29 | city: 'New London',
30 | stateProvince: 'Melon',
31 | postalCode: '02001-0000'
32 | }
33 | },
34 | {
35 | firstName: 'Raiken',
36 | middleName: 'Crushing',
37 | lastName: 'Bellanous',
38 | phone: '555-998-8971',
39 | altPhone: null,
40 | email: 'rbellanous@netforge.org',
41 | isUndead: false,
42 | location: {
43 | address: '13 Westerland Way',
44 | address2: null,
45 | city: 'New London',
46 | stateProvince: 'Alcott',
47 | postalCode: '01001-0400'
48 | }
49 | },
50 | {
51 | firstName: 'Roland',
52 | middleName: null,
53 | lastName: 'Cagznice',
54 | phone: '554-766-0987',
55 | altPhone: null,
56 | email: 'rcog@netforge.org',
57 | isUndead: false,
58 | location: {
59 | address: '132 Belamy Court',
60 | address2: null,
61 | city: 'New London',
62 | stateProvince: 'Melon',
63 | postalCode: '02001-0050'
64 | }
65 | },
66 | {
67 | firstName: 'Jerriack',
68 | middleName: 'Carlos',
69 | lastName: 'Selfane',
70 | phone: '555-087-1551',
71 | altPhone: null,
72 | email: 'jselfane@citywire.com',
73 | isUndead: false,
74 | location: {
75 | address: '89 Welling Gardens',
76 | address2: 'Apt. 10',
77 | city: 'New London',
78 | stateProvince: 'Alcott',
79 | postalCode: '01001-0006'
80 | }
81 | },
82 | {
83 | firstName: 'Bablet',
84 | middleName: null,
85 | lastName: 'Crosswind',
86 | phone: '555-544-0312',
87 | altPhone: '666-111-0981',
88 | email: 'bcross@departed.com',
89 | isUndead: true,
90 | location: {
91 | address: '23 Hades Ave.',
92 | address2: null,
93 | city: 'Lower London',
94 | stateProvince: 'Underland',
95 | postalCode: '6661-2000'
96 | }
97 | },
98 | {
99 | firstName: 'Lanfar',
100 | middleName: 'Cole',
101 | lastName: 'Terrios',
102 | phone: '555-112-8725',
103 | altPhone: null,
104 | email: 'lterrios@citywire.com',
105 | isUndead: false,
106 | location: {
107 | address: '120 Windfar Lane',
108 | address2: null,
109 | city: 'New London',
110 | stateProvince: 'Melon',
111 | postalCode: '02001-0000'
112 | }
113 | },
114 | {
115 | firstName: 'Barker',
116 | middleName: null,
117 | lastName: 'Rafina',
118 | phone: '555-763-7972',
119 | altPhone: null,
120 | email: 'brafina@netforge.org',
121 | isUndead: false,
122 | location: {
123 | address: '125 Bastion Blvd.',
124 | address2: null,
125 | city: 'New London',
126 | stateProvince: 'Alcott',
127 | postalCode: '01001-0600'
128 | }
129 | },
130 | {
131 | firstName: 'Kalassian',
132 | middleName: null,
133 | lastName: 'Tremane',
134 | phone: '555-144-7982',
135 | altPhone: null,
136 | email: 'ktremane@citywire.com',
137 | isUndead: false,
138 | location: {
139 | address: '898 North Ave',
140 | address2: null,
141 | city: 'New London',
142 | stateProvince: 'Melon',
143 | postalCode: '02001-1500'
144 | }
145 | },
146 | {
147 | firstName: 'Rafel',
148 | middleName: 'Callas',
149 | lastName: 'Mellofon',
150 | phone: '666-876-8891',
151 | altPhone: null,
152 | email: 'rmello@departed.com',
153 | isUndead: true,
154 | location: {
155 | address: '12 Everlasting Lane',
156 | address2: null,
157 | city: 'Lower London',
158 | stateProvince: 'Underland',
159 | postalCode: '66601-0000'
160 | }
161 | },
162 | {
163 | firstName: 'Gregor',
164 | middleName: null,
165 | lastName: 'Sandoval',
166 | phone: '555-897-0098',
167 | altPhone: null,
168 | email: 'gsando@citywire.com',
169 | isUndead: false,
170 | location: {
171 | address: '12 Southwest Lane',
172 | address2: null,
173 | city: 'New London',
174 | stateProvince: 'Alcott',
175 | postalCode: '01001-0050'
176 | }
177 | },
178 | {
179 | firstName: 'Braile',
180 | middleName: null,
181 | lastName: 'Snowden',
182 | phone: '555-087-7787',
183 | altPhone: null,
184 | email: 'bsnowden@netforge.org',
185 | isUndead: false,
186 | location: {
187 | address: '22 Bombshell Street',
188 | address2: null,
189 | city: 'New London',
190 | stateProvince: 'Rising',
191 | postalCode: '03001-0400'
192 | }
193 | },
194 | {
195 | firstName: 'Jello',
196 | middleName: 'Chunky',
197 | lastName: 'Wellspring',
198 | phone: '555-877-6677',
199 | altPhone: null,
200 | email: 'jelloc@netforge.org',
201 | isUndead: false,
202 | location: {
203 | address: '102 Wellstone Street',
204 | address2: null,
205 | city: 'New London',
206 | stateProvince: 'Alcott',
207 | postalCode: '01001-0500'
208 | }
209 | },
210 | {
211 | firstName: 'Laster',
212 | middleName: null,
213 | lastName: 'Kenton',
214 | phone: '555-812-6521',
215 | altPhone: null,
216 | email: 'lkent@citywire.com',
217 | isUndead: false,
218 | location: {
219 | address: '74 Brighton Ave.',
220 | address2: null,
221 | city: 'Kent',
222 | stateProvince: 'Dover',
223 | postalCode: '22001-0000'
224 | }
225 | },
226 | {
227 | firstName: 'Quell',
228 | middleName: 'Bastion',
229 | lastName: 'Pollak',
230 | phone: '555-343-1100',
231 | altPhone: null,
232 | email: 'quall@netforge.org',
233 | isUndead: false,
234 | location: {
235 | address: '11290 Central Ave',
236 | address2: null,
237 | city: 'New London',
238 | stateProvince: 'Rising',
239 | postalCode: '03001-0200'
240 | }
241 | },
242 | {
243 | firstName: 'Aldus',
244 | middleName: null,
245 | lastName: 'Renegade',
246 | phone: '555-019-8165',
247 | altPhone: null,
248 | email: 'arenegate@citywire.com',
249 | isUndead: false,
250 | location: {
251 | address: '20 Clown Street',
252 | address2: null,
253 | city: 'Kent',
254 | stateProvince: 'Dover',
255 | postalCode: '22001-0300'
256 | }
257 | },
258 | {
259 | firstName: 'Danton',
260 | middleName: null,
261 | lastName: 'Profane',
262 | phone: '555-655-8791',
263 | altPhone: null,
264 | email: 'dprofane@departed.com',
265 | isUndead: true,
266 | location: {
267 | address: '1200 Hellfire Way',
268 | address2: null,
269 | city: 'Lower London',
270 | stateProvince: 'Underland',
271 | postalCode: '66601-0400'
272 | }
273 | },
274 | {
275 | firstName: 'Montage',
276 | middleName: 'Caron',
277 | lastName: 'Zaire',
278 | phone: '555-898-0032',
279 | altPhone: null,
280 | email: 'mzaire@citywire.com',
281 | isUndead: false,
282 | location: {
283 | address: '43 Frankton Ave.',
284 | address2: null,
285 | city: 'New London',
286 | stateProvince: 'Melon',
287 | postalCode: '02001-1300'
288 | }
289 | },
290 | {
291 | firstName: 'Blaze',
292 | middleName: null,
293 | lastName: 'Canton',
294 | phone: '555-003-9901',
295 | altPhone: null,
296 | email: 'bcanton@citywire.com',
297 | isUndead: false,
298 | location: {
299 | address: '32 Eleventh Street',
300 | address2: null,
301 | city: 'New London',
302 | stateProvince: 'Alcott',
303 | postalCode: '01001-4400'
304 | }
305 | },
306 | {
307 | firstName: 'Amherst',
308 | middleName: null,
309 | lastName: 'Worthington',
310 | phone: '666-901-9090',
311 | altPhone: null,
312 | email: 'aworth@departed.com',
313 | isUndead: true,
314 | location: {
315 | address: '1001 Brimstone Court',
316 | address2: null,
317 | city: 'Lower London',
318 | stateProvince: 'Sulfur',
319 | postalCode: '66602-9000'
320 | }
321 | },
322 | {
323 | firstName: 'Pellas',
324 | middleName: null,
325 | lastName: 'Grimwoire',
326 | phone: '555-101-8801',
327 | altPhone: null,
328 | email: 'pgrim@netforge.org',
329 | isUndead: false,
330 | location: {
331 | address: '29910 North Alley',
332 | address2: null,
333 | city: 'New London',
334 | stateProvince: 'Alcott',
335 | postalCode: '01001-0450'
336 | }
337 | },
338 | {
339 | firstName: 'Cooland',
340 | middleName: null,
341 | lastName: 'Cassan',
342 | phone: '555-897-6575',
343 | altPhone: null,
344 | email: 'ccass@netforge.com',
345 | isUndead: false,
346 | location: {
347 | address: '144 Western Sun Street',
348 | address2: null,
349 | city: 'New London',
350 | stateProvince: 'Melon',
351 | postalCode: '02001-0000'
352 | }
353 | },
354 | {
355 | firstName: 'Davin',
356 | middleName: null,
357 | lastName: 'Lannis',
358 | phone: '554-897-7919',
359 | altPhone: null,
360 | email: 'dlannis@citywire.com',
361 | isUndead: false,
362 | location: {
363 | address: '112 Wonder Lane',
364 | address2: null,
365 | city: 'Kent',
366 | stateProvince: 'Dover',
367 | postalCode: '02001-0000'
368 | }
369 | },
370 | {
371 | firstName: 'Egon',
372 | middleName: 'Fullbring',
373 | lastName: 'Spandex',
374 | phone: '555-665-8971',
375 | altPhone: null,
376 | email: 'egons@netforge.org',
377 | isUndead: false,
378 | location: {
379 | address: '9879 Halfwit Ave.',
380 | address2: null,
381 | city: 'New London',
382 | stateProvince: 'Melon',
383 | postalCode: '02001-0000'
384 | }
385 | },
386 | {
387 | firstName: 'Gator',
388 | middleName: null,
389 | lastName: 'Opperand',
390 | phone: '555-323-7212',
391 | altPhone: null,
392 | email: 'goppe@netforge.org',
393 | isUndead: false,
394 | location: {
395 | address: '898 Eastern Circle',
396 | address2: null,
397 | city: 'Kent',
398 | stateProvince: 'Dover',
399 | postalCode: '02001-0000'
400 | }
401 | },
402 | {
403 | firstName: 'Olso',
404 | middleName: null,
405 | lastName: 'Greymane',
406 | phone: '666-811-880',
407 | altPhone: '555-722-2211',
408 | email: 'gmane@departed.com',
409 | isUndead: true,
410 | location: {
411 | address: '977 Basement Blvd.',
412 | address2: null,
413 | city: 'Lower London',
414 | stateProvince: 'Underland',
415 | postalCode: '66601-3300'
416 | }
417 | },
418 | {
419 | firstName: 'Ellis',
420 | middleName: null,
421 | lastName: 'Wonders',
422 | phone: '555-341-4345',
423 | altPhone: null,
424 | email: 'ewonders@citywire.com',
425 | isUndead: false,
426 | location: {
427 | address: '1889 Promise Lane',
428 | address2: null,
429 | city: 'New London',
430 | stateProvince: 'Melon',
431 | postalCode: '02001-0876'
432 | }
433 | },
434 | {
435 | firstName: 'Yalla',
436 | middleName: 'Angel',
437 | lastName: 'Flowers',
438 | phone: '555-271-3151',
439 | altPhone: null,
440 | email: 'yangel@netforge.org',
441 | isUndead: false,
442 | location: {
443 | address: '8979 Plain Place',
444 | address2: null,
445 | city: 'New London',
446 | stateProvince: 'Alcott',
447 | postalCode: '01001-1100'
448 | }
449 | },
450 | {
451 | firstName: 'Chrysal',
452 | middleName: null,
453 | lastName: 'Erras',
454 | phone: '555-251-8711',
455 | altPhone: null,
456 | email: 'cerras@citywire.com',
457 | isUndead: false,
458 | location: {
459 | address: '766 Chanter Circle',
460 | address2: null,
461 | city: 'New London',
462 | stateProvince: 'Melon',
463 | postalCode: '02001-0053'
464 | }
465 | },
466 | {
467 | firstName: 'Ja',
468 | middleName: null,
469 | lastName: 'Xandos',
470 | phone: '666-322-8981',
471 | altPhone: null,
472 | email: 'jxan@departed.com',
473 | isUndead: true,
474 | location: {
475 | address: '8981 Wandering Way',
476 | address2: null,
477 | city: 'Lower London',
478 | stateProvince: 'Underland',
479 | postalCode: '66601-3200'
480 | }
481 | },
482 | {
483 | firstName: 'Wanda',
484 | middleName: null,
485 | lastName: 'Kessler',
486 | phone: '555-342-8913',
487 | altPhone: null,
488 | email: 'wk@netforge.org',
489 | isUndead: false,
490 | location: {
491 | address: '13223 Sesame Seed Street',
492 | address2: null,
493 | city: 'New London',
494 | stateProvince: 'Melon',
495 | postalCode: '02001-4401'
496 | }
497 | },
498 | {
499 | firstName: 'India',
500 | middleName: 'Shaman',
501 | lastName: 'Durkin',
502 | phone: '555-998-3322',
503 | altPhone: '554-223-8811',
504 | email: 'indiad@citywire.com',
505 | isUndead: false,
506 | location: {
507 | address: '8899 Circle Circle',
508 | address2: null,
509 | city: 'Kent',
510 | stateProvince: 'Dover',
511 | postalCode: '03001-3300'
512 | }
513 | },
514 | {
515 | firstName: 'Roo',
516 | middleName: null,
517 | lastName: 'Manga',
518 | phone: '555-667-8811',
519 | altPhone: null,
520 | email: 'rmanga@citywire.com',
521 | isUndead: false,
522 | location: {
523 | address: '1122 London Lane',
524 | address2: null,
525 | city: 'New London',
526 | stateProvince: 'Alcott',
527 | postalCode: '01001-0040'
528 | }
529 | }, //32
530 | {
531 | firstName: 'Roland',
532 | middleName: null,
533 | lastName: 'Cardigan',
534 | phone: '555-898-7716',
535 | altPhone: null,
536 | email: 'rcardigan@citywire.com',
537 | isUndead: false,
538 | location: {
539 | address: '12 Austin Lane',
540 | address2: null,
541 | city: 'New London',
542 | stateProvince: 'Alcott',
543 | postalCode: '01001-3400'
544 | }
545 | }, //33
546 | {
547 | firstName: 'Pao',
548 | middleName: null,
549 | lastName: 'Renoir',
550 | phone: '555-7781-0801',
551 | altPhone: '554-121-8980',
552 | email: 'pren@netforge.org',
553 | isUndead: false,
554 | location: {
555 | address: '1344 Prime Court',
556 | address2: null,
557 | city: 'New London',
558 | stateProvince: 'Melon',
559 | postalCode: '02001-1513'
560 | }
561 | },
562 | {
563 | firstName: 'Blitz',
564 | middleName: 'Connor',
565 | lastName: 'Macque',
566 | phone: '555-787-1234',
567 | altPhone: null,
568 | email: 'bmacque@citywire.com',
569 | isUndead: false,
570 | location: {
571 | address: '897 Baroque Avenue',
572 | address2: null,
573 | city: 'New London',
574 | stateProvince: 'Alcott',
575 | postalCode: '01001-0013'
576 | }
577 | },
578 | {
579 | firstName: 'Soldack',
580 | middleName: null,
581 | lastName: 'Trevon',
582 | phone: '555-781-8871',
583 | altPhone: null,
584 | email: 'solty@netforge.org',
585 | isUndead: false,
586 | location: {
587 | address: '23 Saffron Alley',
588 | address2: null,
589 | city: 'New London',
590 | stateProvince: 'Alcott',
591 | postalCode: '01001-0000'
592 | }
593 | },
594 | {
595 | firstName: 'Lavion',
596 | middleName: null,
597 | lastName: 'Ballastar',
598 | phone: '555-124-7132',
599 | altPhone: null,
600 | email: 'lballastar@citywire.org',
601 | isUndead: false,
602 | location: {
603 | address: '33 Willow Street',
604 | address2: null,
605 | city: 'New London',
606 | stateProvince: 'Melon',
607 | postalCode: '02031-1100'
608 | }
609 | },
610 | {
611 | firstName: 'Allari',
612 | middleName: 'Semony',
613 | lastName: 'Grulen',
614 | phone: '555-911-8123',
615 | altPhone: null,
616 | email: 'allsemy@netforge.org',
617 | isUndead: false,
618 | location: {
619 | address: '1726 Wanderlust Drive',
620 | address2: 'Apt. 12B',
621 | city: 'New London',
622 | stateProvince: 'Alcott',
623 | postalCode: '01001-0200'
624 | }
625 | },
626 | {
627 | firstName: 'Cadence',
628 | middleName: null,
629 | lastName: 'Operand',
630 | phone: '555-131-8546',
631 | altPhone: null,
632 | email: 'coperand@citywire.com',
633 | isUndead: false,
634 | location: {
635 | address: '715 Davis Road',
636 | address2: null,
637 | city: 'New London',
638 | stateProvince: 'Melon',
639 | postalCode: '02001-0340'
640 | }
641 | },
642 | {
643 | firstName: 'Babilar',
644 | middleName: null,
645 | lastName: 'Mancho',
646 | phone: '555-545-1791',
647 | altPhone: null,
648 | email: 'bman@netforge.org',
649 | isUndead: false,
650 | location: {
651 | address: '2212 Wrangler Way',
652 | address2: null,
653 | city: 'New London',
654 | stateProvince: 'Alcott',
655 | postalCode: '01011-4000'
656 | }
657 | }
658 | ];
659 |
660 | export { people };
661 |
--------------------------------------------------------------------------------
/src/app/db/version.ts:
--------------------------------------------------------------------------------
1 | let version = [
2 | {id: 1, name: '0.0.7'}
3 | ];
4 |
5 | export { version }
6 |
--------------------------------------------------------------------------------
/src/app/domain/chapter-location.ts:
--------------------------------------------------------------------------------
1 | import { Location } from './location';
2 |
3 | export class ChapterLocation extends Location {
4 | entryPoints: number = null;
5 | floors: number = null;
6 | hasBasement: boolean = false;
7 | rooftopAccess: boolean = false;
8 |
9 | constructor( chapterLocationData?: any ) {
10 | super( chapterLocationData );
11 | if( chapterLocationData ) {
12 | let props = Object.keys( this );
13 | for( let p in props ) {
14 | if( chapterLocationData[ props[p] ] ) {
15 | this[ props[p] ] = chapterLocationData[ props[p] ];
16 | }
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/domain/chapter-member.ts:
--------------------------------------------------------------------------------
1 | import { Person } from './person';
2 |
3 |
4 | export class ChapterMember extends Person {
5 | id: number = null;
6 | chapterId: number = null;
7 | startDate: Date = null;
8 | endDate: Date = null;
9 | isActive: boolean = false;
10 |
11 | constructor( memberData?: any ) {
12 | super( memberData );
13 | if( memberData ) {
14 | let props = Object.keys( this );
15 | for( let p in props ) {
16 | if( memberData[ props[p] ] ) {
17 | this[ props[p] ] = memberData[ props[p] ];
18 | }
19 | }
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/app/domain/chapter.ts:
--------------------------------------------------------------------------------
1 | import { ChapterLocation } from './chapter-location';
2 | import { Validatable, PropertyValidations } from '../vadacl/interfaces';
3 |
4 | export class Chapter implements Validatable{
5 | id: number = null;
6 | guildId: number = null;
7 | name: string = null;
8 | location: ChapterLocation = new ChapterLocation();
9 | headChapter: boolean = false;
10 | founded: Date = null;
11 | defenses: Number[] = [];
12 |
13 | validations: { [ index: string ] : PropertyValidations } = {
14 | name: {
15 | required: { message: 'The name is required.'},
16 | pattern: { pattern: '[a-zA-Z]+', message: 'The chapter name cannot contain numbers or spaces.' },
17 | },
18 | guild: { required: { } },
19 | headChapter: { required: { message: 'Must denote if this chapter is the head chapter.' } },
20 | };
21 |
22 | constructor( chapterData?: any ) {
23 |
24 | if( chapterData ) {
25 | let props = Object.keys( this );
26 | for( let p in props ) {
27 | if( chapterData[ props[p] ] ) {
28 | if( props[p] == 'location' ) {
29 | this.location = new ChapterLocation( chapterData.location )
30 | } else {
31 | this[ props[p] ] = chapterData[ props[p] ];
32 | }
33 | }
34 | }
35 |
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/app/domain/customer.ts:
--------------------------------------------------------------------------------
1 | import { Person } from './person';
2 |
3 | export class Customer extends Person {
4 | id: number = null;
5 | rating: number = null;
6 | isBanned: boolean = false;
7 |
8 | constructor( customerData?: any ) {
9 | super( customerData );
10 | if( customerData ) {
11 | let props = Object.keys( this );
12 | for( let p in props ) {
13 | if( customerData[ props[p] ] ) {
14 | this[ props[p] ] = customerData[ props[p] ];
15 | }
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/domain/guild.ts:
--------------------------------------------------------------------------------
1 | export class Guild {
2 | id: number = null;
3 | name: string = null;
4 | email: string = null;
5 | incorporationYear: number = null;
6 |
7 | expenses: number = 0;
8 | revenue: number = 0;
9 | profit: number = 0;
10 |
11 | constructor( guildData?: any ) {
12 | if( guildData ) {
13 | let props = Object.keys( this );
14 | for( let p in props ) {
15 | if( guildData[ props[p] ] ) {
16 | this[ props[p] ] = guildData[ props[p] ];
17 | }
18 | }
19 | this.profit = this.calculateProfit();
20 | }
21 | }
22 |
23 | calculateProfit() {
24 | return this.revenue - this.expenses;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/app/domain/http-response.ts:
--------------------------------------------------------------------------------
1 | export class HttpResponse {
2 | httpStatus: number;
3 | httpData: any;
4 |
5 | ok: boolean;
6 | data: any = {};
7 | errors: string[] = [];
8 |
9 | constructor( response?: any ) {
10 | this.httpStatus = response.status;
11 | this.httpData = response.data ? response.data : null;
12 | this.ok = response.ok ? response.ok : false; //ok is true for all 2xx codes
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/app/domain/location.ts:
--------------------------------------------------------------------------------
1 | export class Location {
2 | address: string = null;
3 | address2: string = null;
4 | city: string = null;
5 | stateProvince: string = null;
6 | postalCode: string = null;
7 |
8 | constructor( locationData?: any ) {
9 | if( locationData ) {
10 | let props = Object.keys( this );
11 | for( let p in props ) {
12 | if( locationData[ props[p] ] ) {
13 | this[ props[p] ] = locationData[ props[p] ];
14 | }
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/domain/person.ts:
--------------------------------------------------------------------------------
1 | import { Location } from './location';
2 |
3 | export class Person {
4 | firstName: string = null;
5 | middleName: string = null;
6 | lastName: string = null;
7 | phone: string = null;
8 | altPhone: string = null;
9 | email: string = null;
10 | isUndead: boolean = false;
11 | residence: Location = new Location();
12 |
13 | constructor( personData?: any ) {
14 | if( personData ) {
15 | let props = Object.keys( this );
16 | for( let p in props ) {
17 | if( personData[ props[p] ] ) {
18 | this[ props[p] ] = personData[ props[p] ];
19 | }
20 | }
21 | if( personData.location ) {
22 | this.residence = new Location( personData.location )
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/environment.ts:
--------------------------------------------------------------------------------
1 | // The file for the current environment will overwrite this one during build
2 | // Different environments can be found in config/environment.{dev|prod}.ts
3 | // The build system defaults to the dev environment
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
--------------------------------------------------------------------------------
/src/app/guild.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | import { Http, Headers } from '@angular/http';
4 |
5 | import { HttpResponse } from './domain/http-response';
6 | import { Guild } from './domain/guild';
7 |
8 | import 'rxjs/add/operator/toPromise';
9 |
10 | @Injectable()
11 | export class GuildService {
12 |
13 | private guildUrl = 'app/guilds';
14 |
15 | constructor( private http: Http ) { }
16 |
17 | getGuilds() {
18 | return this.http.get( this.guildUrl )
19 | .toPromise()
20 | .then( response => {
21 | let serviceResponse = new HttpResponse( response );
22 | serviceResponse.data = response.json().data;
23 | return serviceResponse;
24 | })
25 | .catch( error => {
26 | let serviceResponse = new HttpResponse( error );
27 | return serviceResponse;
28 | });
29 | }
30 |
31 | getGuild( id: number ) {
32 | let selectedGuildUrl = this.guildUrl + `/${id}`;
33 | return this.http.get( selectedGuildUrl )
34 | .toPromise()
35 | .then( response => {
36 | let serviceResponse = new HttpResponse( response );
37 | serviceResponse.data.guild = new Guild( response.json().data );
38 | return serviceResponse;
39 | })
40 | .catch( error => {
41 | let serviceResponse = new HttpResponse( error );
42 | //In case the user has a URL bookmarked that points to a deleted guild
43 | if( error.status == 404 ) {
44 | serviceResponse.errors.push( `Selected guild (${id}) does not exist.` );
45 | } else {
46 | serviceResponse.errors.push( 'An error occurred. Please try again.' );
47 | }
48 | return serviceResponse;
49 | });
50 | }
51 |
52 | saveGuild( guild: Guild ) {
53 | if( guild.id ) {
54 | return this.updateGuild( guild );
55 | } else {
56 | return this.createGuild( guild );
57 | }
58 | }
59 |
60 | private createGuild( guild: Guild ) {
61 | return this.http
62 | .post( this.guildUrl, JSON.stringify( guild ), { headers: this.getHeaders() })
63 | .toPromise()
64 | .then( response => {
65 | let serviceResponse = new HttpResponse( response );
66 | serviceResponse.data.guild = new Guild( response.json().data );
67 | return serviceResponse;
68 | })
69 | .catch( error => {
70 | let serviceResponse = new HttpResponse( error );
71 | if( !serviceResponse.ok ) {
72 | serviceResponse.errors.push( 'An error occurred. Please try again.' );
73 | }
74 | return serviceResponse;
75 | });
76 | }
77 |
78 |
79 | private updateGuild( guild: Guild ) {
80 | let url = `${this.guildUrl}/${guild.id}`;
81 |
82 | return this.http
83 | .put( url, JSON.stringify( guild ), { headers: this.getHeaders() } )
84 | .toPromise()
85 | .then( response => {
86 | let serviceResponse = new HttpResponse( response );
87 | serviceResponse.data.guild = guild;
88 | return serviceResponse;
89 | })
90 | .catch( error => {
91 | let serviceResponse = new HttpResponse( error );
92 | if( !serviceResponse.ok ) {
93 | serviceResponse.errors.push( 'An error occurred. Please try again.' );
94 | }
95 | return serviceResponse;
96 | });
97 | }
98 |
99 | private getHeaders(): Headers {
100 | let headers = new Headers();
101 | headers.append( 'Content-Type', 'application/json' );
102 | return headers;
103 | }
104 |
105 | private handleError(error: any) {
106 | console.error('An error occurred', error);
107 | return Promise.reject(error.message || error);
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/src/app/guilds-detail/guilds-detail.component.css:
--------------------------------------------------------------------------------
1 | div.alert-danger {
2 | padding: 8px;
3 | }
4 |
5 | div.alert-danger ul {
6 | list-style-type: none;
7 | margin-left: 0px;
8 | padding-left: 0px;
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/guilds-detail/guilds-detail.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Loading...
4 |
5 | {{(guild.id ? 'Edit' : 'Add' )}} {{(guild.name ? guild.name : 'Guild')}}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
Error(s):
14 |
15 | -
16 | {{error}}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/src/app/guilds-detail/guilds-detail.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { ActivatedRoute, Params } from '@angular/router';
3 | import { GuildService } from '../guild.service';
4 | import { Guild } from '../domain/guild';
5 | import { HttpResponse } from '../domain/http-response';
6 |
7 | @Component({
8 | moduleId: module.id,
9 | selector: 'app-guilds-detail',
10 | templateUrl: 'guilds-detail.component.html',
11 | styleUrls: ['guilds-detail.component.css']
12 | })
13 | export class GuildsDetailComponent implements OnInit {
14 |
15 | guild: Guild;
16 | status: any = {
17 | name: { errors: []},
18 | incorporationYear: { errors: [] },
19 | email: { errors: [] }
20 | };
21 |
22 | serviceErrors: string[];
23 |
24 | constructor(
25 | private guildService: GuildService,
26 | private route: ActivatedRoute
27 | ) { }
28 |
29 | ngOnInit() {
30 | this.route.params.forEach( (params: Params ) => {
31 | let id = params['id'] ? +params['id'] : null ; //converts param string to number
32 | if( id ) {
33 | this.guildService.getGuild( id )
34 | .then( ( serviceResponse: HttpResponse ) => {
35 | if( serviceResponse.ok ) {
36 | this.guild = serviceResponse.data.guild;
37 | } else {
38 | this.serviceErrors = serviceResponse.errors;
39 | }
40 | });
41 | } else {
42 | this.guild = new Guild();
43 | }
44 | })
45 | }
46 |
47 | checkFix( propertyName: string ) {
48 | if( this.status[ propertyName ].errors.length > 0 ) {
49 | this.checkValidity( propertyName );
50 | }
51 | }
52 |
53 | isFormInvalid() {
54 | let formInvalid = false;
55 | for( let property in this.status ) {
56 | if( this.status[ property ].errors.length > 0 ) {
57 | formInvalid = true;
58 | break;
59 | }
60 | }
61 | return formInvalid;
62 | }
63 |
64 | checkValidity( propertyName: string ) {
65 |
66 | let yearRegEx = /[1-2]\d{3}$/;
67 | let emailRegEx = /[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$/;
68 |
69 | switch( propertyName ) {
70 | case 'name':
71 | this.status.name.errors = [];
72 | if( !this.guild[ propertyName ] ) {
73 | this.status.name.errors.push( 'The name field is required.' );
74 | break;
75 | }
76 | if( this.guild[ propertyName ].length < 5 ) {
77 | this.status.name.errors.push( 'The guild name is too short.' );
78 | }
79 | break;
80 | case 'incorporationYear':
81 | this.status.incorporationYear.errors = [];
82 | if( !this.guild[ propertyName ] ) {
83 | this.status.incorporationYear.errors.push( 'The year of incorporation is required.' );
84 | break;
85 | }
86 | if( isNaN( +this.guild[ propertyName ] ) ) {
87 | this.status.incorporationYear.errors.push( 'The incorporation year must be a number.' );
88 | } else {
89 | if( !yearRegEx.test( this.guild[ propertyName ] ) ) {
90 | this.status.incorporationYear.errors.push( 'The incorporation year must a valid 4-digit year (1xxx or 2xxx).' );
91 | }
92 | }
93 | break;
94 | case 'email':
95 | this.status.email.errors = [];
96 | if( !emailRegEx.test( this.guild[ propertyName ] ) ) {
97 | this.status.email.errors.push( 'Please enter a valid email address.' );
98 | }
99 | break;
100 | }
101 |
102 | }
103 |
104 | goBack() {
105 | window.history.back();
106 | }
107 |
108 | submitForm() {
109 | this.serviceErrors = null;
110 | for( let property in this.status ) {
111 | this.checkValidity( property );
112 | }
113 | if( !this.isFormInvalid() ) {
114 | this.guildService
115 | .saveGuild( this.guild )
116 | .then( ( serviceResponse: HttpResponse ) => {
117 | if( serviceResponse.ok ) {
118 | this.guild = serviceResponse.data.guild;
119 | window.history.back();
120 | } else {
121 | this.serviceErrors = serviceResponse.errors;
122 | }
123 | });
124 | }
125 | }
126 |
127 | clearForm() {
128 | this.guild.name = null;
129 | this.guild.incorporationYear = null ;
130 | this.guild.email = null;
131 | for( let property in this.status ) {
132 | this.status[ property ].errors = [];
133 | }
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/src/app/guilds-detail/index.ts:
--------------------------------------------------------------------------------
1 | export * from './guilds-detail.component';
2 |
--------------------------------------------------------------------------------
/src/app/guilds-master/guilds-master.component.css:
--------------------------------------------------------------------------------
1 | .grBrand {
2 | font-family: "American Typewriter";
3 | font-weight: bold;
4 | }
5 |
--------------------------------------------------------------------------------
/src/app/guilds-master/guilds-master.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Guilds
4 |
Add guild
5 |
6 |
7 |
8 | ID |
9 | Name |
10 | Incorporated |
11 | Expenses |
12 | Revenue |
13 | Profit |
14 | Email |
15 | Actions |
16 |
17 |
18 |
19 |
20 | {{guild.id}} |
21 | {{guild.name}} |
22 | {{guild.incorporationYear}} |
23 | {{guild.expenses | currency:USD:true:'.2-2' }} |
24 | {{guild.revenue | currency:USD:true:'.2-2' }} |
25 | {{guild.profit | currency:USD:true:'.2-2'}} |
26 | {{guild.email}} |
27 | Edit |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/app/guilds-master/guilds-master.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { GuildService } from '../guild.service';
3 | import { Guild } from '../domain/guild';
4 | import { HttpResponse } from "../domain/http-response";
5 |
6 | @Component({
7 | moduleId: module.id,
8 | selector: 'app-guilds-master',
9 | templateUrl: 'guilds-master.component.html',
10 | styleUrls: ['guilds-master.component.css']
11 | })
12 | export class GuildsMasterComponent implements OnInit {
13 |
14 | guilds: Guild[] = [];
15 |
16 | constructor( private guildService: GuildService ) { }
17 |
18 | ngOnInit() {
19 | this.guildService.getGuilds().then( (httpResponse:HttpResponse) => {
20 | httpResponse.data.forEach( guild => {
21 | this.guilds.push( new Guild( guild ) )
22 | })
23 | } );
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/guilds-master/index.ts:
--------------------------------------------------------------------------------
1 | export * from './guilds-master.component';
2 |
--------------------------------------------------------------------------------
/src/app/home/home.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bcswartz/angular2-sandbox-guildrunner/c57c5ff1083b1b6ae5a1b6d5ae93adc9e547efa8/src/app/home/home.component.css
--------------------------------------------------------------------------------
/src/app/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 | This space for rent.
3 |
4 |
--------------------------------------------------------------------------------
/src/app/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | moduleId: module.id,
5 | selector: 'app-home',
6 | templateUrl: 'home.component.html',
7 | styleUrls: ['home.component.css']
8 | })
9 | export class HomeComponent {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/home/index.ts:
--------------------------------------------------------------------------------
1 | export * from './home.component';
2 |
--------------------------------------------------------------------------------
/src/app/in-memory-data.service.ts:
--------------------------------------------------------------------------------
1 | import { version } from './db/version';
2 | import { guilds } from './db/guilds';
3 | import { defenseMeasures } from './db/defense-measures';
4 | import { chapters } from './db/chapters';
5 | import { chapterMembers } from './db/chapter-members';
6 |
7 | export class InMemoryDataService {
8 | createDb() {
9 |
10 | return {
11 | version,
12 | guilds,
13 | defenseMeasures,
14 | chapters,
15 | chapterMembers
16 | };
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/index.ts:
--------------------------------------------------------------------------------
1 | export * from './environment';
2 | export * from './app.component';
3 |
--------------------------------------------------------------------------------
/src/app/main-navigation/index.ts:
--------------------------------------------------------------------------------
1 | export * from './main-navigation.component';
2 |
--------------------------------------------------------------------------------
/src/app/main-navigation/main-navigation.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bcswartz/angular2-sandbox-guildrunner/c57c5ff1083b1b6ae5a1b6d5ae93adc9e547efa8/src/app/main-navigation/main-navigation.component.css
--------------------------------------------------------------------------------
/src/app/main-navigation/main-navigation.component.html:
--------------------------------------------------------------------------------
1 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/app/main-navigation/main-navigation.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 |
3 | import { By } from '@angular/platform-browser';
4 | import { DebugElement } from '@angular/core';
5 | import { addProviders, async, inject } from '@angular/core/testing';
6 | import { MainNavigationComponent } from './main-navigation.component';
7 |
8 | describe('Component: MainNavigation', () => {
9 | it('should create an instance', () => {
10 | let component = new MainNavigationComponent();
11 | expect(component).toBeTruthy();
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/app/main-navigation/main-navigation.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | moduleId: module.id,
5 | selector: 'app-main-navigation',
6 | templateUrl: 'main-navigation.component.html',
7 | styleUrls: ['main-navigation.component.css']
8 | })
9 | export class MainNavigationComponent {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/member.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Http, Headers } from '@angular/http';
3 |
4 | import { HttpResponse } from './domain/http-response';
5 | import { ChapterMember } from './domain/chapter-member';
6 |
7 | import 'rxjs/add/operator/toPromise';
8 |
9 | @Injectable()
10 | export class MemberService {
11 |
12 | private memberUrl = 'app/chapterMembers';
13 |
14 | constructor( private http: Http ) { }
15 |
16 | getMembers() {
17 | return this.http.get( this.memberUrl )
18 | .toPromise()
19 | .then( response => {
20 | let serviceResponse = new HttpResponse( response );
21 | serviceResponse.data = response.json().data;
22 | return serviceResponse;
23 | })
24 | .catch( error => {
25 | let serviceResponse = new HttpResponse( error );
26 | return serviceResponse;
27 | });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/app/members-master/index.ts:
--------------------------------------------------------------------------------
1 | export * from './members-master.component';
2 |
--------------------------------------------------------------------------------
/src/app/members-master/members-master.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bcswartz/angular2-sandbox-guildrunner/c57c5ff1083b1b6ae5a1b6d5ae93adc9e547efa8/src/app/members-master/members-master.component.css
--------------------------------------------------------------------------------
/src/app/members-master/members-master.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Chapter Members
4 |
5 |
6 |
7 | ID |
8 | First Name |
9 | Last Name |
10 | Guild |
11 | Chapter |
12 | Active? |
13 |
14 |
15 |
16 |
17 | {{member.id}} |
18 | {{member.firstName}} |
19 | {{member.lastName}} |
20 | {{guilds[chapters[member.chapterId].guildId].name}} |
21 | {{chapters[member.chapterId].name}} |
22 | {{member.isActive ? 'Yes' : 'No'}} |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/app/members-master/members-master.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { MemberService } from '../member.service';
3 | import { ChapterService } from '../chapter.service';
4 | import { GuildService } from '../guild.service';
5 | import { ChapterMember } from '../domain/chapter-member';
6 | import { HttpResponse } from '../domain/http-response';
7 |
8 | @Component({
9 | moduleId: module.id,
10 | selector: 'app-members-master',
11 | templateUrl: 'members-master.component.html',
12 | styleUrls: ['members-master.component.css']
13 | })
14 | export class MembersMasterComponent implements OnInit {
15 |
16 | members: ChapterMember[] = [];
17 | chapters: any = {};
18 | guilds: any = {};
19 |
20 | constructor(
21 | private memberService: MemberService,
22 | private chapterService: ChapterService,
23 | private guildService: GuildService
24 | ) { }
25 |
26 | ngOnInit() {
27 |
28 | Promise.all( [
29 | this.memberService.getMembers(),
30 | this.chapterService.getChapters(),
31 | this.guildService.getGuilds()
32 | ]).then( (results:Promise[]) => {
33 | results[0]['data'].forEach( memberData => {
34 | this.members.push( new ChapterMember( memberData ) )
35 | });
36 | results[1]['data'].forEach( chapterData => {
37 | this.chapters[ chapterData.id ] = chapterData
38 | });
39 | results[2]['data'].forEach( guildData => {
40 | this.guilds[ guildData.id ] = guildData
41 | });
42 | });
43 |
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/app/sandbox/chapter-list/chapter-list.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bcswartz/angular2-sandbox-guildrunner/c57c5ff1083b1b6ae5a1b6d5ae93adc9e547efa8/src/app/sandbox/chapter-list/chapter-list.component.css
--------------------------------------------------------------------------------
/src/app/sandbox/chapter-list/chapter-list.component.html:
--------------------------------------------------------------------------------
1 | List of all chapters from data:
2 | {{chapterList}}
3 |
--------------------------------------------------------------------------------
/src/app/sandbox/chapter-list/chapter-list.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { SandboxService } from '../sandbox.service';
3 |
4 | @Component({
5 | moduleId: module.id,
6 | selector: 'app-chapter-list',
7 | templateUrl: 'chapter-list.component.html',
8 | styleUrls: ['chapter-list.component.css']
9 | })
10 | export class ChapterListComponent implements OnInit {
11 |
12 | chapterList;
13 |
14 | constructor( private sandboxService: SandboxService ) { }
15 |
16 | ngOnInit() {
17 | this.sandboxService.getChapters().then( chapters => this.chapterList = JSON.stringify( chapters, null, 2 ) );
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/sandbox/chapter-list/index.ts:
--------------------------------------------------------------------------------
1 | export * from './chapter-list.component';
2 |
--------------------------------------------------------------------------------
/src/app/sandbox/chapter-member-list/chapter-member-list.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bcswartz/angular2-sandbox-guildrunner/c57c5ff1083b1b6ae5a1b6d5ae93adc9e547efa8/src/app/sandbox/chapter-member-list/chapter-member-list.component.css
--------------------------------------------------------------------------------
/src/app/sandbox/chapter-member-list/chapter-member-list.component.html:
--------------------------------------------------------------------------------
1 | List of all chapter members from data:
2 | {{memberList}}
3 |
--------------------------------------------------------------------------------
/src/app/sandbox/chapter-member-list/chapter-member-list.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { SandboxService } from '../sandbox.service';
3 |
4 | @Component({
5 | moduleId: module.id,
6 | selector: 'app-chapter-member-list',
7 | templateUrl: 'chapter-member-list.component.html',
8 | styleUrls: ['chapter-member-list.component.css']
9 | })
10 | export class ChapterMemberListComponent implements OnInit {
11 |
12 | memberList;
13 |
14 | constructor( private sandboxService: SandboxService ) { }
15 |
16 | ngOnInit() {
17 | this.sandboxService.getChapterMembers().then( members => this.memberList = JSON.stringify( members, null, 2 ) );
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/sandbox/chapter-member-list/index.ts:
--------------------------------------------------------------------------------
1 | export * from './chapter-member-list.component';
2 |
--------------------------------------------------------------------------------
/src/app/sandbox/chapter-reactive-form/chapter-reactive-form.component.css:
--------------------------------------------------------------------------------
1 | div.alert-danger {
2 | padding: 8px;
3 | }
4 |
5 | ul {
6 | list-style-type: none;
7 | margin-left: 0px;
8 | padding-left: 0px;
9 | }
10 |
11 | .form-group {
12 | padding-bottom:7px;
13 | }
14 |
15 | .rawErrors {
16 | border: 1px solid #999;
17 | margin-top:6px;
18 | padding:4px;
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/sandbox/chapter-reactive-form/chapter-reactive-form.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Loading...
4 | Edit {{chapter.name}}
5 |
6 |
7 |
8 |
147 |
--------------------------------------------------------------------------------
/src/app/sandbox/chapter-reactive-form/chapter-reactive-form.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Chapter } from "../../domain/chapter";
3 | import { guilds } from "../../db/guilds";
4 | import { defenseMeasures } from "../../db/defense-measures";
5 |
6 | import { FormControl, FormGroup, FormArray, FormBuilder } from '@angular/forms';
7 | import { Vadacl } from "../../vadacl/vadacl";
8 |
9 | @Component({
10 | moduleId: module.id,
11 | selector: 'app-chapter-reactive-form',
12 | templateUrl: 'chapter-reactive-form.component.html',
13 | styleUrls: ['chapter-reactive-form.component.css']
14 | })
15 | export class ChapterReactiveFormComponent extends Vadacl implements OnInit {
16 |
17 | chapter: Chapter;
18 | defenseArray: any = [];
19 | guildArray: any = [];
20 | defenseBoxArray: FormArray;
21 |
22 | pageLoaded: boolean = false;
23 |
24 | form: FormGroup;
25 |
26 | constructor( private formBuilder: FormBuilder ) {
27 | super();
28 | }
29 |
30 | ngOnInit() {
31 | this.initializeData();
32 | this.buildForm();
33 | this.pageLoaded = true
34 | }
35 |
36 | buildForm() {
37 |
38 | //Create a custom Validator function for the defenses array
39 | function hasDefenses( formArray: FormArray) {
40 | let valid = false;
41 | for( let c in formArray.controls ) {
42 | if( formArray.controls[c].value == true ) { valid = true }
43 | }
44 | return valid == true ? null : { 'noDefenses': { 'noDefenses': true, 'message': 'Chapter must have at least one defense.' } }
45 | }
46 |
47 | //Construct and populate the defenses FormArray outside of the FormBuilder so we can populate it dynamically
48 | this.defenseBoxArray = new FormArray( [], hasDefenses );
49 | for( let d in this.defenseArray ) {
50 | this.defenseBoxArray.push( new FormControl(( this.chapter.defenses.indexOf( this.defenseArray[d].id ) > -1 )))
51 | }
52 |
53 | this.form = this.formBuilder.group( {
54 | 'name': [
55 | this.chapter.name,
56 | this.applyRules(
57 | this.chapter,
58 | 'name',
59 | { minLength: { minLength: 4, message: 'The name must be at least 4 characters.' } }
60 | )
61 | ],
62 | 'guild': [
63 | this.chapter.guildId,
64 | this.applyRules( this.chapter, 'guild' )
65 | ],
66 | 'headChapter': [
67 | this.chapter.headChapter,
68 | this.applyRules( this.chapter, 'headChapter' )
69 | ],
70 | 'defenses': this.defenseBoxArray
71 | } );
72 |
73 | }
74 |
75 | submitForm() {
76 | if( this.form.valid ) {
77 | this.chapter.name = this.form.value.name; //value is a key/value map
78 | this.chapter.guildId = +this.form.value.guild; //need this translated to number, hence +
79 | this.chapter.headChapter = this.form.value.headChapter === "true";
80 | this.chapter.defenses = [];
81 | for( let db in this.defenseBoxArray.controls ) {
82 | if( this.defenseBoxArray.controls[ db ].value == true ) {
83 | this.chapter.defenses.push( this.defenseArray[ db ].id )
84 | }
85 | }
86 | }
87 | }
88 |
89 | resetForm() {
90 | this.form.reset();
91 | }
92 |
93 | changeName() {
94 | this.changeControlValue( this.form.controls[ 'name' ], '999' );
95 | }
96 |
97 | initializeData() {
98 | this.chapter = new Chapter( {
99 | id: 99,
100 | guildId: 2,
101 | name: 'Lobbyists',
102 | headChapter: null,
103 | founded: new Date(),
104 | defenses: [ 1, 3 ],
105 | location: {
106 | address: '12 Main Street',
107 | city: 'New London',
108 | stateProvince: 'Arkin',
109 | postalCode: '89000-8901',
110 | entryPoints: 2,
111 | floors: 3,
112 | hasBasement: false,
113 | rooftopAccess: true
114 | }
115 | });
116 |
117 | guilds.forEach( guild => {
118 | this.guildArray.push( guild );
119 | });
120 |
121 | defenseMeasures.forEach( defense => {
122 | this.defenseArray.push( defense );
123 | });
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/src/app/sandbox/chapter-reactive-form/index.ts:
--------------------------------------------------------------------------------
1 | export * from './chapter-reactive-form.component';
2 |
--------------------------------------------------------------------------------
/src/app/sandbox/guild-list/guild-list.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bcswartz/angular2-sandbox-guildrunner/c57c5ff1083b1b6ae5a1b6d5ae93adc9e547efa8/src/app/sandbox/guild-list/guild-list.component.css
--------------------------------------------------------------------------------
/src/app/sandbox/guild-list/guild-list.component.html:
--------------------------------------------------------------------------------
1 | List of all guilds from data:
2 | {{guildList}}
3 |
4 |
--------------------------------------------------------------------------------
/src/app/sandbox/guild-list/guild-list.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { SandboxService } from '../sandbox.service';
3 |
4 | @Component({
5 | moduleId: module.id,
6 | selector: 'app-guild-list',
7 | templateUrl: 'guild-list.component.html',
8 | styleUrls: ['guild-list.component.css']
9 | })
10 | export class GuildListComponent implements OnInit {
11 |
12 | guildList;
13 |
14 | constructor( private sandboxService: SandboxService ) { }
15 |
16 | ngOnInit() {
17 | this.sandboxService.getGuilds().then( guilds => this.guildList = JSON.stringify( guilds, null, 2 ) );
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/sandbox/guild-list/index.ts:
--------------------------------------------------------------------------------
1 | export * from './guild-list.component';
2 |
--------------------------------------------------------------------------------
/src/app/sandbox/sandbox.module.ts:
--------------------------------------------------------------------------------
1 | //Modules and routing imports
2 | import { NgModule } from '@angular/core';
3 | import { BrowserModule } from '@angular/platform-browser';
4 | import { sandboxRouting } from './sandbox.routing'
5 | import { ReactiveFormsModule } from '@angular/forms';
6 |
7 | //Declarations (components, directives, and pipes)
8 | import { GuildListComponent } from "./guild-list/guild-list.component";
9 | import { ChapterMemberListComponent } from "./chapter-member-list/chapter-member-list.component";
10 | import { ChapterListComponent } from "./chapter-list/chapter-list.component";
11 | import { ChapterReactiveFormComponent } from "./chapter-reactive-form/chapter-reactive-form.component";
12 |
13 | //Providers (services
14 | import { SandboxService } from './sandbox.service';
15 |
16 |
17 | @NgModule({
18 | imports: [
19 | sandboxRouting,
20 | BrowserModule,
21 | ReactiveFormsModule
22 | ],
23 |
24 | declarations: [
25 | GuildListComponent,
26 | ChapterListComponent,
27 | ChapterMemberListComponent,
28 | ChapterReactiveFormComponent
29 | ],
30 |
31 | providers: [
32 | SandboxService
33 | ]
34 | })
35 |
36 | export class SandboxModule {}
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/app/sandbox/sandbox.routing.ts:
--------------------------------------------------------------------------------
1 | import { Routes, RouterModule } from '@angular/router';
2 |
3 | import { GuildListComponent} from "./guild-list/guild-list.component";
4 | import { ChapterListComponent } from "./chapter-list/chapter-list.component";
5 | import { ChapterMemberListComponent } from "./chapter-member-list/chapter-member-list.component";
6 | import { ChapterReactiveFormComponent } from "./chapter-reactive-form/chapter-reactive-form.component";
7 |
8 |
9 | const sandboxRoutes: Routes = [
10 | { path: 'sandbox-guildlist', component: GuildListComponent },
11 | { path: 'sandbox-chapterlist', component: ChapterListComponent },
12 | { path: 'sandbox-memberlist', component: ChapterMemberListComponent},
13 | { path: 'sandbox-reactivechapter', component: ChapterReactiveFormComponent }
14 | ];
15 |
16 | export const sandboxRouting = RouterModule.forChild( sandboxRoutes );
17 |
--------------------------------------------------------------------------------
/src/app/sandbox/sandbox.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Http } from '@angular/http';
3 |
4 | import 'rxjs/add/operator/toPromise';
5 |
6 | @Injectable()
7 | export class SandboxService {
8 |
9 | private guildUrl = 'app/guilds';
10 | private chapterUrl = 'app/chapters';
11 | private memberUrl = 'app/chapterMembers';
12 |
13 | constructor( private http: Http) {}
14 |
15 | /*
16 | So this gets the raw data (useful for diagnostic display), but if we type it to the object, then what?
17 | */
18 | getGuilds() {
19 | return this.http.get( this.guildUrl )
20 | .toPromise()
21 | .then(response => response.json().data )
22 | .catch(this.handleError);
23 | }
24 |
25 | getChapters() {
26 | return this.http.get( this.chapterUrl )
27 | .toPromise()
28 | .then(response => response.json().data )
29 | .catch(this.handleError);
30 | }
31 |
32 | getChapterMembers() {
33 | return this.http.get( this.memberUrl )
34 | .toPromise()
35 | .then(response => response.json().data )
36 | .catch(this.handleError);
37 | }
38 |
39 |
40 | private handleError(error: any) {
41 | console.error('An error occurred', error);
42 | return Promise.reject(error.message || error);
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/app/shared/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bcswartz/angular2-sandbox-guildrunner/c57c5ff1083b1b6ae5a1b6d5ae93adc9e547efa8/src/app/shared/index.ts
--------------------------------------------------------------------------------
/src/app/vadacl/interfaces.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Interfaces that can be used by the TypeScript compiler to enforce the proper key/value pairs for the validation options.
3 | */
4 | interface RequiredSettings {
5 | message ?: string
6 | }
7 |
8 | interface MinLengthSettings {
9 | minLength : number,
10 | message ?: string
11 | }
12 |
13 | interface MaxLengthSettings {
14 | maxLength : number,
15 | message ?: string
16 | }
17 |
18 | interface PatternSettings {
19 | pattern : string,
20 | message ?: string
21 | }
22 |
23 | interface WithinLengthSettings {
24 | minLength : number,
25 | maxLength : number,
26 | message ?: string
27 | }
28 |
29 | /*
30 | Interface for defining any and all known validation methods for a given domain object property
31 | */
32 | interface PropertyValidations {
33 | required ?: RequiredSettings,
34 | minLength ?: MinLengthSettings,
35 | maxLength ?: MaxLengthSettings,
36 | pattern ?: PatternSettings,
37 | withinLength ?: WithinLengthSettings
38 | }
39 |
40 | /*
41 | Interface that can be implemented by domain classes to enforce the presence of a "validations" property object literal.
42 | */
43 | interface Validatable {
44 | validations: { [ index: string ]: any };
45 | }
46 |
47 | export { PropertyValidations, Validatable }
48 |
49 |
--------------------------------------------------------------------------------
/src/app/vadacl/messages/messages-en.ts:
--------------------------------------------------------------------------------
1 | let Messages = {
2 |
3 | /* DEFAULT VALIDATOR ERROR MESSAGES */
4 | required: 'A value is required',
5 | minLength: 'The value is too short.',
6 | maxLength: 'The value is too long.',
7 | pattern: 'The value does not match the pattern.',
8 | withinLength: 'The value does not meet the size requirements',
9 |
10 | };
11 |
12 | export { Messages };
13 |
14 |
--------------------------------------------------------------------------------
/src/app/vadacl/patterns/patterns-en.ts:
--------------------------------------------------------------------------------
1 | let Patterns = {
2 | /*
3 | Define pattern strings used throughout your application (email, url, etc.).
4 | */
5 | };
6 |
7 | export { Patterns }
8 |
--------------------------------------------------------------------------------
/src/app/vadacl/vadacl.ts:
--------------------------------------------------------------------------------
1 |
2 | import { AbstractControl, FormGroup, FormArray } from '@angular/forms';
3 | import { ValidationMethods } from './validation-methods';
4 |
5 | /*
6 | The Vadacl class can be used as the superclass for a component that implements validation, or as an injected service.
7 | */
8 | export class Vadacl {
9 |
10 | applyRules(domainClass: any, propertyName: string, validationOverrides ?: any ) : any[] {
11 | let validators: any[] = [];
12 |
13 | //Collect the property validations defined in the domain class (if any).
14 | let propertyValidations = domainClass.validations ? ( domainClass.validations[ propertyName ] || {} ) : {};
15 |
16 | //Apply any validation overrides and additional validations.
17 | let mergedValidations = this.mergeValidations( propertyValidations, validationOverrides );
18 |
19 | for( let mv in mergedValidations ) {
20 | //Parse the argument values to apply to the validation method.
21 | let validatorArguments = this.getValidatorArguments( this.getMethodDeclaredArguments( ValidationMethods[ mv ] ), mergedValidations[ mv ] );
22 | /*
23 | Execute the validation method, which will return a validator that the Angular reactive form classes will
24 | trigger on a value change, and add that validator to the validators array.
25 | */
26 | validators.push( ValidationMethods[ mv ].apply( null, validatorArguments ) );
27 | }
28 |
29 | return validators;
30 | }
31 |
32 | /*
33 | Converts function to a string and then uses regular expressions to pull out the argument names. The argument names
34 | are denoted as object literal properties with values equal to their position in the list of arguments (the positions
35 | are needed to help place the validation method argument values in the correct order).
36 | */
37 | getMethodDeclaredArguments( fn: any ) : any {
38 | let methodDeclarationRegExp = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
39 | let argumentSplit = /,/;
40 | let stripCommentsRegExp = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
41 |
42 | let methodArguments = {};
43 | let argumentPosition = 0;
44 |
45 | let methodAsString = fn.toString().replace( stripCommentsRegExp, '' );
46 | let methodDeclaration = methodAsString.match( methodDeclarationRegExp );
47 | let argumentArray = methodDeclaration[ 1 ].split( argumentSplit );
48 |
49 | for( let argIndex in argumentArray) {
50 | methodArguments[ argumentArray[ argIndex ].replace(/\s/g,'') ] = argumentPosition++;
51 | }
52 |
53 | return methodArguments;
54 | }
55 |
56 | /*
57 | The arguments passed into the call to the validation method are organized in the proper order based on the order
58 | of the argument names in the validation method.
59 | */
60 | getValidatorArguments( argumentDeclarations: any, validatorArguments: any ) : any[] {
61 | let executionArguments: any[] = [];
62 | for( let arg in argumentDeclarations ) {
63 | executionArguments[ argumentDeclarations[ arg ] ] = validatorArguments[ arg ] || null;
64 | }
65 | return executionArguments;
66 | }
67 |
68 | /*
69 | Merges the previous validation settings with any new settings.
70 | */
71 | mergeValidations( baseValidations: any, overrideValidations: any ) : any {
72 | for( let validation in overrideValidations ) {
73 | if( baseValidations[ validation ] == undefined ) {
74 | baseValidations[ validation ] = overrideValidations[ validation ]
75 | } else {
76 | for( let setting in overrideValidations[ validation ] ) {
77 | baseValidations[ validation ][ setting ] = overrideValidations[ validation ][ setting ]
78 | }
79 | }
80 | }
81 | return baseValidations;
82 | }
83 |
84 | /*
85 | Determines whether the errors for the form element should be displayed. By default, returns false
86 | if the element is still marked as untouched.
87 | */
88 | showErrors( formElement: AbstractControl|FormGroup|FormArray, onlyAfterTouched: boolean = true ) : boolean {
89 | let elementActive = onlyAfterTouched ? formElement.touched : true;
90 | return ( formElement.dirty && formElement.invalid && elementActive ) ? true : false
91 | }
92 |
93 | /*
94 | Returns array of error messages. By default, error messages only returned when control is dirty.
95 | */
96 | getControlErrors( control: AbstractControl, onlyWhenDirty: boolean = true ) : string[] {
97 | let errorMessages: string[] = [];
98 | if( ( !onlyWhenDirty || control.dirty ) && control.errors ) {
99 | let errorArray = Object.keys( control.errors );
100 | for ( let err in errorArray ) {
101 | if ( control.errors[ errorArray[ err ] ].message ) {
102 | errorMessages.push( control.errors[ errorArray[ err ] ].message );
103 | }
104 | }
105 | }
106 | return errorMessages;
107 | }
108 |
109 | /*
110 | Ensures a programmatic change to an AbstractControl value is marked as dirty (and by default as touched)
111 | prior to the change, properly invoking validation and the display of any validation errors.
112 | */
113 | changeControlValue( control: AbstractControl, value: any, markTouched: boolean = true ) : void {
114 | control.markAsDirty();
115 | control.markAsTouched( markTouched );
116 | control.setValue( value );
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/src/app/vadacl/validation-methods.ts:
--------------------------------------------------------------------------------
1 |
2 | import { AbstractControl, Validators } from '@angular/forms';
3 | import { Messages } from './messages/messages-en';
4 |
5 | /*
6 | The ValidationMethods class contains all of the validation methods defined for use by vadacl. Validation methods
7 | provided in Angular 2 are wrapped and leveraged whenever practical.
8 | */
9 | export class ValidationMethods {
10 |
11 | //Modeled after the official Angular 2 Required validators value checks
12 | static isEmpty( control: AbstractControl ) {
13 | let v = control.value;
14 | if ( v === undefined || v === null || ( typeof v === 'string' && v == '' ) ) {
15 | return true;
16 | } else {
17 | return false;
18 | }
19 | }
20 |
21 | static required( message ?: string ) {
22 | let msg = message || Messages.required;
23 | return function ( control: AbstractControl ) {
24 | return ValidationMethods.isEmpty( control ) ? { 'required': { 'isEmpty': true, 'message': msg } } : null;
25 | }
26 | }
27 |
28 | static minLength( minLength: number, message ?: string ) {
29 | let msg = message || Messages.minLength;
30 | return function ( control: AbstractControl ) {
31 | //Use and invoke the official Angular 2 minLength Validator
32 | let baseValidator = Validators.minLength( minLength );
33 | let outcome = baseValidator( control) ;
34 | if ( outcome ) {
35 | //Append the message
36 | outcome[ 'minlength' ].message = msg;
37 | }
38 | return outcome;
39 | }
40 | }
41 |
42 | static maxLength( maxLength: number, message ?: string ) {
43 | let msg = message || Messages.maxLength;
44 | return function ( control: AbstractControl ) {
45 | //Use and invoke the official Angular 2 maxLength Validator
46 | let baseValidator = Validators.maxLength( maxLength );
47 | let outcome = baseValidator( control) ;
48 | if ( outcome ) {
49 | //Append the message
50 | outcome[ 'maxlength' ].message = msg;
51 | }
52 | return outcome;
53 | }
54 | }
55 |
56 | static pattern( pattern: string, message ?: string ) {
57 | let msg = message || Messages.pattern;
58 | return function ( control: AbstractControl ) {
59 | //Use and invoke the official Angular 2 pattern Validator
60 | let baseValidator = Validators.pattern( pattern );
61 | let outcome = baseValidator( control) ;
62 | if ( outcome ) {
63 | //Append the message
64 | outcome[ 'pattern' ].message = msg;
65 | }
66 | return outcome;
67 | }
68 | }
69 |
70 | static withinLength( minLength: number, maxLength: number, message ?: string ) {
71 | let msg = message || Messages.withinLength;
72 | return function ( control: AbstractControl ) {
73 | //Do not display if the control is empty
74 | if( ValidationMethods.isEmpty( control ) ){
75 | return null;
76 | }
77 | let v = control.value;
78 | return v.length < minLength || v.length > maxLength ?
79 | { 'withinlength': { 'minLength': minLength, 'maxLength': maxLength, 'actualLength': v.length, 'message': msg } } :
80 | null
81 | }
82 | }
83 |
84 | }
85 |
86 |
87 |
--------------------------------------------------------------------------------
/src/app/version/index.ts:
--------------------------------------------------------------------------------
1 | export * from './version.component';
2 |
--------------------------------------------------------------------------------
/src/app/version/version.component.html:
--------------------------------------------------------------------------------
1 | Version: {{versionNumber}}
2 |
--------------------------------------------------------------------------------
/src/app/version/version.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 |
3 | import { By } from '@angular/platform-browser';
4 | import { DebugElement } from '@angular/core';
5 | import { addProviders, async, inject } from '@angular/core/testing';
6 | import { VersionComponent } from './version.component';
7 | import { VersionService } from './version.service';
8 | import { Http } from '@angular/http';
9 |
10 | let http = {} as Http;
11 | let versionService = new VersionService( http );
12 |
13 | describe('Component: Version', () => {
14 | it('should create an instance', () => {
15 | let component = new VersionComponent( versionService );
16 | expect(component).toBeTruthy();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/src/app/version/version.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { VersionService } from './version.service';
3 |
4 | @Component({
5 | moduleId: module.id,
6 | selector: 'app-version',
7 | templateUrl: 'version.component.html',
8 | })
9 | export class VersionComponent implements OnInit {
10 |
11 | versionNumber = '-.-.-';
12 |
13 | constructor( private versionService: VersionService ) { }
14 |
15 | ngOnInit() {
16 | this.versionService.getVersion().then( versions => this.versionNumber = versions[0].name );
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/version/version.service.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 |
3 | import { addProviders, async, inject } from '@angular/core/testing';
4 | import { VersionService } from './version.service';
5 | import { HTTP_PROVIDERS } from '@angular/http';
6 |
7 | describe('Service: Version', () => {
8 | beforeEach(() => {
9 | addProviders([VersionService, HTTP_PROVIDERS ]);
10 | });
11 |
12 | it('should instantiate the service',
13 | inject([VersionService],
14 | (service: VersionService) => {
15 | expect(service).toBeTruthy();
16 | }));
17 | });
18 |
--------------------------------------------------------------------------------
/src/app/version/version.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Http } from '@angular/http';
3 |
4 | import 'rxjs/add/operator/toPromise';
5 |
6 | @Injectable()
7 | export class VersionService {
8 |
9 | private versionUrl = 'app/version';
10 |
11 | constructor( private http: Http ) { }
12 |
13 | getVersion() {
14 | return this.http.get(this.versionUrl)
15 | .toPromise()
16 | .then(response => response.json().data )
17 | .catch(this.handleError);
18 | }
19 |
20 | private handleError(error: any) {
21 | console.error('An error occurred', error);
22 | return Promise.reject(error.message || error);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bcswartz/angular2-sandbox-guildrunner/c57c5ff1083b1b6ae5a1b6d5ae93adc9e547efa8/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GuildRunner
6 |
7 |
8 | {{#unless environment.production}}
9 |
10 | {{/unless}}
11 |
12 |
13 |
14 |
15 |
16 | Loading...
17 |
18 | {{#each scripts.polyfills}}
19 |
20 | {{/each}}
21 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
2 | import { AppModule } from './app/app.module';
3 | platformBrowserDynamic().bootstrapModule(AppModule);
4 |
--------------------------------------------------------------------------------
/src/system-config.ts:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // SystemJS configuration file, see links for more information
4 | // https://github.com/systemjs/systemjs
5 | // https://github.com/systemjs/systemjs/blob/master/docs/config-api.md
6 |
7 | /***********************************************************************************************
8 | * User Configuration.
9 | **********************************************************************************************/
10 | /** Map relative paths to URLs. */
11 | const map: any = {
12 | 'angular2-in-memory-web-api': 'vendor/angular2-in-memory-web-api'
13 | };
14 |
15 | /** User packages configuration. */
16 | const packages: any = {
17 | 'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
18 | };
19 |
20 | ////////////////////////////////////////////////////////////////////////////////////////////////
21 | /***********************************************************************************************
22 | * Everything underneath this line is managed by the CLI.
23 | **********************************************************************************************/
24 | const barrels: string[] = [
25 | // Angular specific barrels.
26 | '@angular/core',
27 | '@angular/common',
28 | '@angular/compiler',
29 | '@angular/forms',
30 | '@angular/http',
31 | '@angular/router',
32 | '@angular/platform-browser',
33 | '@angular/platform-browser-dynamic',
34 |
35 | // Thirdparty barrels.
36 | 'rxjs',
37 |
38 | // App specific barrels.
39 | 'app',
40 | 'app/shared',
41 | 'app/version',
42 | 'app/main-navigation',
43 | 'app/sandbox/guild-list',
44 | 'app/home',
45 | 'app/guilds-master',
46 | 'app/guilds-detail',
47 | 'app/sandbox/chapter-list',
48 | 'app/sandbox/chapter-member-list',
49 | 'app/chapters-master',
50 | 'app/members-master',
51 | 'app/sandbox/chapter-dynamic-form',
52 | 'app/sandbox/chapter-reactive-form',
53 | /** @cli-barrel */
54 | ];
55 |
56 | const cliSystemConfigPackages: any = {};
57 | barrels.forEach((barrelName: string) => {
58 | cliSystemConfigPackages[barrelName] = { main: 'index' };
59 | });
60 |
61 | /** Type declaration for ambient System. */
62 | declare var System: any;
63 |
64 | // Apply the CLI SystemJS configuration.
65 | System.config({
66 | map: {
67 | '@angular': 'vendor/@angular',
68 | 'rxjs': 'vendor/rxjs',
69 | 'main': 'main.js'
70 | },
71 | packages: cliSystemConfigPackages
72 | });
73 |
74 | // Apply the user's configuration.
75 | System.config({ map, packages });
76 |
--------------------------------------------------------------------------------
/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "declaration": false,
5 | "emitDecoratorMetadata": true,
6 | "experimentalDecorators": true,
7 | "mapRoot": "/",
8 | "module": "commonjs",
9 | "moduleResolution": "node",
10 | "noEmitOnError": true,
11 | "noImplicitAny": false,
12 | "outDir": "../dist/",
13 | "rootDir": ".",
14 | "sourceMap": true,
15 | "target": "es5",
16 | "inlineSources": true
17 | },
18 |
19 | "files": [
20 | "main.ts",
21 | "typings.d.ts"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | // Typings reference file, see links for more information
2 | // https://github.com/typings/typings
3 | // https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html
4 |
5 | ///
6 | declare var module: { id: string };
7 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "class-name": true,
7 | "comment-format": [
8 | true,
9 | "check-space"
10 | ],
11 | "curly": true,
12 | "eofline": true,
13 | "forin": true,
14 | "indent": [
15 | true,
16 | "spaces"
17 | ],
18 | "label-position": true,
19 | "label-undefined": true,
20 | "max-line-length": [
21 | true,
22 | 140
23 | ],
24 | "member-access": false,
25 | "member-ordering": [
26 | true,
27 | "static-before-instance",
28 | "variables-before-functions"
29 | ],
30 | "no-arg": true,
31 | "no-bitwise": true,
32 | "no-console": [
33 | true,
34 | "debug",
35 | "info",
36 | "time",
37 | "timeEnd",
38 | "trace"
39 | ],
40 | "no-construct": true,
41 | "no-debugger": true,
42 | "no-duplicate-key": true,
43 | "no-duplicate-variable": true,
44 | "no-empty": false,
45 | "no-eval": true,
46 | "no-inferrable-types": true,
47 | "no-shadowed-variable": true,
48 | "no-string-literal": false,
49 | "no-switch-case-fall-through": true,
50 | "no-trailing-whitespace": true,
51 | "no-unused-expression": true,
52 | "no-unused-variable": true,
53 | "no-unreachable": true,
54 | "no-use-before-declare": true,
55 | "no-var-keyword": true,
56 | "object-literal-sort-keys": false,
57 | "one-line": [
58 | true,
59 | "check-open-brace",
60 | "check-catch",
61 | "check-else",
62 | "check-whitespace"
63 | ],
64 | "quotemark": [
65 | true,
66 | "single"
67 | ],
68 | "radix": true,
69 | "semicolon": [
70 | "always"
71 | ],
72 | "triple-equals": [
73 | true,
74 | "allow-null-check"
75 | ],
76 | "typedef-whitespace": [
77 | true,
78 | {
79 | "call-signature": "nospace",
80 | "index-signature": "nospace",
81 | "parameter": "nospace",
82 | "property-declaration": "nospace",
83 | "variable-declaration": "nospace"
84 | }
85 | ],
86 | "variable-name": false,
87 | "whitespace": [
88 | true,
89 | "check-branch",
90 | "check-decl",
91 | "check-operator",
92 | "check-separator",
93 | "check-type"
94 | ],
95 |
96 | "directive-selector-name": [true, "camelCase"],
97 | "component-selector-name": [true, "kebab-case"],
98 | "directive-selector-type": [true, "attribute"],
99 | "component-selector-type": [true, "element"],
100 | "use-input-property-decorator": true,
101 | "use-output-property-decorator": true,
102 | "use-host-property-decorator": true,
103 | "no-input-rename": true,
104 | "no-output-rename": true,
105 | "use-life-cycle-interface": true,
106 | "use-pipe-transform-interface": true,
107 | "component-class-suffix": true,
108 | "directive-class-suffix": true
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/typings.json:
--------------------------------------------------------------------------------
1 | {
2 | "globalDevDependencies": {
3 | "angular-protractor": "registry:dt/angular-protractor#1.5.0+20160425143459",
4 | "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
5 | "selenium-webdriver": "registry:dt/selenium-webdriver#2.44.0+20160317120654"
6 | },
7 | "globalDependencies": {
8 | "es6-shim": "registry:dt/es6-shim#0.31.2+20160602141504"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------