├── .angular-cli.json
├── .editorconfig
├── .gitignore
├── README.md
├── e2e
├── app.e2e-spec.ts
├── app.po.ts
└── tsconfig.e2e.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── protractor.conf.js
├── src
├── app
│ ├── app-routing.module.ts
│ ├── app.component.html
│ ├── app.component.scss
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── auth
│ │ ├── auth-routing.module.ts
│ │ ├── auth.module.ts
│ │ ├── auth.service.spec.ts
│ │ ├── auth.service.ts
│ │ ├── custom-validators.ts
│ │ ├── login
│ │ │ ├── login.component.html
│ │ │ ├── login.component.spec.ts
│ │ │ └── login.component.ts
│ │ └── register
│ │ │ ├── register.component.html
│ │ │ ├── register.component.spec.ts
│ │ │ └── register.component.ts
│ ├── change-password
│ │ ├── change-password-routing.module.ts
│ │ ├── change-password.component.html
│ │ ├── change-password.component.scss
│ │ ├── change-password.component.spec.ts
│ │ ├── change-password.component.ts
│ │ ├── change-password.module.ts
│ │ ├── change-password.service.spec.ts
│ │ └── change-password.service.ts
│ ├── core
│ │ ├── core.module.ts
│ │ ├── guards
│ │ │ ├── authenticated.guard.spec.ts
│ │ │ ├── authenticated.guard.ts
│ │ │ ├── unauthenticated.guard.spec.ts
│ │ │ └── unauthenticated.guard.ts
│ │ ├── models
│ │ │ ├── index.ts
│ │ │ └── user.ts
│ │ └── services
│ │ │ ├── api.service.spec.ts
│ │ │ ├── api.service.ts
│ │ │ ├── user.service.spec.ts
│ │ │ └── user.service.ts
│ ├── home
│ │ ├── chats.service.spec.ts
│ │ ├── chats.service.ts
│ │ ├── components
│ │ │ ├── dropdown
│ │ │ │ ├── dropdown.component.html
│ │ │ │ ├── dropdown.component.scss
│ │ │ │ ├── dropdown.component.spec.ts
│ │ │ │ └── dropdown.component.ts
│ │ │ ├── messages
│ │ │ │ ├── messages.component.html
│ │ │ │ ├── messages.component.scss
│ │ │ │ ├── messages.component.spec.ts
│ │ │ │ └── messages.component.ts
│ │ │ ├── msg-block
│ │ │ │ ├── msg-block.component.html
│ │ │ │ ├── msg-block.component.scss
│ │ │ │ ├── msg-block.component.spec.ts
│ │ │ │ └── msg-block.component.ts
│ │ │ ├── sidebar-group
│ │ │ │ ├── sidebar-group.component.html
│ │ │ │ ├── sidebar-group.component.scss
│ │ │ │ ├── sidebar-group.component.spec.ts
│ │ │ │ └── sidebar-group.component.ts
│ │ │ └── sidebar
│ │ │ │ ├── sidebar.component.html
│ │ │ │ ├── sidebar.component.scss
│ │ │ │ ├── sidebar.component.spec.ts
│ │ │ │ └── sidebar.component.ts
│ │ ├── home-routing.module.ts
│ │ ├── home.component.html
│ │ ├── home.component.scss
│ │ ├── home.component.spec.ts
│ │ ├── home.component.ts
│ │ ├── home.module.ts
│ │ └── models
│ │ │ └── index.ts
│ ├── shared
│ │ ├── components
│ │ │ └── not-found
│ │ │ │ ├── not-found.component.html
│ │ │ │ ├── not-found.component.scss
│ │ │ │ ├── not-found.component.spec.ts
│ │ │ │ └── not-found.component.ts
│ │ ├── material.module.ts
│ │ └── shared.module.ts
│ └── utils
│ │ └── index.ts
├── assets
│ └── .gitkeep
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── sass
│ ├── _common.scss
│ ├── _material.scss
│ ├── _variables.scss
│ └── styles.scss
├── test.ts
├── tsconfig.app.json
├── tsconfig.spec.json
└── typings.d.ts
├── tsconfig.json
└── tslint.json
/.angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "project": {
4 | "name": "ngslack"
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 | "polyfills": "polyfills.ts",
17 | "test": "test.ts",
18 | "tsconfig": "tsconfig.app.json",
19 | "testTsconfig": "tsconfig.spec.json",
20 | "prefix": "app",
21 | "styles": [
22 | "sass/styles.scss"
23 | ],
24 | "scripts": [],
25 | "environmentSource": "environments/environment.ts",
26 | "environments": {
27 | "dev": "environments/environment.ts",
28 | "prod": "environments/environment.prod.ts"
29 | }
30 | }
31 | ],
32 | "e2e": {
33 | "protractor": {
34 | "config": "./protractor.conf.js"
35 | }
36 | },
37 | "lint": [
38 | {
39 | "project": "src/tsconfig.app.json",
40 | "exclude": "**/node_modules/**"
41 | },
42 | {
43 | "project": "src/tsconfig.spec.json",
44 | "exclude": "**/node_modules/**"
45 | },
46 | {
47 | "project": "e2e/tsconfig.e2e.json",
48 | "exclude": "**/node_modules/**"
49 | }
50 | ],
51 | "test": {
52 | "karma": {
53 | "config": "./karma.conf.js"
54 | }
55 | },
56 | "defaults": {
57 | "styleExt": "scss",
58 | "component": {
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/.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 | /dist-server
6 | /tmp
7 | /out-tsc
8 |
9 | # dependencies
10 | /node_modules
11 |
12 | # IDEs and editors
13 | /.idea
14 | .project
15 | .classpath
16 | .c9/
17 | *.launch
18 | .settings/
19 | *.sublime-workspace
20 |
21 | # IDE - VSCode
22 | .vscode/*
23 | !.vscode/settings.json
24 | !.vscode/tasks.json
25 | !.vscode/launch.json
26 | !.vscode/extensions.json
27 |
28 | # misc
29 | /.sass-cache
30 | /connect.lock
31 | /coverage
32 | /libpeerconnection.log
33 | npm-debug.log
34 | yarn-error.log
35 | testem.log
36 | /typings
37 |
38 | # e2e
39 | /e2e/*.js
40 | /e2e/*.map
41 |
42 | # System Files
43 | .DS_Store
44 | Thumbs.db
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angular-Slack
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.7.4.
4 |
5 | ## Development server
6 |
7 | 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.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Requirements
14 |
15 | - Node >= 6.9.0
16 | - MongoDB
17 |
18 | ## Install & Run
19 |
20 | ### Frontend
21 |
22 | ```
23 | git clone ttps://github.com/Big-Silver/Angular-Slack.git
24 | cd Angular-Slack
25 | npm install
26 | npm start
27 | ```
28 |
29 | ## Demo
30 |
31 | [Angular-Slack](https://angular-slack.herokuapp.com/)
32 |
33 | ## Build
34 |
35 | 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.
36 |
37 | ## Running unit tests
38 |
39 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
40 |
41 | ## Running end-to-end tests
42 |
43 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
44 |
45 | ## Further help
46 |
47 | 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).
48 |
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 |
3 | describe('ngslack App', () => {
4 | let page: AppPage;
5 |
6 | beforeEach(() => {
7 | page = new AppPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to app!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": [
9 | "jasmine",
10 | "jasminewd2",
11 | "node"
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/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-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular/cli/plugins/karma')
14 | ],
15 | client:{
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | reports: [ 'html', 'lcovonly' ],
20 | fixWebpackSourcePaths: true
21 | },
22 | angularCli: {
23 | environment: 'dev'
24 | },
25 | reporters: ['progress', 'kjhtml'],
26 | port: 9876,
27 | colors: true,
28 | logLevel: config.LOG_INFO,
29 | autoWatch: true,
30 | browsers: ['Chrome'],
31 | singleRun: false
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-slack",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "ng serve",
8 | "build": "ng build --prod",
9 | "test": "ng test",
10 | "lint": "ng lint",
11 | "e2e": "ng e2e",
12 | "postinstall": "ng build --aot -prod"
13 | },
14 | "private": true,
15 | "dependencies": {
16 | "@angular/animations": "^5.2.0",
17 | "@angular/cdk": "^5.2.4",
18 | "@angular/cli": "~1.7.4",
19 | "@angular/common": "^5.2.0",
20 | "@angular/compiler": "^5.2.0",
21 | "@angular/compiler-cli": "^5.2.0",
22 | "@angular/core": "^5.2.0",
23 | "@angular/forms": "^5.2.0",
24 | "@angular/http": "^5.2.0",
25 | "@angular/material": "^5.2.4",
26 | "@angular/platform-browser": "^5.2.0",
27 | "@angular/platform-browser-dynamic": "^5.2.0",
28 | "@angular/router": "^5.2.0",
29 | "core-js": "^2.4.1",
30 | "express": "^4.16.3",
31 | "hammerjs": "^2.0.8",
32 | "ionicons": "^3.0.0",
33 | "lodash": "^4.17.5",
34 | "ng2-password-strength-bar": "^1.2.0",
35 | "ngx-textarea-autosize": "^1.1.1",
36 | "nodemon": "^1.18.4",
37 | "path": "^0.12.7",
38 | "rxjs": "^5.5.6",
39 | "socket.io-client": "^2.1.0",
40 | "typescript": "~2.5.3",
41 | "zone.js": "^0.8.19"
42 | },
43 | "devDependencies": {
44 | "@angular/cli": "~1.7.4",
45 | "@angular/compiler-cli": "^5.2.0",
46 | "@angular/language-service": "^5.2.0",
47 | "@types/jasmine": "~2.8.3",
48 | "@types/jasminewd2": "~2.0.2",
49 | "@types/node": "~6.0.60",
50 | "babel-cli": "^6.26.0",
51 | "codelyzer": "^4.0.1",
52 | "enhanced-resolve": "^3.3.0",
53 | "jasmine-core": "~2.8.0",
54 | "jasmine-spec-reporter": "~4.2.1",
55 | "karma": "~2.0.0",
56 | "karma-chrome-launcher": "~2.2.0",
57 | "karma-coverage-istanbul-reporter": "^1.2.1",
58 | "karma-jasmine": "~1.1.0",
59 | "karma-jasmine-html-reporter": "^0.2.2",
60 | "protractor": "~5.1.2",
61 | "ts-node": "~4.1.0",
62 | "tslint": "~5.9.1",
63 | "typescript": "~2.5.3"
64 | },
65 | "engines": {
66 | "node": "9.2.1",
67 | "npm": "5.5.1"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/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 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './e2e/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: 'e2e/tsconfig.e2e.json'
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | import { NotFoundComponent } from './shared/components/not-found/not-found.component';
5 |
6 | const routes: Routes = [
7 | {
8 | path: '',
9 | pathMatch: 'full',
10 | redirectTo: 'home'
11 | },
12 | {
13 | path: 'home',
14 | loadChildren: './home/home.module#HomeModule'
15 | },
16 | {
17 | path: 'change-password',
18 | loadChildren: './change-password/change-password.module#ChangePasswordModule'
19 | },
20 | {
21 | path: '',
22 | loadChildren: './auth/auth.module#AuthModule'
23 | },
24 | {
25 | path: '404',
26 | component: NotFoundComponent
27 | },
28 | {
29 | path: '**',
30 | redirectTo: '/404'
31 | }
32 | ];
33 |
34 | @NgModule({
35 | imports: [RouterModule.forRoot(routes)],
36 | exports: [RouterModule]
37 | })
38 | export class AppRoutingModule { }
39 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Big-Silver/Angular-Slack/c43f2571c447289cd1f77f7b68bd4c0d1380f0a5/src/app/app.component.scss
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 | describe('AppComponent', () => {
4 | beforeEach(async(() => {
5 | TestBed.configureTestingModule({
6 | declarations: [
7 | AppComponent
8 | ],
9 | }).compileComponents();
10 | }));
11 | it('should create the app', async(() => {
12 | const fixture = TestBed.createComponent(AppComponent);
13 | const app = fixture.debugElement.componentInstance;
14 | expect(app).toBeTruthy();
15 | }));
16 | it(`should have as title 'app'`, async(() => {
17 | const fixture = TestBed.createComponent(AppComponent);
18 | const app = fixture.debugElement.componentInstance;
19 | expect(app.title).toEqual('app');
20 | }));
21 | it('should render title in a h1 tag', async(() => {
22 | const fixture = TestBed.createComponent(AppComponent);
23 | fixture.detectChanges();
24 | const compiled = fixture.debugElement.nativeElement;
25 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
26 | }));
27 | });
28 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.scss']
7 | })
8 | export class AppComponent {
9 | title = 'app';
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
4 |
5 | import { AppComponent } from './app.component';
6 |
7 | import { AppRoutingModule } from './app-routing.module';
8 | import { CoreModule } from './core/core.module';
9 | import { SharedModule } from './shared/shared.module';
10 |
11 | @NgModule({
12 | declarations: [
13 | AppComponent
14 | ],
15 | imports: [
16 | BrowserModule,
17 | BrowserAnimationsModule,
18 | AppRoutingModule,
19 | CoreModule,
20 | SharedModule
21 | ],
22 | providers: [],
23 | bootstrap: [AppComponent]
24 | })
25 | export class AppModule { }
26 |
--------------------------------------------------------------------------------
/src/app/auth/auth-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | import { LoginComponent } from './login/login.component';
5 | import { RegisterComponent } from './register/register.component';
6 | import { UnauthenticatedGuard } from '../core/guards/unauthenticated.guard';
7 |
8 | const routes: Routes = [
9 | {
10 | path: 'login',
11 | component: LoginComponent,
12 | canActivate: [UnauthenticatedGuard]
13 | },
14 | {
15 | path: 'register',
16 | component: RegisterComponent,
17 | canActivate: [UnauthenticatedGuard]
18 | }
19 | ];
20 |
21 | @NgModule({
22 | imports: [RouterModule.forChild(routes)],
23 | exports: [RouterModule]
24 | })
25 | export class AuthRoutingModule { }
26 |
--------------------------------------------------------------------------------
/src/app/auth/auth.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { ReactiveFormsModule } from '@angular/forms';
4 |
5 | import { AuthRoutingModule } from './auth-routing.module';
6 | import { MaterialModule } from '../shared/material.module';
7 |
8 | import { LoginComponent } from './login/login.component';
9 | import { RegisterComponent } from './register/register.component';
10 |
11 | import { AuthService } from './auth.service';
12 |
13 | @NgModule({
14 | imports: [
15 | CommonModule,
16 | ReactiveFormsModule,
17 | AuthRoutingModule,
18 | MaterialModule
19 | ],
20 | declarations: [
21 | LoginComponent,
22 | RegisterComponent
23 | ],
24 | providers: [
25 | AuthService
26 | ]
27 | })
28 | export class AuthModule { }
29 |
--------------------------------------------------------------------------------
/src/app/auth/auth.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { AuthService } from './auth.service';
4 |
5 | describe('AuthService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [AuthService]
9 | });
10 | });
11 |
12 | it('should be created', inject([AuthService], (service: AuthService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/app/auth/auth.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { ApiService } from '../core/services/api.service';
3 | import { UserService } from '../core/services/user.service';
4 |
5 | @Injectable()
6 | export class AuthService {
7 |
8 | constructor(
9 | private api: ApiService,
10 | private user: UserService
11 | ) { }
12 |
13 | login(data) {
14 | return this.api.login(data)
15 | .map((res: any) => {
16 | this.user.setUser(res.user.username, res.user.email, res.token);
17 | return true;
18 | });
19 | }
20 |
21 | register(data) {
22 | return this.api.register(data)
23 | .map((res: any) => {
24 | this.user.setUser(res.user.username, res.user.email, res.token);
25 | return true;
26 | });
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/auth/custom-validators.ts:
--------------------------------------------------------------------------------
1 | import {AbstractControl} from '@angular/forms';
2 | export class CustomValidators {
3 | static matchPassword(AC: AbstractControl) {
4 | let password = AC.get('password').value;
5 | let confirmPassword = AC.get('confirm').value;
6 |
7 | if(password != confirmPassword) {
8 | AC.get('confirm').setErrors( {matchpassword: true} )
9 | } else {
10 | return null
11 | }
12 | }
13 |
14 | static passwordStrength(AC: AbstractControl) {
15 | let password = AC.get('password').value;
16 |
17 | const letter = /[A-Za-z]+/g;
18 | const number = /\d+/g;
19 | const special = /[!@#$%^&*()\[\]{}<>\/]/g;
20 |
21 | if (!(letter.exec(password) && number.exec(password) && special.exec(password))) {
22 | AC.get('password').setErrors({passwordStrength: true});
23 | }
24 | return null;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/auth/login/login.component.html:
--------------------------------------------------------------------------------
1 |
25 |
--------------------------------------------------------------------------------
/src/app/auth/login/login.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { LoginComponent } from './login.component';
4 |
5 | describe('LoginComponent', () => {
6 | let component: LoginComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ LoginComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(LoginComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/auth/login/login.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { FormGroup, FormBuilder, Validators } from '@angular/forms';
3 | import { Router } from '@angular/router';
4 | import { MatSnackBar } from '@angular/material';
5 |
6 | import { AuthService } from '../auth.service';
7 | import { handleError } from '../../utils';
8 |
9 | @Component({
10 | selector: 'app-login',
11 | templateUrl: './login.component.html',
12 | })
13 | export class LoginComponent implements OnInit {
14 |
15 | loginForm: FormGroup;
16 | loading = false;
17 |
18 | constructor(
19 | private fb: FormBuilder,
20 | private auth: AuthService,
21 | private router: Router,
22 | private snackBar: MatSnackBar
23 | ) { }
24 |
25 | ngOnInit() {
26 | this.loginForm = this.fb.group({
27 | email: ['', Validators.compose([
28 | Validators.required, Validators.email
29 | ])],
30 | password: ['', Validators.compose([
31 | Validators.required, Validators.minLength(8)
32 | ])]
33 | });
34 | }
35 |
36 | submit() {
37 | if (!this.loading) {
38 | this.loading = true;
39 |
40 | this.auth.login(this.loginForm.value)
41 | .subscribe(
42 | success => {
43 | this.router.navigate(['/home']);
44 | },
45 | handleError(this.snackBar),
46 | () => { this.loading = false; }
47 | );
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/app/auth/register/register.component.html:
--------------------------------------------------------------------------------
1 |
34 |
--------------------------------------------------------------------------------
/src/app/auth/register/register.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { RegisterComponent } from './register.component';
4 |
5 | describe('RegisterComponent', () => {
6 | let component: RegisterComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ RegisterComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(RegisterComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/auth/register/register.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { FormGroup, FormBuilder, Validators } from '@angular/forms';
3 | import { Router } from '@angular/router';
4 | import { MatSnackBar } from '@angular/material';
5 | import { pick } from 'lodash';
6 |
7 | import { CustomValidators } from '../custom-validators';
8 | import { AuthService } from '../auth.service';
9 | import { handleError } from '../../utils';
10 |
11 | @Component({
12 | selector: 'app-register',
13 | templateUrl: './register.component.html',
14 | })
15 | export class RegisterComponent implements OnInit {
16 |
17 | registerForm: FormGroup;
18 |
19 | constructor(
20 | private fb: FormBuilder,
21 | private auth: AuthService,
22 | private router: Router,
23 | private snackBar: MatSnackBar
24 | ) { }
25 |
26 | ngOnInit() {
27 | this.registerForm = this.fb.group({
28 | email: ['', Validators.compose([
29 | Validators.required, Validators.email
30 | ])],
31 | username: ['', Validators.required],
32 | password: ['', Validators.compose([
33 | Validators.required, Validators.minLength(8)
34 | ])],
35 | confirm: ''
36 | }, {
37 | validator: CustomValidators.matchPassword
38 | });
39 | }
40 |
41 | submit() {
42 | this.auth.register(
43 | pick(this.registerForm.value, ['email', 'username', 'password'])
44 | ).subscribe(
45 | success => {
46 | this.router.navigate(['/home']);
47 | },
48 | handleError(this.snackBar)
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/app/change-password/change-password-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | import { ChangePasswordComponent } from './change-password.component';
5 |
6 | const routes: Routes = [
7 | {
8 | path: '',
9 | component: ChangePasswordComponent
10 | }
11 | ];
12 |
13 | @NgModule({
14 | imports: [RouterModule.forChild(routes)],
15 | exports: [RouterModule]
16 | })
17 | export class ChangePasswordRoutingModule { }
18 |
--------------------------------------------------------------------------------
/src/app/change-password/change-password.component.html:
--------------------------------------------------------------------------------
1 |
2 |
Change password
3 |
4 |
29 |
30 |
--------------------------------------------------------------------------------
/src/app/change-password/change-password.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Big-Silver/Angular-Slack/c43f2571c447289cd1f77f7b68bd4c0d1380f0a5/src/app/change-password/change-password.component.scss
--------------------------------------------------------------------------------
/src/app/change-password/change-password.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ChangePasswordComponent } from './change-password.component';
4 |
5 | describe('ChangePasswordComponent', () => {
6 | let component: ChangePasswordComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ ChangePasswordComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ChangePasswordComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/change-password/change-password.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { FormGroup, FormBuilder, Validators } from '@angular/forms';
3 | import { CustomValidators } from '../auth/custom-validators';
4 | import { ChangePasswordService } from './change-password.service';
5 | import { MatSnackBar } from '@angular/material';
6 |
7 | @Component({
8 | selector: 'app-change-password',
9 | templateUrl: './change-password.component.html',
10 | styleUrls: ['./change-password.component.scss']
11 | })
12 | export class ChangePasswordComponent implements OnInit {
13 |
14 | passwordForm: FormGroup;
15 | public barLabel: string = '';
16 | public myColors = ['#DD2C00', '#FF6D00', '#FFD600', '#AEEA00', '#00C853'];
17 |
18 | constructor(private fb: FormBuilder, private service: ChangePasswordService, private snackBar: MatSnackBar) { }
19 |
20 | ngOnInit() {
21 | this.passwordForm = this.fb.group({
22 | oldPassword: '',
23 | password: ['', Validators.compose([
24 | Validators.required,
25 | Validators.minLength(6)
26 | ])],
27 | confirm: ''
28 | }, {
29 | validator: Validators.compose([
30 | CustomValidators.matchPassword,
31 | CustomValidators.passwordStrength
32 | ])
33 | });
34 | }
35 |
36 |
37 | submit() {
38 | this.service.change(this.passwordForm.value)
39 | .subscribe(rst => {
40 | if (rst.success) {
41 | this.snackBar.open('Password changed successfully!', '', {
42 | duration: 3000,
43 | horizontalPosition: 'right',
44 | verticalPosition: 'top',
45 | panelClass: 'welcome-msg'
46 | });
47 | }
48 | }, err => {
49 | this.snackBar.open('Password change failed!', '', {
50 | duration: 3000,
51 | horizontalPosition: 'right',
52 | verticalPosition: 'top',
53 | panelClass: 'error-msg'
54 | });
55 | });
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/app/change-password/change-password.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { ReactiveFormsModule } from '@angular/forms';
3 | import { CommonModule } from '@angular/common';
4 | import { HttpModule } from '@angular/http';
5 | import { PasswordStrengthBarModule } from 'ng2-password-strength-bar';
6 |
7 | import { MaterialModule } from '../shared/material.module';
8 |
9 | import { ChangePasswordRoutingModule } from './change-password-routing.module';
10 | import { ChangePasswordComponent } from './change-password.component';
11 |
12 | import { ChangePasswordService } from './change-password.service';
13 |
14 | @NgModule({
15 | imports: [
16 | CommonModule,
17 | ReactiveFormsModule,
18 | ChangePasswordRoutingModule,
19 | MaterialModule,
20 | HttpModule,
21 | PasswordStrengthBarModule
22 | ],
23 | declarations: [ChangePasswordComponent],
24 | providers: [ChangePasswordService]
25 | })
26 | export class ChangePasswordModule { }
27 |
--------------------------------------------------------------------------------
/src/app/change-password/change-password.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { ChangePasswordService } from './change-password.service';
4 |
5 | describe('ChangePasswordService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [ChangePasswordService]
9 | });
10 | });
11 |
12 | it('should be created', inject([ChangePasswordService], (service: ChangePasswordService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/app/change-password/change-password.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Http } from '@angular/http';
3 |
4 | @Injectable()
5 | export class ChangePasswordService {
6 |
7 | constructor(private http: Http) { }
8 |
9 | change(form) {
10 | console.log(form);
11 |
12 | return this.http.post('http://192.168.0.27:8080/changepwd', {
13 | oldpwd: form.oldPassword,
14 | newpwd: form.password
15 | })
16 | .map(res => res.json());
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/core/core.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { HttpClientModule } from '@angular/common/http';
4 |
5 | import { ApiService } from './services/api.service';
6 | import { UserService } from './services/user.service';
7 | import { AuthenticatedGuard } from './guards/authenticated.guard';
8 | import { UnauthenticatedGuard } from './guards/unauthenticated.guard';
9 |
10 | @NgModule({
11 | imports: [
12 | CommonModule,
13 | HttpClientModule
14 | ],
15 | declarations: [],
16 | providers: [
17 | ApiService,
18 | UserService,
19 | AuthenticatedGuard,
20 | UnauthenticatedGuard
21 | ]
22 | })
23 | export class CoreModule { }
24 |
--------------------------------------------------------------------------------
/src/app/core/guards/authenticated.guard.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async, inject } from '@angular/core/testing';
2 |
3 | import { AuthenticatedGuard } from './authenticated.guard';
4 |
5 | describe('AuthenticatedGuard', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [AuthenticatedGuard]
9 | });
10 | });
11 |
12 | it('should ...', inject([AuthenticatedGuard], (guard: AuthenticatedGuard) => {
13 | expect(guard).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/app/core/guards/authenticated.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
3 | import { Observable } from 'rxjs/Observable';
4 | import { UserService } from '../services/user.service';
5 |
6 | @Injectable()
7 | export class AuthenticatedGuard implements CanActivate {
8 | constructor(private user: UserService, private router: Router) {}
9 |
10 | canActivate(
11 | next: ActivatedRouteSnapshot,
12 | state: RouterStateSnapshot
13 | ): Observable | Promise | boolean {
14 |
15 | if (this.user.isLoggedIn()) {
16 | return true;
17 | }
18 |
19 | this.router.navigate(['/login']);
20 |
21 | return false;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/core/guards/unauthenticated.guard.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async, inject } from '@angular/core/testing';
2 |
3 | import { UnauthenticatedGuard } from './unauthenticated.guard';
4 |
5 | describe('UnauthenticatedGuard', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [UnauthenticatedGuard]
9 | });
10 | });
11 |
12 | it('should ...', inject([UnauthenticatedGuard], (guard: UnauthenticatedGuard) => {
13 | expect(guard).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/app/core/guards/unauthenticated.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
3 | import { Observable } from 'rxjs/Observable';
4 | import { UserService } from '../services/user.service';
5 |
6 | @Injectable()
7 | export class UnauthenticatedGuard implements CanActivate {
8 | constructor(private user: UserService, private router: Router) {}
9 |
10 | canActivate(
11 | next: ActivatedRouteSnapshot,
12 | state: RouterStateSnapshot
13 | ): Observable | Promise | boolean {
14 | if (!this.user.isLoggedIn()) {
15 | return true;
16 | }
17 |
18 | this.router.navigate(['/home']);
19 |
20 | return this.user.isLoggedIn();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/app/core/models/index.ts:
--------------------------------------------------------------------------------
1 | export * from './user';
2 |
--------------------------------------------------------------------------------
/src/app/core/models/user.ts:
--------------------------------------------------------------------------------
1 | export class User {
2 | username: string;
3 | email: string;
4 | token: string;
5 |
6 | isLoggedIn() {
7 | return this.token !== '';
8 | }
9 |
10 | constructor(username = '', email = '', token = '') {
11 | this.username = username;
12 | this.email = email;
13 | this.token = token;
14 | }
15 |
16 | cloneWithToken(token: string) {
17 | return new User(this.username, this.email, token);
18 | }
19 |
20 | cloneWithInfo(username: string, email: string) {
21 | return new User(username, email, this.token);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/core/services/api.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { ApiService } from './api.service';
4 |
5 | describe('ApiService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [ApiService]
9 | });
10 | });
11 |
12 | it('should be created', inject([ApiService], (service: ApiService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/app/core/services/api.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { HttpClient, HttpHeaders } from '@angular/common/http';
3 | import { environment } from '../../../environments/environment.prod';
4 |
5 | const baseURL = environment.baseURL;
6 |
7 | @Injectable()
8 | export class ApiService {
9 |
10 | constructor(private httpClient: HttpClient ) { }
11 |
12 | login(form) {
13 | return this.post('/auth/login', form);
14 | }
15 |
16 | register(form) {
17 | return this.post('/auth/register', form);
18 | }
19 |
20 | validate(token) {
21 | return this.get('/auth/validate', token);
22 | }
23 |
24 | getChats(token) {
25 | return this.get('/chats', token);
26 | }
27 |
28 | private get(url, token = '') {
29 | if (token) {
30 | const httpOptions = {
31 | headers: new HttpHeaders({
32 | 'Content-Type': 'application/json',
33 | 'x-access-token': token
34 | })
35 | };
36 |
37 | return this.httpClient.get(`${baseURL}${url}`, httpOptions);
38 | }
39 |
40 | return this.httpClient.get(`${baseURL}${url}`);
41 | }
42 |
43 | private post(url, data, token = '') {
44 | if (token) {
45 | const httpOptions = {
46 | headers: new HttpHeaders({
47 | 'Content-Type': 'application/json',
48 | 'x-access-token': token
49 | })
50 | };
51 |
52 | return this.httpClient.post(`${baseURL}${url}`, data, httpOptions);
53 | }
54 |
55 | return this.httpClient.post(`${baseURL}${url}`, data);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/app/core/services/user.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { UserService } from './user.service';
4 |
5 | describe('UserService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [UserService]
9 | });
10 | });
11 |
12 | it('should be created', inject([UserService], (service: UserService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/app/core/services/user.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { BehaviorSubject } from 'rxjs/BehaviorSubject';
3 | import { Router } from '@angular/router';
4 |
5 | import { User } from '../models';
6 | import { ApiService } from './api.service';
7 |
8 | @Injectable()
9 | export class UserService {
10 | user: BehaviorSubject = new BehaviorSubject(new User());
11 |
12 | constructor(private api: ApiService, private router: Router) {
13 | const token = localStorage.getItem('token');
14 | if (token) {
15 | const user = new User('', '', token);
16 | this.user = new BehaviorSubject(user);
17 |
18 | api.validate(token)
19 | .subscribe(
20 | (res: any) => {
21 | const { username, email } = res.user;
22 | const newUser = new User(username, email, token);
23 | this.user.next(newUser);
24 | },
25 | err => {
26 | console.log(err);
27 | this.resetUser();
28 | }
29 | );
30 | }
31 | }
32 |
33 | setUser(username: string, email: string, token: string) {
34 | localStorage.setItem('token', token);
35 | const newUser = new User(username, email, token);
36 | this.user.next(newUser);
37 | }
38 |
39 | resetUser() {
40 | localStorage.removeItem('token');
41 | this.user.next(new User());
42 | this.router.navigate(['/login']);
43 | }
44 |
45 | isLoggedIn() {
46 | return this.user.value.isLoggedIn();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/app/home/chats.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { ChatsService } from './chats.service';
4 |
5 | describe('ChatsService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [ChatsService]
9 | });
10 | });
11 |
12 | it('should be created', inject([ChatsService], (service: ChatsService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/app/home/chats.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { MatSnackBar } from '@angular/material';
3 | import { ApiService } from '../core/services/api.service';
4 | import { UserService } from '../core/services/user.service';
5 | import { User } from '../core/models';
6 | import * as socketIo from 'socket.io-client';
7 | import { BehaviorSubject } from 'rxjs/BehaviorSubject';
8 | import { Message } from './models';
9 | import { environment } from '../../environments/environment.prod';
10 |
11 | const baseURL = environment.baseURL;
12 |
13 | @Injectable()
14 | export class ChatsService {
15 | msgs = new BehaviorSubject([]);
16 | isSend = new BehaviorSubject(false);
17 |
18 | private socket;
19 | private user: User;
20 | private isConnected: Boolean;
21 | private interval;
22 |
23 | constructor(
24 | private api: ApiService,
25 | private userService: UserService,
26 | private snackBar: MatSnackBar,
27 | ) {
28 | this.userService.user.subscribe(_user => { this.user = _user; });
29 | this.isConnected = true;
30 | this.interval = null;
31 | }
32 |
33 | getChats() {
34 | this.api.getChats(this.user.token)
35 | .subscribe((messages: Message[]) => {
36 | this.msgs.next(messages);
37 | });
38 | }
39 |
40 | initSocket() {
41 | this.socket = socketIo(baseURL, { query: 'token=' + this.user.token });
42 |
43 | this.socket.on('connect_failed', () => {
44 | this.socket.close();
45 | });
46 |
47 | this.socket.on('disconnect', () => {
48 | this.isConnected = false;
49 | this.connectionFailMessage();
50 | this.isSend.next(false);
51 | this.interval = window.setInterval(() => {
52 | if (this.isConnected) {
53 | clearInterval(this.interval);
54 | this.interval = null;
55 | return;
56 | }
57 | this.socket.connect();
58 | }, 5000);
59 | // this.socket.close();
60 | });
61 |
62 | this.socket.on('receive', (data: { msg: Message }) => {
63 | this.msgs.next([
64 | ...this.msgs.value,
65 | data.msg
66 | ]);
67 | });
68 |
69 | this.socket.on('connect', () => {
70 | this.isSend.next(true);
71 | if (!this.isConnected) {
72 | this.connectionSuccessMessage();
73 | }
74 | this.isConnected = true;
75 | this.getChats();
76 | });
77 | }
78 |
79 | sendMessage(text) {
80 | this.socket.emit('send', text);
81 | }
82 |
83 | disconnect() {
84 | this.socket.disconnect();
85 | }
86 |
87 | private connectionFailMessage() {
88 | setTimeout(() => {
89 | this.snackBar.open(`Try to reconnect.`, 'close', {
90 | duration: 10000,
91 | horizontalPosition: 'right',
92 | verticalPosition: 'top',
93 | panelClass: 'error-msg'
94 | });
95 | });
96 | }
97 |
98 | private connectionSuccessMessage() {
99 | setTimeout(() => {
100 | this.snackBar.open(`The network was reconnected.`, 'close', {
101 | duration: 10000,
102 | horizontalPosition: 'right',
103 | verticalPosition: 'top',
104 | panelClass: 'welcome-msg'
105 | });
106 | });
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/app/home/components/dropdown/dropdown.component.html:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 | {{ item.label }}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/app/home/components/dropdown/dropdown.component.scss:
--------------------------------------------------------------------------------
1 | .dropdown {
2 | position: relative;
3 | flex: none;
4 |
5 | .dd-header {
6 | background: #3E313C;
7 | box-shadow: inset 0 0 1px #000000;
8 | display: flex;
9 | font-size: 20px;
10 | font-weight: bold;
11 | justify-content: space-between;
12 | padding: 15px 20px;
13 | cursor: pointer;
14 | }
15 |
16 | .dd-content {
17 | background: #3E313C;
18 | box-sizing: border-box;
19 | font-size: 1.2em;
20 | padding: 10px 20px;
21 | position: absolute;
22 | top: 0;
23 | transform: translateY(-100%);
24 | width: 100%;
25 |
26 | .dd-item {
27 | cursor: pointer;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/app/home/components/dropdown/dropdown.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { DropdownComponent } from './dropdown.component';
4 |
5 | describe('DropdownComponent', () => {
6 | let component: DropdownComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ DropdownComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(DropdownComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/home/components/dropdown/dropdown.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
2 |
3 | import { MenuDir, MenuItem } from '../../models';
4 |
5 | @Component({
6 | selector: 'app-dropdown',
7 | templateUrl: './dropdown.component.html',
8 | styleUrls: ['./dropdown.component.scss']
9 | })
10 | export class DropdownComponent implements OnInit {
11 | @Input() title = 'Title';
12 | @Input() showArrow = false;
13 | @Input() menus: Array