├── .editorconfig
├── .gitignore
├── README.md
├── angular-cli.json
├── e2e
├── app.e2e-spec.ts
├── app.po.ts
└── tsconfig.json
├── karma.conf.js
├── package.json
├── protractor.conf.js
├── src
├── app
│ ├── app.component.css
│ ├── app.component.html
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── home-page
│ │ ├── home-page.component.css
│ │ ├── home-page.component.html
│ │ ├── home-page.component.spec.ts
│ │ └── home-page.component.ts
│ ├── login-page
│ │ ├── login-page.component.css
│ │ ├── login-page.component.html
│ │ ├── login-page.component.spec.ts
│ │ └── login-page.component.ts
│ └── registration-page
│ │ ├── registration-page.component.css
│ │ ├── registration-page.component.html
│ │ ├── registration-page.component.spec.ts
│ │ └── registration-page.component.ts
├── assets
│ └── .gitkeep
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── providers
│ └── af.ts
├── styles.css
├── test.ts
└── tsconfig.json
└── tslint.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 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.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 |
10 | # IDEs and editors
11 | /.idea
12 | .project
13 | .classpath
14 | .c9/
15 | *.launch
16 | .settings/
17 |
18 | # IDE - VSCode
19 | .vscode/*
20 | !.vscode/settings.json
21 | !.vscode/tasks.json
22 | !.vscode/launch.json
23 | !.vscode/extensions.json
24 |
25 | # misc
26 | /.sass-cache
27 | /connect.lock
28 | /coverage/*
29 | /libpeerconnection.log
30 | npm-debug.log
31 | testem.log
32 | /typings
33 |
34 | # e2e
35 | /e2e/*.js
36 | /e2e/*.map
37 |
38 | #System Files
39 | .DS_Store
40 | Thumbs.db
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FirebaseCrud
2 |
3 | ## This is the code for a tutorial series that can be found on [progblog.io](https://progblog.io)
4 |
5 | In this tutorial we start with a blank app generated using Angular CLI. We use [Firebase](https://firebase.google.com/) to make the next steps EASY. First, we use Firebase's authentication to add Google Login. We later extend that to also use email/password. We then use the Firebase Realtime Database to make a simple chat app. This project is my attempt to show how easy Firebase makes seemingly arduous tasks.
6 |
7 | ### Sections
8 |
9 | [**Part 1:**](https://progblog.io/Angular-2-Firebase-Tutorial-Part-1-Create-a-Firebase-3-CRUD-app-with-Angular-CLI/) Installing necessary components. Generating a blank app through Angular CLI. Add Bootstrap for styling. Create Firebase account. Install and setup Firebase through NPM.
10 |
11 | The code for **part 1** can be found [here](https://github.com/HuddleHouse/firebase-crud/tree/firebase-crud-part-1).
12 |
13 | [**Part 2:**](https://progblog.io/Angular-2-Firebase-Tutorial-Part-2-Adding-Authentication/) Add user login through their Google account. Create the Login Page and Home Page Components. Add asynchronous monitoring of the logged in status. Redirect to the Home Page when logged in and to the Login Page when the user logs out.
14 |
15 | The code for **part 2** can be found [here](https://github.com/HuddleHouse/firebase-crud/tree/firebase-crud-part-2).
16 |
17 | [**Part 3:**](https://progblog.io/Angular-2-Firebase-Tutorial-Part-3-Adding-the-Realtime-Database/) We will add a simple chat app that stores messages into a Firebase Realtime Database.
18 |
19 | The code for **part 3** can be found [here](https://github.com/HuddleHouse/firebase-crud/tree/firebase-crud-part-3).
20 |
21 | [**Part 4:**](https://progblog.io/Angular-2-Firebase-Tutorial-Part-4-Adding-Email-Password-Authentication/) We add Email/Password user authentication using Firebase.
22 |
23 | The code for **part 4** can be found [here](https://github.com/HuddleHouse/firebase-crud/tree/firebase-crud-part-4).
24 |
25 | **Part 5:** COMING SOON Adding the ability for a user to edit or delete any messages that they have sent.
26 |
27 | ## Development server
28 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
29 |
30 | ## Code scaffolding
31 |
32 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`.
33 |
34 | ## Build
35 |
36 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
37 |
38 | ## Running unit tests
39 |
40 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
41 |
42 | ## Running end-to-end tests
43 |
44 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
45 | Before running the tests make sure you are serving the app via `ng serve`.
46 |
47 | ## Deploying to Github Pages
48 |
49 | Run `ng github-pages:deploy` to deploy to Github Pages.
50 |
51 | ## Further help
52 |
53 | To get more help on the `angular-cli` use `ng help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
54 | # firebase-crud
55 |
--------------------------------------------------------------------------------
/angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "project": {
3 | "version": "1.0.0-beta.24",
4 | "name": "firebase-crud"
5 | },
6 | "apps": [
7 | {
8 | "root": "src",
9 | "outDir": "dist",
10 | "assets": [
11 | "assets",
12 | "favicon.ico"
13 | ],
14 | "index": "index.html",
15 | "main": "main.ts",
16 | "test": "test.ts",
17 | "tsconfig": "tsconfig.json",
18 | "prefix": "app",
19 | "mobile": false,
20 | "styles": [
21 | "styles.css"
22 | ],
23 | "scripts": [],
24 | "environments": {
25 | "source": "environments/environment.ts",
26 | "dev": "environments/environment.ts",
27 | "prod": "environments/environment.prod.ts"
28 | }
29 | }
30 | ],
31 | "addons": [],
32 | "packages": [],
33 | "e2e": {
34 | "protractor": {
35 | "config": "./protractor.conf.js"
36 | }
37 | },
38 | "test": {
39 | "karma": {
40 | "config": "./karma.conf.js"
41 | }
42 | },
43 | "defaults": {
44 | "styleExt": "css",
45 | "prefixInterfaces": false,
46 | "inline": {
47 | "style": false,
48 | "template": false
49 | },
50 | "spec": {
51 | "class": false,
52 | "component": true,
53 | "directive": true,
54 | "module": false,
55 | "pipe": true,
56 | "service": true
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { FirebaseCrudPage } from './app.po';
2 |
3 | describe('firebase-crud App', function() {
4 | let page: FirebaseCrudPage;
5 |
6 | beforeEach(() => {
7 | page = new FirebaseCrudPage();
8 | });
9 |
10 | it('should display message saying app works', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('app works!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, element, by } from 'protractor';
2 |
3 | export class FirebaseCrudPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "declaration": false,
5 | "emitDecoratorMetadata": true,
6 | "experimentalDecorators": true,
7 | "module": "commonjs",
8 | "moduleResolution": "node",
9 | "outDir": "../dist/out-tsc-e2e",
10 | "sourceMap": true,
11 | "target": "es5",
12 | "typeRoots": [
13 | "../node_modules/@types"
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/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', 'angular-cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-remap-istanbul'),
12 | require('angular-cli/plugins/karma')
13 | ],
14 | files: [
15 | { pattern: './src/test.ts', watched: false }
16 | ],
17 | preprocessors: {
18 | './src/test.ts': ['angular-cli']
19 | },
20 | mime: {
21 | 'text/x-typescript': ['ts','tsx']
22 | },
23 | remapIstanbulReporter: {
24 | reports: {
25 | html: 'coverage',
26 | lcovonly: './coverage/coverage.lcov'
27 | }
28 | },
29 | angularCli: {
30 | config: './angular-cli.json',
31 | environment: 'dev'
32 | },
33 | reporters: config.angularCli && config.angularCli.codeCoverage
34 | ? ['progress', 'karma-remap-istanbul']
35 | : ['progress'],
36 | port: 9876,
37 | colors: true,
38 | logLevel: config.LOG_INFO,
39 | autoWatch: true,
40 | browsers: ['Chrome'],
41 | singleRun: false
42 | });
43 | };
44 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "firebase-crud",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "angular-cli": {},
6 | "scripts": {
7 | "ng": "ng",
8 | "start": "ng serve",
9 | "lint": "tslint \"src/**/*.ts\"",
10 | "test": "ng test",
11 | "pree2e": "webdriver-manager update --standalone false --gecko false",
12 | "e2e": "protractor"
13 | },
14 | "private": true,
15 | "dependencies": {
16 | "@angular/common": "^2.3.1",
17 | "@angular/compiler": "^2.3.1",
18 | "@angular/core": "^2.3.1",
19 | "@angular/forms": "^2.3.1",
20 | "@angular/http": "^2.3.1",
21 | "@angular/platform-browser": "^2.3.1",
22 | "@angular/platform-browser-dynamic": "^2.3.1",
23 | "@angular/router": "^3.3.1",
24 | "angularfire2": "^2.0.0-beta.6",
25 | "core-js": "^2.4.1",
26 | "firebase": "^3.6.4",
27 | "rxjs": "^5.0.1",
28 | "ts-helpers": "^1.1.1",
29 | "zone.js": "^0.7.2"
30 | },
31 | "devDependencies": {
32 | "@angular/compiler-cli": "^2.3.1",
33 | "@types/jasmine": "2.5.38",
34 | "@types/node": "^6.0.42",
35 | "angular-cli": "1.0.0-beta.24",
36 | "codelyzer": "~2.0.0-beta.1",
37 | "jasmine-core": "2.5.2",
38 | "jasmine-spec-reporter": "2.5.0",
39 | "karma": "1.2.0",
40 | "karma-chrome-launcher": "^2.0.0",
41 | "karma-cli": "^1.0.1",
42 | "karma-jasmine": "^1.0.2",
43 | "karma-remap-istanbul": "^0.2.1",
44 | "protractor": "~4.0.13",
45 | "ts-node": "1.2.1",
46 | "tslint": "^4.0.2",
47 | "typescript": "~2.0.3"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
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 |
--------------------------------------------------------------------------------
/src/app/app.component.css:
--------------------------------------------------------------------------------
1 | .border-left {
2 | border-left: solid 1px;
3 | }
4 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 |
3 | import { TestBed, async } from '@angular/core/testing';
4 | import { AppComponent } from './app.component';
5 |
6 | describe('AppComponent', () => {
7 | beforeEach(() => {
8 | TestBed.configureTestingModule({
9 | declarations: [
10 | AppComponent
11 | ],
12 | });
13 | TestBed.compileComponents();
14 | });
15 |
16 | it('should create the app', async(() => {
17 | let fixture = TestBed.createComponent(AppComponent);
18 | let app = fixture.debugElement.componentInstance;
19 | expect(app).toBeTruthy();
20 | }));
21 |
22 | it(`should have as title 'app works!'`, async(() => {
23 | let fixture = TestBed.createComponent(AppComponent);
24 | let app = fixture.debugElement.componentInstance;
25 | expect(app.title).toEqual('app works!');
26 | }));
27 |
28 | it('should render title in a h1 tag', async(() => {
29 | let fixture = TestBed.createComponent(AppComponent);
30 | fixture.detectChanges();
31 | let compiled = fixture.debugElement.nativeElement;
32 | expect(compiled.querySelector('h1').textContent).toContain('app works!');
33 | }));
34 | });
35 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { AF } from "../providers/af";
3 | import { Router } from "@angular/router";
4 |
5 | @Component({
6 | selector: 'app-root',
7 | templateUrl: './app.component.html',
8 | styleUrls: ['./app.component.css']
9 | })
10 | export class AppComponent {
11 | public isLoggedIn: boolean;
12 |
13 | constructor(public afService: AF, private router: Router) {
14 | // This asynchronously checks if our user is logged it and will automatically
15 | // redirect them to the Login page when the status changes.
16 | // This is just a small thing that Firebase does that makes it easy to use.
17 | this.afService.af.auth.subscribe(
18 | (auth) => {
19 | if(auth == null) {
20 | console.log("Not Logged in.");
21 |
22 | this.isLoggedIn = false;
23 | this.router.navigate(['login']);
24 | }
25 | else {
26 | console.log("Successfully Logged in.");
27 | // Set the Display Name and Email so we can attribute messages to them
28 | if(auth.google) {
29 | this.afService.displayName = auth.google.displayName;
30 | this.afService.email = auth.google.email;
31 | }
32 | else {
33 | this.afService.displayName = auth.auth.email;
34 | this.afService.email = auth.auth.email;
35 | }
36 |
37 | this.isLoggedIn = true;
38 | this.router.navigate(['']);
39 | }
40 | }
41 | );
42 | }
43 |
44 | logout() {
45 | this.afService.logout();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { AppComponent } from './app.component';
4 | import { AngularFireModule } from 'angularfire2';
5 | import { LoginPageComponent } from './login-page/login-page.component';
6 | import {RouterModule, Routes} from "@angular/router";
7 | import {AF} from "../providers/af";
8 | import { HomePageComponent } from './home-page/home-page.component';
9 | import {FormsModule} from "@angular/forms";
10 | import { RegistrationPageComponent } from './registration-page/registration-page.component';
11 |
12 | export const firebaseConfig = {
13 | apiKey: "*************************",
14 | authDomain: "fir-crud-93710.firebaseapp.com",
15 | databaseURL: "https://fir-crud-93710.firebaseio.com",
16 | storageBucket: "fir-crud-93710.appspot.com",
17 | messagingSenderId: "720204736178"
18 | };
19 |
20 | const routes: Routes = [
21 | { path: '', component: HomePageComponent },
22 | { path: 'login', component: LoginPageComponent },
23 | { path: 'register', component: RegistrationPageComponent}
24 | ];
25 |
26 | @NgModule({
27 | imports: [
28 | BrowserModule,
29 | AngularFireModule.initializeApp(firebaseConfig),
30 | RouterModule.forRoot(routes),
31 | FormsModule
32 | ],
33 | declarations: [ AppComponent, LoginPageComponent, HomePageComponent, RegistrationPageComponent ],
34 | bootstrap: [ AppComponent ],
35 | providers: [AF]
36 | })
37 | export class AppModule {}
38 |
--------------------------------------------------------------------------------
/src/app/home-page/home-page.component.css:
--------------------------------------------------------------------------------
1 |
2 | .bs-example {
3 | position: relative;
4 | padding: 15px 15px 15px;
5 | margin: 0 -15px 0px;
6 | border-color: #e5e5e5 #eee #eee;
7 | border-style: solid;
8 | border-width: 1px 0;
9 | background-color: #E1E2E3;
10 | box-shadow: inset 0 3px 6px rgba(0,0,0,.05);
11 | height: 60vh;
12 | overflow-y: scroll;
13 | }
14 | #messages {
15 | background-color: #2d384a !important;
16 | }
17 |
18 | .author {
19 | font-size: 12px;
20 | }
21 | .send-message {
22 | color: #ffffff;
23 | background-color: #4184f3;
24 | text-decoration: none;
25 | padding: 10px 20px 10px 20px;
26 | display: inline-flex;
27 | cursor: pointer;
28 | }
29 |
30 | .message-text {
31 | display: block;
32 | padding: 9.5px;
33 | margin: 0 0 10px;
34 | font-size: 13px;
35 | line-height: 1.42857143;
36 | color: #333;
37 | word-break: break-all;
38 | word-wrap: break-word;
39 | background-color: #f5f5f5;
40 | border: 1px solid #ccc;
41 | border-radius: 4px;
42 | width: 100%;
43 | }
44 |
45 | p {
46 | font-size: 16px;
47 | }
48 |
49 | .bubble{
50 | background-color: #F2F2F2;
51 | border-radius: 5px;
52 | box-shadow: 0 0 6px #B2B2B2;
53 | display: inline-block;
54 | padding: 10px 18px;
55 | position: relative;
56 | vertical-align: top;
57 | }
58 |
59 | .bubble::before {
60 | background-color: #F2F2F2;
61 | content: "\00a0";
62 | display: block;
63 | height: 16px;
64 | position: absolute;
65 | top: 11px;
66 | transform: rotate( 29deg ) skew( -35deg );
67 | -moz-transform: rotate( 29deg ) skew( -35deg );
68 | -ms-transform: rotate( 29deg ) skew( -35deg );
69 | -o-transform: rotate( 29deg ) skew( -35deg );
70 | -webkit-transform: rotate( 29deg ) skew( -35deg );
71 | width: 20px;
72 | }
73 |
74 | .me {
75 | display: inherit;
76 | margin: 5px 45px 5px 20px;
77 | }
78 |
79 | .me::before {
80 | box-shadow: -2px 2px 2px 0 rgba( 178, 178, 178, .4 );
81 | left: -9px;
82 | }
83 |
84 | .you {
85 | display: inherit;
86 | margin: 5px 20px 5px 45px;
87 | }
88 |
89 | .you::before {
90 | box-shadow: 2px -2px 2px 0 rgba( 178, 178, 178, .4 );
91 | right: -9px;
92 | }
93 |
94 | .bs-example+.highlight, .bs-example+.zero-clipboard+.highlight {
95 | margin: 0px -15px 15px;
96 | border-width: 0 0 1px;
97 | border-radius: 0;
98 | }
99 | .highlight {
100 | padding: 9px 14px;
101 | margin-bottom: 14px;
102 | background-color: #f7f7f9;
103 | border: 1px solid #e1e1e8;
104 | border-radius: 4px;
105 | }
106 |
--------------------------------------------------------------------------------
/src/app/home-page/home-page.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
{{ message.message }}
8 |
9 | {{ message.displayName }} | {{ message.timestamp | date:"MM/dd/yy hh:mm a" }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | SEND
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/app/home-page/home-page.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3 | import { By } from '@angular/platform-browser';
4 | import { DebugElement } from '@angular/core';
5 |
6 | import { HomePageComponent } from './home-page.component';
7 |
8 | describe('HomePageComponent', () => {
9 | let component: HomePageComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | declarations: [ HomePageComponent ]
15 | })
16 | .compileComponents();
17 | }));
18 |
19 | beforeEach(() => {
20 | fixture = TestBed.createComponent(HomePageComponent);
21 | component = fixture.componentInstance;
22 | fixture.detectChanges();
23 | });
24 |
25 | it('should create', () => {
26 | expect(component).toBeTruthy();
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/app/home-page/home-page.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, OnInit, AfterViewChecked, ElementRef, ViewChild} from '@angular/core';
2 | import {AF} from "../../providers/af";
3 | import {FirebaseListObservable} from "angularfire2";
4 |
5 | @Component({
6 | selector: 'app-home-page',
7 | templateUrl: './home-page.component.html',
8 | styleUrls: ['./home-page.component.css']
9 | })
10 | export class HomePageComponent implements OnInit, AfterViewChecked {
11 | @ViewChild('scrollMe') private myScrollContainer: ElementRef;
12 | public newMessage: string;
13 | public messages: FirebaseListObservable;
14 |
15 | constructor(public afService: AF) {
16 | this.messages = this.afService.messages;
17 | }
18 |
19 | ngOnInit() {}
20 |
21 | ngAfterViewChecked() {
22 | this.scrollToBottom();
23 | }
24 |
25 | scrollToBottom(): void {
26 | try {
27 | this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight;
28 | } catch(err) { }
29 | }
30 |
31 | sendMessage(){
32 | this.afService.sendMessage(this.newMessage);
33 | this.newMessage = '';
34 | }
35 |
36 | isYou(email) {
37 | if(email == this.afService.email)
38 | return true;
39 | else
40 | return false;
41 | }
42 |
43 | isMe(email) {
44 | if(email == this.afService.email)
45 | return false;
46 | else
47 | return true;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/app/login-page/login-page.component.css:
--------------------------------------------------------------------------------
1 | /****** LOGIN MODAL ******/
2 | .loginmodal-container {
3 | padding: 30px;
4 | max-width: 350px;
5 | width: 100% !important;
6 | background-color: #F7F7F7;
7 | margin: 0 auto;
8 | border-radius: 2px;
9 | box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
10 | overflow: hidden;
11 | font-family: roboto;
12 | }
13 |
14 | .loginmodal-container h1 {
15 | text-align: center;
16 | font-size: 1.8em;
17 | font-family: roboto;
18 | }
19 |
20 | .loginmodal-submit {
21 | /* border: 1px solid #3079ed; */
22 | width: 100%;
23 | border: 0px;
24 | color: #fff;
25 | text-shadow: 0 1px rgba(0,0,0,0.1);
26 | background-color: #4d90fe;
27 | padding: 11px 0px;
28 | font-family: roboto;
29 | font-size: 21px;
30 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#4d90fe), to(#4787ed));
31 | }
32 |
33 | .loginmodal-submit:hover {
34 | /* border: 1px solid #2f5bb7; */
35 | border: 0px;
36 | text-shadow: 0 1px rgba(0,0,0,0.3);
37 | background-color: #357ae8;
38 | /* background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#4d90fe), to(#357ae8)); */
39 | }
40 |
41 | .loginmodal-container a {
42 | text-decoration: none;
43 | color: #2e6da4;
44 | font-weight: 400;
45 | text-align: center;
46 | display: inline-block;
47 | opacity: 0.6;
48 | transition: opacity ease 0.5s;
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/src/app/login-page/login-page.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Error:
4 | {{error}}
5 |
6 |
7 |
8 |
9 |
Login to Your Account
10 |
11 |
19 |
20 |
21 |
22 |
23 |
Register Now
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/app/login-page/login-page.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3 | import { By } from '@angular/platform-browser';
4 | import { DebugElement } from '@angular/core';
5 |
6 | import { LoginPageComponent } from './login-page.component';
7 |
8 | describe('LoginPageComponent', () => {
9 | let component: LoginPageComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | declarations: [ LoginPageComponent ]
15 | })
16 | .compileComponents();
17 | }));
18 |
19 | beforeEach(() => {
20 | fixture = TestBed.createComponent(LoginPageComponent);
21 | component = fixture.componentInstance;
22 | fixture.detectChanges();
23 | });
24 |
25 | it('should create', () => {
26 | expect(component).toBeTruthy();
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/app/login-page/login-page.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import {AF} from "../../providers/af";
3 | import {Router} from "@angular/router";
4 |
5 | @Component({
6 | selector: 'app-login-page',
7 | templateUrl: './login-page.component.html',
8 | styleUrls: ['./login-page.component.css']
9 | })
10 | export class LoginPageComponent {
11 | public error: any;
12 |
13 | constructor(public afService: AF, private router: Router) {}
14 |
15 | loginWithGoogle() {
16 | this.afService.loginWithGoogle().then((data) => {
17 | // Send them to the homepage if they are logged in
18 | console.log(data);
19 | this.afService.addUserInfo();
20 | this.router.navigate(['']);
21 | })
22 | }
23 |
24 | loginWithEmail(event, email, password){
25 | event.preventDefault();
26 | this.afService.loginWithEmail(email, password).then(() => {
27 | this.router.navigate(['']);
28 | })
29 | .catch((error: any) => {
30 | if (error) {
31 | this.error = error;
32 | console.log(this.error);
33 | }
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/app/registration-page/registration-page.component.css:
--------------------------------------------------------------------------------
1 | /****** LOGIN MODAL ******/
2 | .registermodal-container {
3 | padding: 30px;
4 | max-width: 350px;
5 | width: 100% !important;
6 | background-color: #F7F7F7;
7 | margin: 0 auto;
8 | border-radius: 2px;
9 | box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
10 | overflow: hidden;
11 | font-family: roboto;
12 | }
13 |
14 | .registermodal-container h1 {
15 | text-align: center;
16 | font-size: 1.8em;
17 | font-family: roboto;
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/registration-page/registration-page.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Error:
4 | {{error}}
5 |
6 |
7 |
8 |
9 |
Register
10 |
11 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/app/registration-page/registration-page.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3 | import { By } from '@angular/platform-browser';
4 | import { DebugElement } from '@angular/core';
5 |
6 | import { RegistrationPageComponent } from './registration-page.component';
7 |
8 | describe('RegistrationPageComponent', () => {
9 | let component: RegistrationPageComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | declarations: [ RegistrationPageComponent ]
15 | })
16 | .compileComponents();
17 | }));
18 |
19 | beforeEach(() => {
20 | fixture = TestBed.createComponent(RegistrationPageComponent);
21 | component = fixture.componentInstance;
22 | fixture.detectChanges();
23 | });
24 |
25 | it('should create', () => {
26 | expect(component).toBeTruthy();
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/app/registration-page/registration-page.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import {AF} from "../../providers/af";
3 | import {Router} from "@angular/router";
4 |
5 | @Component({
6 | selector: 'app-registration-page',
7 | templateUrl: './registration-page.component.html',
8 | styleUrls: ['./registration-page.component.css']
9 | })
10 | export class RegistrationPageComponent {
11 | public error: any;
12 |
13 | constructor(private afService: AF, private router: Router) { }
14 |
15 | register(event, name, email, password) {
16 | event.preventDefault();
17 | this.afService.registerUser(email, password).then((user) => {
18 | this.afService.saveUserInfoFromForm(user.uid, name, email).then(() => {
19 | this.router.navigate(['']);
20 | })
21 | .catch((error) => {
22 | this.error = error;
23 | });
24 | })
25 | .catch((error) => {
26 | this.error = error;
27 | console.log(this.error);
28 | });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HuddleHouse/firebase-crud/270f5860622bea69e7f5a8bd676439a8149cca5d/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false
8 | };
9 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HuddleHouse/firebase-crud/270f5860622bea69e7f5a8bd676439a8149cca5d/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | FirebaseCrud
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import './polyfills.ts';
2 |
3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4 | import { enableProdMode } from '@angular/core';
5 | import { environment } from './environments/environment';
6 | import { AppModule } from './app/app.module';
7 |
8 | if (environment.production) {
9 | enableProdMode();
10 | }
11 |
12 | platformBrowserDynamic().bootstrapModule(AppModule);
13 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | // This file includes polyfills needed by Angular 2 and is loaded before
2 | // the app. You can add your own extra polyfills to this file.
3 | import 'core-js/es6/symbol';
4 | import 'core-js/es6/object';
5 | import 'core-js/es6/function';
6 | import 'core-js/es6/parse-int';
7 | import 'core-js/es6/parse-float';
8 | import 'core-js/es6/number';
9 | import 'core-js/es6/math';
10 | import 'core-js/es6/string';
11 | import 'core-js/es6/date';
12 | import 'core-js/es6/array';
13 | import 'core-js/es6/regexp';
14 | import 'core-js/es6/map';
15 | import 'core-js/es6/set';
16 | import 'core-js/es6/reflect';
17 |
18 | import 'core-js/es7/reflect';
19 | import 'zone.js/dist/zone';
20 |
--------------------------------------------------------------------------------
/src/providers/af.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from "@angular/core";
2 | import {AngularFire, AuthProviders, AuthMethods, FirebaseListObservable, FirebaseObjectObservable} from 'angularfire2';
3 | import {FirebaseObjectFactoryOpts} from "angularfire2/interfaces";
4 |
5 | @Injectable()
6 | export class AF {
7 | public messages: FirebaseListObservable;
8 | public users: FirebaseListObservable;
9 | public displayName: string;
10 | public email: string;
11 | public user: FirebaseObjectObservable;
12 |
13 | constructor(public af: AngularFire) {
14 | this.af.auth.subscribe(
15 | (auth) => {
16 | if (auth != null) {
17 | this.user = this.af.database.object('users/' + auth.uid);
18 | }
19 | });
20 |
21 | this.messages = this.af.database.list('messages');
22 | this.users = this.af.database.list('users');
23 | }
24 |
25 | /**
26 | * Logs in the user
27 | * @returns {firebase.Promise}
28 | */
29 | loginWithGoogle() {
30 | return this.af.auth.login({
31 | provider: AuthProviders.Google,
32 | method: AuthMethods.Popup,
33 | });
34 | }
35 |
36 | /**
37 | * Logs out the current user
38 | */
39 | logout() {
40 | return this.af.auth.logout();
41 | }
42 |
43 | /**
44 | *
45 | */
46 | addUserInfo(){
47 | //We saved their auth info now save the rest to the db.
48 | this.users.push({
49 | email: this.email,
50 | displayName: this.displayName
51 | });
52 | }
53 |
54 | /**
55 | * Saves a message to the Firebase Realtime Database
56 | * @param text
57 | */
58 | sendMessage(text) {
59 | var message = {
60 | message: text,
61 | displayName: this.displayName,
62 | email: this.email,
63 | timestamp: Date.now()
64 | };
65 | this.messages.push(message);
66 | }
67 |
68 | /**
69 | *
70 | * @param model
71 | * @returns {firebase.Promise}
72 | */
73 | registerUser(email, password) {
74 | console.log(email)
75 | return this.af.auth.createUser({
76 | email: email,
77 | password: password
78 | });
79 |
80 |
81 | }
82 |
83 | /**
84 | *
85 | * @param uid
86 | * @param model
87 | * @returns {firebase.Promise}
88 | */
89 | saveUserInfoFromForm(uid, name, email) {
90 | return this.af.database.object('registeredUsers/' + uid).set({
91 | name: name,
92 | email: email,
93 | });
94 | }
95 |
96 | /**
97 | * Logs the user in using their Email/Password combo
98 | * @param email
99 | * @param password
100 | * @returns {firebase.Promise}
101 | */
102 | loginWithEmail(email, password) {
103 | return this.af.auth.login({
104 | email: email,
105 | password: password,
106 | },
107 | {
108 | provider: AuthProviders.Password,
109 | method: AuthMethods.Password,
110 | });
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | import './polyfills.ts';
2 |
3 | import 'zone.js/dist/long-stack-trace-zone';
4 | import 'zone.js/dist/proxy.js';
5 | import 'zone.js/dist/sync-test';
6 | import 'zone.js/dist/jasmine-patch';
7 | import 'zone.js/dist/async-test';
8 | import 'zone.js/dist/fake-async-test';
9 | import { getTestBed } from '@angular/core/testing';
10 | import {
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting
13 | } from '@angular/platform-browser-dynamic/testing';
14 |
15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
16 | declare var __karma__: any;
17 | declare var require: any;
18 |
19 | // Prevent Karma from running prematurely.
20 | __karma__.loaded = function () {};
21 |
22 | // First, initialize the Angular testing environment.
23 | getTestBed().initTestEnvironment(
24 | BrowserDynamicTestingModule,
25 | platformBrowserDynamicTesting()
26 | );
27 | // Then we find all the tests.
28 | let context = require.context('./', true, /\.spec\.ts$/);
29 | // And load the modules.
30 | context.keys().map(context);
31 | // Finally, start Karma to run the tests.
32 | __karma__.start();
33 |
--------------------------------------------------------------------------------
/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "",
4 | "declaration": false,
5 | "emitDecoratorMetadata": true,
6 | "experimentalDecorators": true,
7 | "lib": ["es6", "dom"],
8 | "mapRoot": "./",
9 | "module": "es6",
10 | "moduleResolution": "node",
11 | "outDir": "../dist/out-tsc",
12 | "sourceMap": true,
13 | "target": "es5",
14 | "typeRoots": [
15 | "../node_modules/@types"
16 | ]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/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 | "max-line-length": [
20 | true,
21 | 140
22 | ],
23 | "member-access": false,
24 | "member-ordering": [
25 | true,
26 | "static-before-instance",
27 | "variables-before-functions"
28 | ],
29 | "no-arg": true,
30 | "no-bitwise": true,
31 | "no-console": [
32 | true,
33 | "debug",
34 | "info",
35 | "time",
36 | "timeEnd",
37 | "trace"
38 | ],
39 | "no-construct": true,
40 | "no-debugger": true,
41 | "no-duplicate-variable": true,
42 | "no-empty": false,
43 | "no-eval": true,
44 | "no-inferrable-types": true,
45 | "no-shadowed-variable": true,
46 | "no-string-literal": false,
47 | "no-switch-case-fall-through": true,
48 | "no-trailing-whitespace": true,
49 | "no-unused-expression": true,
50 | "no-use-before-declare": true,
51 | "no-var-keyword": true,
52 | "object-literal-sort-keys": false,
53 | "one-line": [
54 | true,
55 | "check-open-brace",
56 | "check-catch",
57 | "check-else",
58 | "check-whitespace"
59 | ],
60 | "quotemark": [
61 | true,
62 | "single"
63 | ],
64 | "radix": true,
65 | "semicolon": [
66 | "always"
67 | ],
68 | "triple-equals": [
69 | true,
70 | "allow-null-check"
71 | ],
72 | "typedef-whitespace": [
73 | true,
74 | {
75 | "call-signature": "nospace",
76 | "index-signature": "nospace",
77 | "parameter": "nospace",
78 | "property-declaration": "nospace",
79 | "variable-declaration": "nospace"
80 | }
81 | ],
82 | "variable-name": false,
83 | "whitespace": [
84 | true,
85 | "check-branch",
86 | "check-decl",
87 | "check-operator",
88 | "check-separator",
89 | "check-type"
90 | ],
91 |
92 | "directive-selector": [true, "attribute", "app", "camelCase"],
93 | "component-selector": [true, "element", "app", "kebab-case"],
94 | "use-input-property-decorator": true,
95 | "use-output-property-decorator": true,
96 | "use-host-property-decorator": true,
97 | "no-input-rename": true,
98 | "no-output-rename": true,
99 | "use-life-cycle-interface": true,
100 | "use-pipe-transform-interface": true,
101 | "component-class-suffix": true,
102 | "directive-class-suffix": true,
103 | "no-access-missing-member": true,
104 | "templates-use-public": true,
105 | "invoke-injectable": true
106 | }
107 | }
108 |
--------------------------------------------------------------------------------