diff --git a/README.md b/README.md index 025b9eff..243003e8 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,43 @@ -# Angular Master Class by thoughtram +# Angular Master Class -This is the Angular Master Class exercise repository. Here's where you'll build your Angular application throughout this training. +This repository contains all the **Lab Solutions** for the Angular Master Class courseware by thoughtram. -Please make sure to follow our [Preparation Guide](http://thoughtram.io/prepare-for-your-training.html) to set up your machine **before** you come to the class. +## Learning Modules +Each learning module of the **Angular Master Class** course has 1 or more lab exercises. Each lab exercise adds features and enhancements to the previous lab exercise. + +* The final module solutions are stored in module branches. +* The lab exercise solutions are accessed via git tags; e.g. `jump-start-step-10` or `observables-step-3`. + +Shown below are quick-links and ordering of the Angular Master Class courseware modules: + +| Module | Git Command | +|--------|--------| +| Module 1: **[Jump-Start](https://github.com/thoughtram/angular-master-class-solutions/tree/jump-start/src)** | `git checkout jump-start-step-` | +| Module 2: **[Observables](https://github.com/thoughtram/angular-master-class-solutions/tree/observables/src)** | `git checkout observables-step-` | +| Module 3: **[Architecture](https://github.com/thoughtram/angular-master-class-solutions/tree/architecture/src)** | `git checkout architecture-step-` | +| Module 4: **[Routing](https://github.com/thoughtram/angular-master-class-solutions/tree/routing/src)** | `git checkout routing-step-` | +| Module 5: **[Forms](https://github.com/thoughtram/angular-master-class-solutions/tree/forms/src)** | `git checkout forms-step-` | +| Module 6: **[Redux](https://github.com/thoughtram/angular-master-class-solutions/tree/redux/src)** | `git checkout redux-step-` | +| Module 7: **[ngrx](https://github.com/thoughtram/angular-master-class-solutions/tree/ngrx/src)** | `git checkout ngrx-step-` | +| Module 8: **[Testing](https://github.com/thoughtram/angular-master-class-solutions/tree/testing/src)** | `git checkout testing-step-` | +| Module 9: **[Reusable Libraries](https://github.com/thoughtram/angular-master-class-solutions/tree/reusable-libraries/src)** | `git checkout reusable-libraries-step-` | +| Module 10: **[Progressive Web Apps](https://github.com/thoughtram/angular-master-class-solutions/tree/pwa/src)** | `git checkout pwa-step-` | +| Module 11: **[Universal](https://github.com/thoughtram/angular-master-class-solutions/tree/universal/src)** | `git checkout universal-step-` | + +For each lab exercise, simply use the *git* command to quickly checkout the solution for that lab exercise: + +``` +git checkout jump-start-step-11 +``` + +To *checkout* the final module solution (after all the lab exercises are completed), either checkout the *branch* or the latest *tag* of that branch. So, for example, to checkout the final solution **Module 1: Jump-Start**, use: + +``` +git checkout jump-start +``` + +## Local Setup If not done already, clone this repository using: ``` @@ -17,13 +51,30 @@ $ cd angular-master-class-starter $ npx yarn install ``` -Then run the application by executing: +> You may also need to globally install the Angular-Cli: `npm i -g @angular/cli` + +## Web and App Servers + +To launch your web application, use a Terminal session with the command: ``` $ ng serve ``` -Then open up your browser at `http://localhost:4200` +This starts a web server for the Angular application; open with a browser url `http://localhost:4200`. + +And since your Angular application may request external, remote data [from `http://localhost:4201/api`], you will need a local app server to respond to the REST API calls. We have already configured a server as part of this repository. + +Simply start a second, separate Terminal session with the commend: + +``` +$ npm run rest-api +``` + +> While only some of the solutions use the app server, it will not hurt to start it immediately. + + +## Important Have fun and good luck! diff --git a/README_starter.md b/README_starter.md new file mode 100644 index 00000000..025b9eff --- /dev/null +++ b/README_starter.md @@ -0,0 +1,30 @@ +# Angular Master Class by thoughtram + +This is the Angular Master Class exercise repository. Here's where you'll build your Angular application throughout this training. + +Please make sure to follow our [Preparation Guide](http://thoughtram.io/prepare-for-your-training.html) to set up your machine **before** you come to the class. + +If not done already, clone this repository using: + +``` +$ git clone https://github.com/thoughtram/angular-master-class-starter.git +``` + +Done? Great, now install the dependencies (this might take a little while): + +``` +$ cd angular-master-class-starter +$ npx yarn install +``` + +Then run the application by executing: + +``` +$ ng serve +``` + +Then open up your browser at `http://localhost:4200` + +Have fun and good luck! + +Christoph & Pascal & Dominic diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 60c84248..9f378e1b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,10 +1,12 @@ import { Component } from '@angular/core'; @Component({ - selector: 'trm-contacts-app', - templateUrl: './app.component.html', - styleUrls: ['./app.component.scss'] + selector: 'trm-voting-app', + template: ` + + `, + styles: [ ] }) -export class ContactsAppComponent { - title = 'Angular Master Class'; +export class AppComponent { + } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 14ee29fa..5fc9d111 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,22 +1,37 @@ import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NgModule } from '@angular/core'; -import { FlexLayoutModule } from '@angular/flex-layout'; +import { FormsModule } from '@angular/forms'; +import { HttpClientModule } from '@angular/common/http'; + import { ContactsMaterialModule } from './contacts-material.module'; +import { FlexLayoutModule } from "@angular/flex-layout"; -import { ContactsAppComponent } from './app.component'; +import { AppStore } from './store/app-store'; +import { AppComponent } from "./app.component"; +import { DashboardComponent } from "./components/dashboard.component"; +import { VoterComponent } from "./components/voter.component"; +import { StatusComponent } from "./components/status.component"; @NgModule({ - declarations: [ContactsAppComponent], imports: [ BrowserModule, + FormsModule, + HttpClientModule, BrowserAnimationsModule, ContactsMaterialModule, FlexLayoutModule ], - bootstrap: [ContactsAppComponent] + declarations: [ + AppComponent, + DashboardComponent, + VoterComponent, + StatusComponent + ], + providers: [AppStore], + bootstrap: [AppComponent] }) -export class ContactsModule { - +export class AppModule { } + diff --git a/src/app/components/dashboard.component.ts b/src/app/components/dashboard.component.ts new file mode 100644 index 00000000..f07281b8 --- /dev/null +++ b/src/app/components/dashboard.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'trm-dashboard', + template: ` + + + + ` +}) +export class DashboardComponent { } + diff --git a/src/app/components/status.component.ts b/src/app/components/status.component.ts new file mode 100644 index 00000000..b3e92de5 --- /dev/null +++ b/src/app/components/status.component.ts @@ -0,0 +1,31 @@ +import { Component } from '@angular/core'; +import { AppStore } from '../store/app-store'; + +@Component({ + selector: 'trm-status', + template: ` + {{ state.counter }} +
All Votes!
+ `, + styles : [ + `:host { text-align:center; font-size:1.1em; font-weight: bolder }`, + `.tip { font-size:0.7em; padding-top:5px;font-weight: normal; }` + ] +}) +export class StatusComponent { + + state; + + /** + * Inject the appStore here and listen + * for vote changes! + */ + constructor(private store: AppStore) { + this.state = store.getState(); + + store.subscribe(() => { + this.state = store.getState(); + }) + } +} + diff --git a/src/app/components/voter.component.ts b/src/app/components/voter.component.ts new file mode 100644 index 00000000..c11bf33d --- /dev/null +++ b/src/app/components/voter.component.ts @@ -0,0 +1,39 @@ +import { Component } from '@angular/core'; +import { AppStore } from '../store/app-store'; + +@Component({ + selector: 'trm-voter', + template: ` +
+ + + + + +
+ `, + styles : [ + `button.yes { background-color: darkgreen; }`, + `button.no { background-color: red; }` + ] +}) +export class VoterComponent { + + /** + * Inject the appstore + */ + constructor(private store: AppStore) { } + + private increment() { + // @Todo - dispatch action to the store + } + + private decrement() { + // @Todo - dispatch action to the store + } +} + diff --git a/src/app/store/app-store.ts b/src/app/store/app-store.ts new file mode 100644 index 00000000..20cb70d8 --- /dev/null +++ b/src/app/store/app-store.ts @@ -0,0 +1,53 @@ +/** + * To emulate a Redux store, simply + * support the dispatch(), subscribe() methods and + * use reducers to process actions with immutable instances + */ +export class AppStore { + + /** + * Accessor to current store state + */ + getState():any { + return this.state; + } + + /** + * Dispatch the action thru reducers to + * condistionally update the state + */ + dispatch(action) { + const newState = this.reducer(this.state, action); + if (newState !== this.state) { + this.state = newState; + this.listeners.forEach(notify => notify()); + } + } + + /** + * Allow views to listen for store synchronous + * store changes + */ + subscribe(notify) { + this.listeners.push(notify); + } + + /** + * Use the custom actions to update the counter state! + * + * @TODO - add your custom actions here! + */ + protected reducer(state, action){ + switch(action) { + default : return state; + } + } + + protected listeners = []; + protected state = { + counter : 0 + }; + +} + + diff --git a/src/app/store/store.ts b/src/app/store/store.ts new file mode 100644 index 00000000..f53abfb3 --- /dev/null +++ b/src/app/store/store.ts @@ -0,0 +1,14 @@ +/** + * Interfaces copied from the Redux/index.d.ts file... useful to avoid Typescript warnings. + * + * + */ +export interface Action { type: any; } +export interface Unsubscribe { (): void; } +export interface Dispatch { (action: A): A; } + +export interface Store { + getState(): S; + dispatch: (action)=>void; + subscribe(listener: () => void): Unsubscribe; +} diff --git a/src/assets/images/taxtAct_bkgrnd.jpg b/src/assets/images/taxtAct_bkgrnd.jpg new file mode 100644 index 00000000..b5a57259 Binary files /dev/null and b/src/assets/images/taxtAct_bkgrnd.jpg differ diff --git a/src/assets/images/tea-tax-hero.jpeg b/src/assets/images/tea-tax-hero.jpeg new file mode 100644 index 00000000..5522b50c Binary files /dev/null and b/src/assets/images/tea-tax-hero.jpeg differ diff --git a/src/assets/images/tea_act_logo.jpeg b/src/assets/images/tea_act_logo.jpeg new file mode 100644 index 00000000..5b8333ca Binary files /dev/null and b/src/assets/images/tea_act_logo.jpeg differ diff --git a/src/index.html b/src/index.html index f65e5efc..ecf60433 100644 --- a/src/index.html +++ b/src/index.html @@ -2,13 +2,16 @@ - Contacts + Voting with Angular + Redux + + - + Loading... + diff --git a/src/main.ts b/src/main.ts index 7fb0ca35..18cbbe5b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,10 @@ import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - -import { ContactsModule } from './app/app.module'; import { environment } from './environments/environment'; +import { AppModule } from './app/app.module'; if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(ContactsModule) - .catch(err => console.log(err)); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/src/styles.scss b/src/styles.scss index e2c8a291..b1f2d8e9 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -4,6 +4,7 @@ body, html { background-color: #f6f6f6; position: relative; + font-family: "Roboto"; } html, body, trm-contacts-app, mat-drawer-container, .main-content { @@ -16,174 +17,25 @@ html, body, trm-contacts-app, mat-drawer-container, .main-content { width: 100%; } -.mat-list-base { - background-color: white; - padding-top: 0!important; -} - -.mat-list-base .mat-list-item { - border-bottom: 1px solid #ddd; - text-decoration: none; - - &:hover { - background-color: #f0f0f0; - } - - &.active { - background-color: #e9e9e9; - } -} - -.trm-floating-button { - position: absolute!important; - right: 2em; - bottom: 2em; - color: #fff; -} - -.trm-contacts-detail, -.trm-contacts-creator, -.trm-contacts-editor, -.trm-about { - .mat-card { - max-width: 500px; - margin: 0 auto; - margin-top: 2em; - - img { - border-radius: 50%; - } - } - - .mat-card-title-group.fullBleed { - background-color: #afbafc; - padding: 30px; - margin-top: -24px; - margin-left: -16px; - margin-right: -16px; - margin-bottom: 24px; - - &.editing { - background-color: #fdecbb; - } - } - - .mat-card-content { - dl { - margin-top: 2em; - border-bottom: 1px solid rgba(221, 221, 221, 0.15); - padding-top: 1em; - padding-bottom: 1.5em; - } - - dt, dd { - display: inline-block; - margin-top: 0.5em; - - &:first-child { - margin-top: 0; - } - } - - dt { - width: 25%; - font-weight: bold; - } - - dd { - margin-left: 0; - width: 70%; - } - - dt:first-child + dd { - margin-top: 0; - } - } - - fieldset { - legend { color: #ccc; } - border: 1px solid #ddd; - margin-top: 1em; - padding-left: 1em; - padding-right: 1em; - - mat-input-container, mat-select { - margin-top: 2em; - } - - } - - .mat-form-field { - - &.ng-invalid { - - .mat-hint { - color: rgb(215, 65, 57); - } - - &.ng-touched:not(.ng-pristine) { - .mat-input-underline { - border-color: rgb(215, 65, 57); - } - } - } - } - - mat-radio-button { - display: block; - color: #666; - } - - .gender-label { - color: #ccc; - } - .mat-radio-outer-circle { - border-color: rgba(0,0,0,.12); - } - - .mat-radio-label { - margin-top: 0.5em; - } -} - -.trm-about .mat-card img { - border-radius: 0; - max-height: 400px; -} - -.trm-search-container { - width: 100%; -} - -mat-dialog-container { - font-family: Roboto,"Helvetica Neue",sans-serif; - - [mat-dialog-title] { - margin-left: -24px; - margin-right: -24px; - margin-top: -24px; - padding: 0.5em; - text-align: center; - color: #fff; - background-color: #3f51b5; - font-weight: normal; - } - - [mat-dialog-content] { - margin-bottom: 12px; - margin-top: 36px; - font-size: 14px; - } -} - -mat-dialog-actions { - padding: 8px 0; - [mat-button],[mat-raised-button] { - margin: 0 4px; - } -} - -trm-tab > mat-input-container:first-child { - padding-top: 25px; +trm-dashboard { + padding-left: 50px; + background: url(http://github.com/assets/images/taxtAct_bkgrnd.jpg); + width: 781px; + height: 395px; + display: block; +} + +trm-voter { + border: 2px solid gray; + background-color: rgba(2455, 255, 255, 0.75); + width: 84px; + min-height: 263px; + display: block; + border-radius: 20px; + position: absolute; + left: 543px; + top: 108px; + padding:20px; + padding-left: 0px; }