├── src
├── assets
│ ├── .gitkeep
│ ├── __MACOSX
│ │ ├── ._images
│ │ └── images
│ │ │ ├── ._buffet.png
│ │ │ ├── ._logo.png
│ │ │ ├── ._alberto.png
│ │ │ ├── ._vadonut.png
│ │ │ ├── ._uthappizza.png
│ │ │ ├── ._zucchipakoda.png
│ │ │ ├── ._.DS_Store
│ │ │ └── ._elaicheesecake.png
│ └── _db284e833226b010f3e252d9220f85d5_images.zip
├── app
│ ├── app.component.scss
│ ├── home
│ │ ├── home.component.scss
│ │ ├── home.component.spec.ts
│ │ ├── home.component.ts
│ │ └── home.component.html
│ ├── aboutus
│ │ ├── aboutus.component.scss
│ │ ├── aboutus.component.spec.ts
│ │ ├── aboutus.component.ts
│ │ └── aboutus.component.html
│ ├── login
│ │ ├── login.component.scss
│ │ ├── login.component.spec.ts
│ │ ├── login.component.ts
│ │ └── login.component.html
│ ├── shared
│ │ ├── baseurl.ts
│ │ ├── comment.ts
│ │ ├── leader.ts
│ │ ├── promotion.ts
│ │ ├── restConfig.ts
│ │ ├── feedback.ts
│ │ ├── dish.ts
│ │ ├── promotions.ts
│ │ ├── leaders.ts
│ │ └── dishes.ts
│ ├── menu
│ │ ├── menu.component.scss
│ │ ├── menu.component.ts
│ │ ├── menu.component.html
│ │ └── menu.component.spec.ts
│ ├── contact
│ │ ├── contact.component.scss
│ │ ├── contact.component.spec.ts
│ │ ├── contact.component.ts
│ │ └── contact.component.html
│ ├── app.component.ts
│ ├── directives
│ │ ├── highlight.directive.spec.ts
│ │ └── highlight.directive.ts
│ ├── footer
│ │ ├── footer.component.ts
│ │ ├── footer.component.spec.ts
│ │ ├── footer.component.scss
│ │ └── footer.component.html
│ ├── app.component.html
│ ├── dishdetail
│ │ ├── dishdetail.component.scss
│ │ ├── dishdetail.component.spec.ts
│ │ ├── dishdetail.component.html
│ │ └── dishdetail.component.ts
│ ├── services
│ │ ├── dish.service.spec.ts
│ │ ├── leader.service.spec.ts
│ │ ├── feedback.service.spec.ts
│ │ ├── promotion.service.spec.ts
│ │ ├── process-httpmsg.service.spec.ts
│ │ ├── feedback.service.ts
│ │ ├── process-httpmsg.service.ts
│ │ ├── leader.service.ts
│ │ ├── promotion.service.ts
│ │ └── dish.service.ts
│ ├── app-routing
│ │ ├── app-routing.module.ts
│ │ └── routes.ts
│ ├── header
│ │ ├── header.component.scss
│ │ ├── header.component.ts
│ │ ├── header.component.spec.ts
│ │ └── header.component.html
│ ├── app.component.spec.ts
│ ├── animations
│ │ └── app.animation.ts
│ └── app.module.ts
├── favicon.ico
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── illstruation_img
│ └── Template.png
├── typings.d.ts
├── tsconfig.app.json
├── tsconfig.spec.json
├── main.ts
├── index.html
├── test.ts
├── styles.scss
└── polyfills.ts
├── .editorconfig
├── e2e
├── tsconfig.e2e.json
├── app.po.ts
└── app.e2e-spec.ts
├── tsconfig.json
├── .gitignore
├── protractor.conf.js
├── karma.conf.js
├── .angular-cli.json
├── package.json
├── tslint.json
└── README.md
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/home/home.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/aboutus/aboutus.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/login/login.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/shared/baseurl.ts:
--------------------------------------------------------------------------------
1 |
2 | export const baseURL = 'http://localhost:3000/';
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NH1900/Online_Restaurant/HEAD/src/favicon.ico
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/src/app/menu/menu.component.scss:
--------------------------------------------------------------------------------
1 | .container{
2 | color:black;
3 | margin:20px;
4 | display:flex;
5 | }
--------------------------------------------------------------------------------
/src/assets/__MACOSX/._images:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NH1900/Online_Restaurant/HEAD/src/assets/__MACOSX/._images
--------------------------------------------------------------------------------
/src/illstruation_img/Template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NH1900/Online_Restaurant/HEAD/src/illstruation_img/Template.png
--------------------------------------------------------------------------------
/src/assets/__MACOSX/images/._buffet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NH1900/Online_Restaurant/HEAD/src/assets/__MACOSX/images/._buffet.png
--------------------------------------------------------------------------------
/src/assets/__MACOSX/images/._logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NH1900/Online_Restaurant/HEAD/src/assets/__MACOSX/images/._logo.png
--------------------------------------------------------------------------------
/src/app/shared/comment.ts:
--------------------------------------------------------------------------------
1 | export class Comment{
2 | rating:number;
3 | comment:string;
4 | author:string;
5 | date:string;
6 | }
--------------------------------------------------------------------------------
/src/assets/__MACOSX/images/._alberto.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NH1900/Online_Restaurant/HEAD/src/assets/__MACOSX/images/._alberto.png
--------------------------------------------------------------------------------
/src/assets/__MACOSX/images/._vadonut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NH1900/Online_Restaurant/HEAD/src/assets/__MACOSX/images/._vadonut.png
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /* SystemJS module definition */
2 | declare var module: NodeModule;
3 | interface NodeModule {
4 | id: string;
5 | }
6 |
--------------------------------------------------------------------------------
/src/assets/__MACOSX/images/._uthappizza.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NH1900/Online_Restaurant/HEAD/src/assets/__MACOSX/images/._uthappizza.png
--------------------------------------------------------------------------------
/src/assets/__MACOSX/images/._zucchipakoda.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NH1900/Online_Restaurant/HEAD/src/assets/__MACOSX/images/._zucchipakoda.png
--------------------------------------------------------------------------------
/src/assets/__MACOSX/images/._.DS_Store:
--------------------------------------------------------------------------------
1 | Mac OS X 2 F x ATTR x x
--------------------------------------------------------------------------------
/src/assets/__MACOSX/images/._elaicheesecake.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NH1900/Online_Restaurant/HEAD/src/assets/__MACOSX/images/._elaicheesecake.png
--------------------------------------------------------------------------------
/src/assets/_db284e833226b010f3e252d9220f85d5_images.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NH1900/Online_Restaurant/HEAD/src/assets/_db284e833226b010f3e252d9220f85d5_images.zip
--------------------------------------------------------------------------------
/src/app/shared/leader.ts:
--------------------------------------------------------------------------------
1 | export class Leader{
2 | id:number;
3 | name:string;
4 | image:string;
5 | designation:string;
6 | abbr:string;
7 | featured:boolean;
8 | description:string;
9 | }
--------------------------------------------------------------------------------
/src/app/shared/promotion.ts:
--------------------------------------------------------------------------------
1 | export class Promotion{
2 | id:number;
3 | name:string;
4 | image:string;
5 | label:string;
6 | price:string;
7 | featured:boolean;
8 | description:string;
9 | }
--------------------------------------------------------------------------------
/src/app/shared/restConfig.ts:
--------------------------------------------------------------------------------
1 |
2 | import { baseURL } from './baseurl';
3 |
4 | // Function for settting the default restangular configuration
5 | export function RestangularConfigFactory (RestangularProvider) {
6 | RestangularProvider.setBaseUrl(baseURL);
7 | }
--------------------------------------------------------------------------------
/src/app/contact/contact.component.scss:
--------------------------------------------------------------------------------
1 | .full-width {
2 | width: 95%
3 | }
4 |
5 | .half-width {
6 | width: 45%
7 | }
8 |
9 | .form-size {
10 | width: 75%
11 | }
12 |
13 | .form-size{
14 | color:black;
15 | background-color:lightblue;
16 | }
--------------------------------------------------------------------------------
/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/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "baseUrl": "./",
6 | "module": "es2015",
7 | "types": []
8 | },
9 | "exclude": [
10 | "test.ts",
11 | "**/*.spec.ts"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/src/app/shared/feedback.ts:
--------------------------------------------------------------------------------
1 | export class Feedback {
2 | firstname: string;
3 | lastname: string;
4 | telnum: number;
5 | email: string;
6 | agree: boolean;
7 | contacttype: string;
8 | message: string;
9 | };
10 |
11 | export const ContactType = ['None', 'Tel', 'Email'];
--------------------------------------------------------------------------------
/src/app/directives/highlight.directive.spec.ts:
--------------------------------------------------------------------------------
1 | import { HighlightDirective } from './highlight.directive';
2 |
3 | describe('HighlightDirective', () => {
4 | it('should create an instance', () => {
5 | // const directive = new HighlightDirective();
6 | //expect(directive).toBeTruthy();
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/src/app/shared/dish.ts:
--------------------------------------------------------------------------------
1 | import {Comment} from './comment';
2 |
3 | export class Dish{
4 | id:number;
5 | name:string;
6 | image:string;
7 | category:string;
8 | label:string;
9 | price:string;
10 | featured:boolean;
11 | description:string;
12 | comments: Comment[];
13 | }
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/app/footer/footer.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-footer',
5 | templateUrl: './footer.component.html',
6 | styleUrls: ['./footer.component.scss']
7 | })
8 | export class FooterComponent implements OnInit {
9 |
10 | constructor() { }
11 |
12 | ngOnInit() {
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "types": [
8 | "jasmine",
9 | "node"
10 | ]
11 | },
12 | "files": [
13 | "test.ts"
14 | ],
15 | "include": [
16 | "**/*.spec.ts",
17 | "**/*.d.ts"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/dishdetail/dishdetail.component.scss:
--------------------------------------------------------------------------------
1 | h2{
2 | color:black;
3 | margin:20px;
4 | display:flex;
5 | }
6 |
7 | p{
8 | color:black;
9 | margin:20px;
10 | display:flex;
11 | }
12 |
13 | .form-size{
14 | color:black;
15 | background-color:lightblue;
16 | }
17 |
18 | .full-width {
19 | width: 95%
20 | }
21 |
22 | .form{
23 | background-color:lightblue;
24 | color:black;
25 | }
26 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.log(err));
13 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist/out-tsc",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "target": "es5",
11 | "typeRoots": [
12 | "node_modules/@types"
13 | ],
14 | "lib": [
15 | "es2017",
16 | "dom"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/services/dish.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { DishService } from './dish.service';
4 |
5 | describe('DishService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [DishService]
9 | });
10 | });
11 |
12 | it('should be created', inject([DishService], (service: DishService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ConFusion
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/app/services/leader.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { LeaderService } from './leader.service';
4 |
5 | describe('LeaderService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [LeaderService]
9 | });
10 | });
11 |
12 | it('should be created', inject([LeaderService], (service: LeaderService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/app/services/feedback.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { FeedbackService } from './feedback.service';
4 |
5 | describe('FeedbackService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [FeedbackService]
9 | });
10 | });
11 |
12 | it('should be created', inject([FeedbackService], (service: FeedbackService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo(link: string) {
5 | return browser.get(link);
6 | }
7 |
8 | getParagraphText(selector: string) {
9 | return element(by.css(selector)).getText();
10 | }
11 |
12 | getElement(selector: string) {
13 | return element(by.css(selector));
14 | }
15 |
16 | getAllElements(selector: string) {
17 | return element.all(by.css(selector));
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/services/promotion.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { PromotionService } from './promotion.service';
4 |
5 | describe('PromotionService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [PromotionService]
9 | });
10 | });
11 |
12 | it('should be created', inject([PromotionService], (service: PromotionService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/app/services/process-httpmsg.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { ProcessHttpmsgService } from './process-httpmsg.service';
4 |
5 | describe('ProcessHttpmsgService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [ProcessHttpmsgService]
9 | });
10 | });
11 |
12 | it('should be created', inject([ProcessHttpmsgService], (service: ProcessHttpmsgService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/app/shared/promotions.ts:
--------------------------------------------------------------------------------
1 | import {Promotion} from './promotion';
2 |
3 | export const PROMOTIONS: Promotion[] = [{
4 | id: 0,
5 | name: 'Weekend Grand Buffet',
6 | image: '/assets/images/buffet.png',
7 | label: 'New',
8 | price: '19.99',
9 | featured: true,
10 | description: 'Featuring mouthwatering combinations with a choice of five different salads, six enticing appetizers, six main entrees and five choicest desserts. Free flowing bubbly and soft drinks. All for just $19.99 per person'
11 | }];
--------------------------------------------------------------------------------
/src/app/app-routing/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import {RouterModule, Routes} from '@angular/router';
4 |
5 | import {routes} from './routes';
6 |
7 |
8 | @NgModule({
9 | imports: [
10 | CommonModule,
11 | RouterModule.forRoot(routes)
12 | ],
13 | //we want to use this app routes module in app module so we add a exports decorator to this module
14 | exports:[RouterModule],
15 | declarations: []
16 | })
17 | export class AppRoutingModule { }
18 |
--------------------------------------------------------------------------------
/src/app/header/header.component.scss:
--------------------------------------------------------------------------------
1 | $lt-gray: #ddd;
2 | $background-moredark:#4527A0;
3 | $background-dark: #512DA8;
4 | $background-light: #9575CD;
5 | $background-pale: #D1C4E9;
6 |
7 | @mixin zero-margin($pad-up-down, $pad-left-right) {
8 | margin: 0px auto;
9 | padding: $pad-up-down $pad-left-right;
10 | }
11 |
12 | .jumbotron {
13 | @include zero-margin(70px,30px);
14 | background: $background-light ;
15 | color:floralwhite;
16 | min-height: 100px;
17 | max-height: 120px;
18 | }
19 |
20 | .active{
21 | background: $background-moredark;
22 | }
--------------------------------------------------------------------------------
/src/app/directives/highlight.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, ElementRef, Renderer2, HostListener } from '@angular/core';
2 |
3 | @Directive({
4 | selector: '[appHighlight]'
5 | })
6 | export class HighlightDirective {
7 |
8 | constructor(private el: ElementRef,
9 | private renderer: Renderer2) { }
10 |
11 | @HostListener('mouseenter') onMouseEnter() {
12 | this.renderer.addClass(this.el.nativeElement, 'highlight');
13 | }
14 |
15 | @HostListener('mouseleave') onMouseLeave() {
16 | this.renderer.removeClass(this.el.nativeElement, 'highlight');
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/header/header.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { LoginComponent } from '../login/login.component';
3 | import { MdDialog, MdDialogRef } from '@angular/material';
4 |
5 | @Component({
6 | selector: 'app-header',
7 | templateUrl: './header.component.html',
8 | styleUrls: ['./header.component.scss']
9 | })
10 | export class HeaderComponent implements OnInit {
11 |
12 |
13 | constructor(public dialog:MdDialog) { }
14 |
15 | ngOnInit() {
16 | }
17 |
18 | openLoginForm() {
19 | this.dialog.open(LoginComponent, {width: '500px', height: '450px'});
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/services/feedback.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable } from 'rxjs/Observable';
3 | import { Feedback } from '../shared/feedback';
4 | //这个傻逼of要单独加。
5 | import 'rxjs/add/observable/of';
6 | import 'rxjs/add/operator/delay';
7 |
8 | import { ProcessHttpmsgService } from './process-httpmsg.service';
9 | //import 'rxjs/add/operator/catch';
10 | import { RestangularModule, Restangular } from 'ngx-restangular';
11 | @Injectable()
12 | export class FeedbackService {
13 |
14 | constructor(private restangular: Restangular,
15 | private processHTTPMsgService: ProcessHttpmsgService) { }
16 |
17 | submitFeedback(fb: Feedback){
18 | this.restangular.all('feedback').post(fb);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /menu\.component\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/app/home/home.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { HomeComponent } from './home.component';
4 |
5 | describe('HomeComponent', () => {
6 | let component: HomeComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ HomeComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(HomeComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/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/footer/footer.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { FooterComponent } from './footer.component';
4 |
5 | describe('FooterComponent', () => {
6 | let component: FooterComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ FooterComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(FooterComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/header/header.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { HeaderComponent } from './header.component';
4 |
5 | describe('HeaderComponent', () => {
6 | let component: HeaderComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ HeaderComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(HeaderComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/aboutus/aboutus.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AboutusComponent } from './aboutus.component';
4 |
5 | describe('AboutusComponent', () => {
6 | let component: AboutusComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ AboutusComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(AboutusComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/contact/contact.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ContactComponent } from './contact.component';
4 |
5 | describe('ContactComponent', () => {
6 | let component: ContactComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ ContactComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ContactComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/dishdetail/dishdetail.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { DishdetailComponent } from './dishdetail.component';
4 |
5 | describe('DishdetailComponent', () => {
6 | let component: DishdetailComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ DishdetailComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(DishdetailComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/app-routing/routes.ts:
--------------------------------------------------------------------------------
1 | import {Routes} from '@angular/router';
2 |
3 | import { MenuComponent } from '../menu/menu.component';
4 | import { DishdetailComponent } from '../dishdetail/dishdetail.component';
5 | import { HomeComponent } from '../home/home.component';
6 | import { AboutusComponent } from '../aboutus/aboutus.component';
7 | import { ContactComponent } from '../contact/contact.component';
8 |
9 | export const routes: Routes = [
10 | {path:'home',component:HomeComponent},
11 | {path:'menu',component:MenuComponent},
12 | //deflaut location if there is no URL provided
13 | {path: 'dishdetail/:id',component: DishdetailComponent},
14 | {path: 'contactus',component:ContactComponent},
15 | {path: 'aboutus', component:AboutusComponent},
16 | {path:'',redirectTo:'/home',pathMatch:'full'}
17 | ];
18 |
--------------------------------------------------------------------------------
/src/app/footer/footer.component.scss:
--------------------------------------------------------------------------------
1 | $lt-gray: #ddd;
2 | $background-dark: #512DA8;
3 | $background-light: #9575CD;
4 | $background-pale: #D1C4E9;
5 |
6 | @mixin zero-margin($pad-up-down, $pad-left-right) {
7 | margin: 0px auto;
8 | padding: $pad-up-down $pad-left-right;
9 | }
10 |
11 | .footer{
12 | background-color: $background-pale;
13 | @include zero-margin(20px, 0px);
14 | }
15 |
16 | .btn-facebook {color:#fff!important; background-color:#3b5998!important;}
17 | .btn-google-plus{color:#fff!important;background-color:#dd4b39!important;}
18 | .btn-youtube{color:#fff!important;background-color:#ff4b39!important;}
19 | .btn-linkedin{color:#fff!important;background-color:#007bb6!important;}
20 | .btn-twitter{color:#fff!important;background-color:#55acee!important;}
21 | .btn-mail{color:#fff!important;background-color:#512DA8!important;}
--------------------------------------------------------------------------------
/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/login/login.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | import {MdDialog, MdDialogRef} from '@angular/material';
4 |
5 |
6 | @Component({
7 | selector: 'app-login',
8 | templateUrl: './login.component.html',
9 | styleUrls: ['./login.component.scss']
10 | })
11 | export class LoginComponent implements OnInit {
12 |
13 | user = {username: '', password: '', remember: false};
14 | //create the a object of dialog (reference) to login component
15 | constructor(public dialogRef: MdDialogRef) { }
16 |
17 | ngOnInit() {
18 | }
19 |
20 | //we can check console content in browser Javascript consoler
21 | onSubmit() {
22 | //put value to console
23 | console.log("User: ", this.user);
24 | //invoke close method to close the dialog
25 | this.dialogRef.close();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/app/aboutus/aboutus.component.ts:
--------------------------------------------------------------------------------
1 | import { Leader } from '../shared/leader';
2 | import { LeaderService } from '../services/leader.service';
3 | import { flyInOut,expand } from '../animations/app.animation';
4 | import { Component, OnInit ,Inject} from '@angular/core';
5 |
6 | @Component({
7 | selector: 'app-aboutus',
8 | templateUrl: './aboutus.component.html',
9 | styleUrls: ['./aboutus.component.scss'],
10 | host: {
11 | '[@flyInOut]': 'true',
12 | 'style': 'display: block;'
13 | },
14 | animations: [
15 | flyInOut(),
16 | expand()
17 | ]
18 | })
19 | export class AboutusComponent implements OnInit {
20 | leaders: Leader[];
21 | leaderErrMess: string;
22 | constructor(private leaderService:LeaderService,
23 | @Inject('BaseURL') private BaseURL) { }
24 |
25 | ngOnInit() {
26 | this.leaderService.getLeaders().subscribe(leaders => this.leaders = leaders,errmess => this.leaderErrMess = errmess);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/app/menu/menu.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit ,Inject } from '@angular/core';
2 | import{Dish} from '../shared/dish';
3 | //import{DISHES} from '../shared/dishes';
4 | import {DishService} from '../services/dish.service';
5 | import { flyInOut, expand} from '../animations/app.animation';
6 |
7 | @Component({
8 | selector: 'app-menu',
9 | templateUrl: './menu.component.html',
10 | styleUrls: ['./menu.component.scss'],
11 | host: {
12 | '[@flyInOut]': 'true',
13 | 'style': 'display: block;'
14 | },
15 | animations: [
16 | flyInOut(),
17 | expand()
18 | ]
19 | })
20 | export class MenuComponent implements OnInit {
21 |
22 | dishes: Dish[];
23 | errMess: string;
24 |
25 | constructor(private dishService: DishService,
26 | @Inject('BaseURL') private BaseURL) { }
27 |
28 | ngOnInit() {
29 | this.dishService.getDishes()
30 | .subscribe(dishes => this.dishes = dishes,
31 | errmess => this.errMess = errmess);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/app/services/process-httpmsg.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable } from 'rxjs/Observable';
3 | import { Http, Response } from '@angular/http';
4 | import 'rxjs/add/observable/throw';
5 |
6 | @Injectable()
7 | export class ProcessHttpmsgService {
8 |
9 | constructor() { }
10 |
11 | public extractData(res: Response) {
12 | let body = res.json();
13 | console.log(body);
14 | return body || { };
15 | }
16 |
17 | public handleError (error: Response | any) {
18 | // In a real world app, you might use a remote logging infrastructure
19 | let errMsg: string;
20 | if (error instanceof Response) {
21 | const body = error.json() || '';
22 | const err = body.error || JSON.stringify(body);
23 | errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
24 | } else {
25 | errMsg = error.message ? error.message : error.toString();
26 | }
27 | console.error(errMsg);
28 | return Observable.throw(errMsg);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 | import { browser } from 'protractor';
3 | describe('con-fusion App', () => {
4 | let page: AppPage;
5 |
6 | beforeEach(() => {
7 | page = new AppPage();
8 | });
9 |
10 | it('should display message saying Ristorante Con Fusion', () => {
11 | page.navigateTo('/');
12 | expect(page.getParagraphText('app-root h1')).toEqual('NH1900 Con Fusion');
13 | });
14 |
15 | it('should navigate to about us page by clicking on the link', () => {
16 | page.navigateTo('/');
17 |
18 | let navlink = page.getAllElements('a').get(1);
19 | navlink.click();
20 |
21 | expect(page.getParagraphText('h3')).toBe('About Us')
22 | });
23 |
24 | it('should enter a new comment for the first dish', () => {
25 | page.navigateTo('/dishdetail/0');
26 |
27 | let newAuthor = page.getElement('input[type=text]');
28 | newAuthor.sendKeys('Test Author');
29 |
30 | let newComment = page.getElement('textarea');
31 | newComment.sendKeys('Test Comment');
32 |
33 | let newSubmitButton = page.getElement("button[type=submit]");
34 | newSubmitButton.click();
35 |
36 | browser.pause();
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 | @import '~@angular/material/prebuilt-themes/pink-bluegrey.css';
3 | $primary-color-pale:gray;
4 | $primary-color-dark:#312DA8;
5 | $primary-color:#673AB7;
6 | $primary-color-light:#D1C4E9;
7 | $primary-color-text:#FFFFFF;
8 | $accent-color:#FFC107;
9 | $primary-text-color:#212121;
10 | $secondary-text-color:#757575;
11 | $divider-color:#BDBDBD;
12 | @mixin zero-margin($pad-up-down, $pad-left-right){
13 | margin:0px auto;
14 | padding: $pad-up-down $pad-lefft-right;
15 | }
16 |
17 | body{
18 | padding: 0;
19 | margin: 0;
20 | font-family: 'Times New Roman', Times, serif;
21 | }
22 |
23 | .container{
24 | color:black;
25 | margin:20px;
26 | display:flex;
27 | }
28 |
29 | .background-primary{
30 | background-color: $primary-text-color!important;
31 | }
32 |
33 | .background-accent{
34 | background-color:$accent-color!important;
35 | }
36 |
37 | .text-floral-white{
38 | color:floralwhite!important;
39 | }
40 |
41 | .text-spacer{
42 | flex:1 1 auto;
43 | }
44 |
45 | .highlight {
46 | background-color: $primary-color-pale;
47 | border: 1px solid $primary-color-dark;
48 | z-index: 1;
49 | transform: scale(1.01);
50 | }
--------------------------------------------------------------------------------
/src/app/animations/app.animation.ts:
--------------------------------------------------------------------------------
1 | import { trigger, state, style, animate, transition } from '@angular/animations';
2 |
3 | export function visibility() {
4 | return trigger('visibility', [
5 | state('shown', style({
6 | transform: 'scale(1.0)',
7 | opacity: 1
8 | })),
9 | state('hidden', style({
10 | transform: 'scale(0.5)',
11 | opacity: 0
12 | })),
13 | transition('* => *', animate('0.5s ease-in-out'))
14 | ]);
15 | }
16 |
17 | export function flyInOut() {
18 | return trigger('flyInOut',[
19 | state('*', style({ opacity: 1, transform: 'translateX(0)'})),
20 | transition(':enter', [
21 | style({ transform: 'translateX(-100%)', opacity:0 }),
22 | animate('500ms ease-in')
23 | ]),
24 | transition(':leave', [
25 | animate('500ms ease-out', style({ transform: 'translateX(100%)', opacity: 0}))
26 | ])
27 | ]);
28 | }
29 |
30 | export function expand() {
31 | return trigger('expand', [
32 | state('*', style({ opacity: 1, transform: 'translateX(0)' })),
33 | transition(':enter', [
34 | style({ transform: 'translateY(-50%)', opacity:0 }),
35 | animate('200ms ease-in', style({ opacity: 1, transform: 'translateX(0)' }))
36 | ])
37 | ]);
38 | }
--------------------------------------------------------------------------------
/.angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "project": {
4 | "name": "con-fusion"
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 | "styles.scss",
23 | "../node_modules/font-awesome/scss/font-awesome.scss"
24 | ],
25 | "scripts": [],
26 | "environmentSource": "environments/environment.ts",
27 | "environments": {
28 | "dev": "environments/environment.ts",
29 | "prod": "environments/environment.prod.ts"
30 | }
31 | }
32 | ],
33 | "e2e": {
34 | "protractor": {
35 | "config": "./protractor.conf.js"
36 | }
37 | },
38 | "lint": [
39 | {
40 | "project": "src/tsconfig.app.json",
41 | "exclude": "**/node_modules/**"
42 | },
43 | {
44 | "project": "src/tsconfig.spec.json",
45 | "exclude": "**/node_modules/**"
46 | },
47 | {
48 | "project": "e2e/tsconfig.e2e.json",
49 | "exclude": "**/node_modules/**"
50 | }
51 | ],
52 | "test": {
53 | "karma": {
54 | "config": "./karma.conf.js"
55 | }
56 | },
57 | "defaults": {
58 | "styleExt": "scss",
59 | "component": {}
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/app/header/header.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Home
4 | About
5 | Menu
6 | Contact
7 |
8 | Login
9 |
10 |
11 |
20 |
21 |
22 |
23 |
NH1900 Con Fusion
24 |
We take inspiration from the World's best cuisines, and create a unique fusion experience. Our lipsmacking creations
25 | will tickle your culinary senses!
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/app/menu/menu.component.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | {{dishx.name | uppercase}}
15 |
16 |
17 |
18 |
19 |
20 |
Loading . . . Please Wait
21 |
22 |
23 |
Error
24 | {{errMess}}
25 |
26 |
27 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/app/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit ,Inject} from '@angular/core';
2 |
3 | import { Dish } from '../shared/dish';
4 | import { DishService } from '../services/dish.service';
5 | import { Promotion } from '../shared/promotion';
6 | import { PromotionService } from '../services/promotion.service';
7 | import { Leader } from '../shared/leader';
8 | import { LeaderService } from '../services/leader.service';
9 | import { flyInOut, expand } from '../animations/app.animation';
10 | @Component({
11 | selector: 'app-home',
12 | templateUrl: './home.component.html',
13 | styleUrls: ['./home.component.scss'],
14 | host: {
15 | '[@flyInOut]': 'true',
16 | 'style': 'display: block;'
17 | },
18 | animations: [
19 | flyInOut(),
20 | expand()
21 | ]
22 | })
23 | export class HomeComponent implements OnInit {
24 |
25 | leader: Leader;
26 | dish: Dish;
27 | promotion: Promotion;
28 | dishErrMess: string;
29 | promotionErrMess: string;
30 | leaderErrMess: string;
31 | constructor(private dishservice: DishService,
32 | private promotionservice: PromotionService,
33 | private leaderservice: LeaderService,
34 | @Inject('BaseURL') private BaseURL) { }
35 |
36 | ngOnInit() {
37 | this.dishservice.getFeaturedDish().subscribe( dish => this.dish = dish,errmess => this.dishErrMess = errmess);
38 | this.promotionservice.getFeaturedPromotion().subscribe(promotion => this.promotion = promotion,errmess => this.promotionErrMess = errmess);
39 | this.leaderservice.getFeaturedLeader().subscribe(leader => this.leader = leader,errmess => this.leaderErrMess = errmess);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/app/services/leader.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Leader } from '../shared/leader';
3 | import { LEADERS } from '../shared/leaders';
4 |
5 | import { Observable } from 'rxjs/Observable';
6 | //这个傻逼of要单独加。
7 | import 'rxjs/add/observable/of';
8 | import 'rxjs/add/operator/delay';
9 |
10 | import { ProcessHttpmsgService } from './process-httpmsg.service';
11 | //import 'rxjs/add/operator/catch';
12 | import { RestangularModule, Restangular } from 'ngx-restangular';
13 | @Injectable()
14 | export class LeaderService {
15 |
16 | constructor(private restangular: Restangular,
17 | private processHTTPMsgService: ProcessHttpmsgService) { }
18 |
19 | // can write as getLeaders(): Leader[] no space between "getLeaders():" and "Leader[]"
20 | /*getLeaders():Promise{
21 | return Promise.resolve(LEADERS);
22 | }
23 |
24 | getFeaturedLeader():Promise{
25 | return Promise.resolve(LEADERS.filter((leader) => (leader.featured))[0]);
26 | }*/
27 |
28 | //test latency using pormise
29 | /*
30 | getLeaders():Promise{
31 | return new Promise(resolve=> {
32 | // Simulate server latency with 2 second delay
33 | setTimeout(() => resolve(LEADERS), 2000);
34 | });
35 | }*/
36 |
37 | getFeaturedLeader():Observable{
38 | return this.restangular.all('leaders').getList({featured: true})
39 | .map(leaders => leaders[0]);
40 | //Observable.of(LEADERS.filter((leader) => (leader.featured))[0]).delay(2000);
41 | }
42 |
43 | getLeaders():Observable{
44 | return this.restangular.all('leaders').getList();
45 | //Observable.of(LEADERS).delay(2000);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "con-fusion",
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 | },
13 | "private": true,
14 | "dependencies": {
15 | "@angular/animations": "^5.2.6",
16 | "@angular/cdk": "^2.0.0-beta.8",
17 | "@angular/common": "^5.2.0",
18 | "@angular/compiler": "^5.2.0",
19 | "@angular/core": "^5.2.0",
20 | "@angular/flex-layout": "^5.0.0-beta.13",
21 | "@angular/forms": "^5.2.0",
22 | "@angular/http": "^5.2.0",
23 | "@angular/material": "^2.0.0-beta.8",
24 | "@angular/platform-browser": "^5.2.0",
25 | "@angular/platform-browser-dynamic": "^5.2.0",
26 | "@angular/router": "^5.2.0",
27 | "core-js": "^2.4.1",
28 | "font-awesome": "^4.7.0",
29 | "hammerjs": "^2.0.8",
30 | "ngx-restangular": "^2.0.2",
31 | "rxjs": "^5.5.6",
32 | "zone.js": "^0.8.19"
33 | },
34 | "devDependencies": {
35 | "@angular/cli": "~1.7.1",
36 | "@angular/compiler-cli": "^5.2.0",
37 | "@angular/language-service": "^5.2.0",
38 | "@types/jasmine": "~2.8.3",
39 | "@types/jasminewd2": "~2.0.2",
40 | "@types/node": "~6.0.60",
41 | "codelyzer": "^4.0.1",
42 | "jasmine-core": "~2.8.0",
43 | "jasmine-spec-reporter": "~4.2.1",
44 | "karma": "~2.0.0",
45 | "karma-chrome-launcher": "~2.2.0",
46 | "karma-coverage-istanbul-reporter": "^1.2.1",
47 | "karma-jasmine": "~1.1.0",
48 | "karma-jasmine-html-reporter": "^0.2.2",
49 | "protractor": "~5.1.2",
50 | "ts-node": "~4.1.0",
51 | "tslint": "~5.9.1",
52 | "typescript": "~2.5.3"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/app/login/login.component.html:
--------------------------------------------------------------------------------
1 |
2 | Login
3 |
4 | ×
5 |
6 |
7 | {{ user | json }}
8 |
9 |
--------------------------------------------------------------------------------
/src/app/home/home.component.html:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 | {{dish.name | uppercase}}
13 |
14 |
15 |
16 |
17 | {{dish.description}}
18 |
19 |
20 |
21 |
22 |
Loading . . . Please Wait
23 |
24 |
25 |
Error
26 | {{errMess}}
27 |
28 |
29 |
30 |
31 |
32 |
33 | {{promotion.name | uppercase}}
34 |
35 |
36 |
37 |
38 | {{promotion.description}}
39 |
40 |
41 |
42 |
43 |
Loading . . . Please Wait
44 |
45 |
46 |
Error
47 | {{errMess}}
48 |
49 |
50 |
51 |
52 |
53 |
54 | {{leader.name | uppercase}}
55 |
56 |
57 |
58 |
59 | {{leader.description}}
60 |
61 |
62 |
63 |
64 |
Loading . . . Please Wait
65 |
66 |
67 |
Error
68 | {{errMess}}
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/app/shared/leaders.ts:
--------------------------------------------------------------------------------
1 | import {Leader} from './leader';
2 |
3 | export const LEADERS: Leader[] = [
4 | {
5 | id: 0,
6 | name: 'Peter Pan',
7 | image: '/assets/images/Taki.jpg',
8 | designation: 'Chief Epicurious Officer',
9 | abbr: 'CEO',
10 | featured: false,
11 | description: "Our CEO, Peter, credits his hardworking East Asian immigrant parents who undertook the arduous journey to the shores of America with the intention of giving their children the best future. His mother's wizardy in the kitchen whipping up the tastiest dishes with whatever is available inexpensively at the supermarket, was his first inspiration to create the fusion cuisines for which The Frying Pan became well known. He brings his zeal for fusion cuisines to this restaurant, pioneering cross-cultural culinary connections."
12 | },
13 | {
14 | id: 1,
15 | name: 'Dhanasekaran Witherspoon',
16 | image: '/assets/images/Taki.jpg',
17 | designation: 'Chief Food Officer',
18 | abbr: 'CFO',
19 | featured: false,
20 | description: 'Our CFO, Danny, as he is affectionately referred to by his colleagues, comes from a long established family tradition in farming and produce. His experiences growing up on a farm in the Australian outback gave him great appreciation for varieties of food sources. As he puts it in his own words, Everything that runs, wins, and everything that stays, pays!'
21 | },
22 | {
23 | id: 2,
24 | name: 'Agumbe Tang',
25 | image: '/assets/images/Taki.jpg',
26 | designation: 'Chief Taste Officer',
27 | abbr: 'CTO',
28 | featured: false,
29 | description: 'Blessed with the most discerning gustatory sense, Agumbe, our CFO, personally ensures that every dish that we serve meets his exacting tastes. Our chefs dread the tongue lashing that ensues if their dish does not meet his exacting standards. He lives by his motto, You click only if you survive my lick.'
30 | },
31 | {
32 | id: 3,
33 | name: 'Taki',
34 | image: '/assets/images/Taki.jpg',
35 | designation: 'Executive Chef',
36 | abbr: 'EC',
37 | featured: true,
38 | description: 'The most attractive role in film Your Name'
39 | }
40 | ];
--------------------------------------------------------------------------------
/src/app/footer/footer.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/menu/menu.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 | import { MaterialModule } from '@angular/material';
3 | import { FlexLayoutModule } from '@angular/flex-layout';
4 | import { Dish } from '../shared/dish';
5 | import { DishService } from '../services/dish.service';
6 | import { DISHES } from '../shared/dishes';
7 | import { baseURL } from '../shared/baseurl';
8 | import { Observable } from 'rxjs/Observable';
9 | import { MenuComponent } from './menu.component';
10 | import { RouterTestingModule } from '@angular/router/testing';
11 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
12 | import { By } from '@angular/platform-browser';
13 | import { DebugElement } from '@angular/core';
14 |
15 | describe('MenuComponent', () => {
16 | let component: MenuComponent;
17 | let fixture: ComponentFixture;
18 |
19 | beforeEach(async(() => {
20 |
21 | let dishServiceStub = {
22 | getDishes: function(): Observable {
23 | return Observable.of(DISHES);
24 | }
25 | };
26 |
27 | TestBed.configureTestingModule({
28 | imports: [ BrowserAnimationsModule,
29 | MaterialModule,
30 | FlexLayoutModule,
31 | RouterTestingModule.withRoutes([{ path: 'menu', component: MenuComponent }])
32 | ],
33 | declarations: [ MenuComponent ],
34 | providers: [
35 | { provide: DishService, useValue: dishServiceStub },
36 | { provide: 'BaseURL', useValue: baseURL },
37 | ]
38 | })
39 | .compileComponents();
40 |
41 | let dishservice = TestBed.get(DishService);
42 |
43 | }));
44 |
45 | beforeEach(() => {
46 | fixture = TestBed.createComponent(MenuComponent);
47 | component = fixture.componentInstance;
48 | fixture.detectChanges();
49 | });
50 |
51 | it('should create', () => {
52 | expect(component).toBeTruthy();
53 | });
54 |
55 | it('dishes items should be 4', () => {
56 | expect(component.dishes.length).toBe(4);
57 | expect(component.dishes[1].name).toBe('Zucchipakoda');
58 | expect(component.dishes[3].featured).toBeFalsy();
59 | });
60 |
61 | it('should use dishes in the template', () => {
62 | fixture.detectChanges();
63 |
64 | let de: DebugElement;
65 | let el: HTMLElement;
66 | de = fixture.debugElement.query(By.css('h1'));
67 | el = de.nativeElement;
68 |
69 | expect(el.textContent).toContain(DISHES[0].name.toUpperCase());
70 |
71 | });
72 | });
73 |
--------------------------------------------------------------------------------
/src/app/services/promotion.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | import {Promotion} from '../shared/promotion';
4 | //import {PROMOTIONS} from '../shared/promotions';
5 |
6 | import { Observable } from 'rxjs/Observable';
7 | //这个傻逼of要单独加。
8 | import 'rxjs/add/observable/of';
9 | import 'rxjs/add/operator/delay';
10 | import 'rxjs/add/operator/map';
11 | import { Http, Response } from '@angular/http';
12 | //import { baseURL } from '../shared/baseurl';
13 | import { ProcessHttpmsgService } from './process-httpmsg.service';
14 | //import 'rxjs/add/operator/catch';
15 | import { RestangularModule, Restangular } from 'ngx-restangular';
16 | @Injectable()
17 | export class PromotionService {
18 |
19 | constructor(private restangular: Restangular,
20 | private processHTTPMsgService: ProcessHttpmsgService) { }
21 |
22 | /*getPromotions():Promise{
23 | return Promise.resolve(PROMOTIONS);
24 | }
25 |
26 | getPromotion(id: number):Promise{
27 | return Promise.resolve(PROMOTIONS.filter((promo) => (promo.id === id))[0]);
28 | }
29 |
30 | getFeaturedPromotion():Promise{
31 | //the filter will return a sub array of dishes so we point [0] to output only one element
32 | return Promise.resolve(PROMOTIONS.filter((promo) => (promo.featured))[0]);
33 | }*/
34 |
35 | //test latency using promise
36 | /*
37 | getPromotions():Promise{
38 | return new Promise(resolve=> {
39 | // Simulate server latency with 2 second delay
40 | setTimeout(() => resolve(PROMOTIONS), 2000);
41 | });
42 | }
43 |
44 | getPromotion(id: number):Promise{
45 | return new Promise(resolve=> {
46 | // Simulate server latency with 2 second delay
47 | setTimeout(() => resolve(PROMOTIONS.filter((promo) => (promo.id === id))[0]), 2000);
48 | });
49 | }
50 |
51 | getFeaturedPromotion():Promise{
52 | //the filter will return a sub array of dishes so we point [0] to output only one element
53 | return new Promise(resolve=> {
54 | // Simulate server latency with 2 second delay
55 | setTimeout(() => resolve(PROMOTIONS.filter((promo) => (promo.featured))[0]), 2000);
56 | });
57 | }
58 | */
59 |
60 | getPromotions():Observable{
61 | return this.restangular.all('promotions').getList();
62 | }
63 |
64 | getPromotion(id: number):Observable{
65 | return this.restangular.one('promotions',id).get();
66 | }
67 |
68 | getFeaturedPromotion():Observable{
69 | return this.restangular.all('promotions').getList({featured: true})
70 | .map(promotions => promotions[0]);
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
3 | import { MaterialModule } from '@angular/material';
4 | import { FlexLayoutModule } from '@angular/flex-layout';
5 | import { NgModule } from '@angular/core';
6 | import { HttpModule } from '@angular/http';
7 |
8 | import 'hammerjs';
9 |
10 | import { AppComponent } from './app.component';
11 | //after installation thie menu component is automatically imported into app
12 | import { MenuComponent } from './menu/menu.component';
13 | import { DishdetailComponent } from './dishdetail/dishdetail.component';
14 | import { HeaderComponent } from './header/header.component';
15 | import { FooterComponent } from './footer/footer.component';
16 | import { HomeComponent } from './home/home.component';
17 | import { AboutusComponent } from './aboutus/aboutus.component';
18 | import { ContactComponent } from './contact/contact.component';
19 | import { ReactiveFormsModule } from '@angular/forms';
20 |
21 | import { DishService } from './services/dish.service';
22 | import { PromotionService } from './services/promotion.service';
23 | import { LeaderService } from './services/leader.service';
24 | import { FeedbackService } from './services/feedback.service';
25 |
26 | import { AppRoutingModule } from './app-routing/app-routing.module';
27 | import { LoginComponent } from './login/login.component';
28 | import { FormsModule } from '@angular/forms';
29 | import { baseURL } from './shared/baseurl';
30 | import { ProcessHttpmsgService } from './services/process-httpmsg.service';
31 | import { RestangularModule, Restangular } from 'ngx-restangular';
32 | import { RestangularConfigFactory } from './shared/restConfig';
33 | import { HighlightDirective } from './directives/highlight.directive';
34 |
35 |
36 | @NgModule({
37 | declarations: [
38 | AppComponent,
39 | MenuComponent,
40 | DishdetailComponent,
41 | HeaderComponent,
42 | FooterComponent,
43 | HomeComponent,
44 | ContactComponent,
45 | LoginComponent,
46 | AboutusComponent,
47 | HighlightDirective
48 | ],
49 | imports: [
50 | BrowserAnimationsModule,
51 | MaterialModule,
52 | FlexLayoutModule,
53 | BrowserModule,
54 | AppRoutingModule,
55 | FormsModule,
56 | HttpModule,
57 | RestangularModule.forRoot(RestangularConfigFactory),
58 | ReactiveFormsModule
59 | ],
60 | entryComponents:[
61 | LoginComponent
62 | ],
63 | providers: [DishService,PromotionService,LeaderService,ProcessHttpmsgService,FeedbackService,
64 | {provide: 'BaseURL', useValue: baseURL}],
65 | bootstrap: [AppComponent]
66 | })
67 | export class AppModule { }
68 |
--------------------------------------------------------------------------------
/src/app/aboutus/aboutus.component.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
About Us
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
Our History
16 |
Started in 2018, NH1900
17 |
The restaurant traces its humble beginnings to The Frying Pan
18 |
19 |
20 |
21 |
22 |
23 | Facts At a Glance
24 |
25 |
26 |
27 |
28 | Started
29 | 3 Feb. 2013
30 | Major Stake Holder
31 | HK Fine Foods Inc.
32 | Last Year's Turnover
33 | $1,250,375
34 | Employees
35 | 40
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | You better cut the pizza in four pieces because
47 | I'm not hungry enough to eat six.
48 | -- Yogi Berra,
49 | The Wit and Wisdom of Yogi Berra,
50 | P. Pepe, Diversion Books, 2014
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
Corporate Leadership
59 |
60 |
61 |
62 |
63 | {{leader.name | uppercase}}
64 |
65 |
66 |
Loading . . . Please Wait
67 |
68 |
69 |
Error
70 | {{errMess}}
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
38 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
39 |
40 | /** IE10 and IE11 requires the following for the Reflect API. */
41 | // import 'core-js/es6/reflect';
42 |
43 |
44 | /** Evergreen browsers require these. **/
45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
46 | import 'core-js/es7/reflect';
47 |
48 |
49 | /**
50 | * Required to support Web Animations `@angular/platform-browser/animations`.
51 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
52 | **/
53 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
54 |
55 | /**
56 | * By default, zone.js will patch all possible macroTask and DomEvents
57 | * user can disable parts of macroTask/DomEvents patch by setting following flags
58 | */
59 |
60 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
61 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
62 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
63 |
64 | /*
65 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
66 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
67 | */
68 | // (window as any).__Zone_enable_cross_context_check = true;
69 |
70 | /***************************************************************************************************
71 | * Zone JS is required by default for Angular itself.
72 | */
73 | import 'zone.js/dist/zone'; // Included with Angular CLI.
74 |
75 |
76 |
77 | /***************************************************************************************************
78 | * APPLICATION IMPORTS
79 | */
80 |
--------------------------------------------------------------------------------
/src/app/contact/contact.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { FormBuilder, FormGroup, Validators } from '@angular/forms';
3 | import { Feedback, ContactType } from '../shared/feedback';
4 | import { flyInOut } from '../animations/app.animation';
5 | import { FeedbackService } from '../services/feedback.service';
6 |
7 | @Component({
8 | selector: 'app-contact',
9 | templateUrl: './contact.component.html',
10 | styleUrls: ['./contact.component.scss'],
11 | host: {
12 | '[@flyInOut]': 'true',
13 | 'style': 'display: block;'
14 | },
15 | animations: [
16 | flyInOut()
17 | ]
18 | })
19 | export class ContactComponent implements OnInit {
20 |
21 | formErrors = {
22 | 'firstname': '',
23 | 'lastname': '',
24 | 'telnum': '',
25 | 'email': ''
26 | };
27 | validationMessages = {
28 | 'firstname': {
29 | 'required': 'First Name is required.',
30 | 'minlength': 'First Name must be at least 2 characters long.',
31 | 'maxlength': 'FirstName cannot be more than 25 characters long.'
32 | },
33 | 'lastname': {
34 | 'required': 'Last Name is required.',
35 | 'minlength': 'Last Name must be at least 2 characters long.',
36 | 'maxlength': 'Last Name cannot be more than 25 characters long.'
37 | },
38 | 'telnum': {
39 | 'required': 'Tel. number is required.',
40 | 'pattern': 'Tel. number must contain only numbers.'
41 | },
42 | 'email': {
43 | 'required': 'Email is required.',
44 | 'email': 'Email not in valid format.'
45 | },
46 | };
47 | feedbackForm: FormGroup;
48 | feedback: Feedback;
49 | contactType = ContactType;
50 | constructor(private fb: FormBuilder,private fbservice: FeedbackService) {
51 | this.createForm();
52 | }
53 |
54 | ngOnInit() {
55 | }
56 |
57 | createForm() {
58 | this.feedbackForm = this.fb.group({
59 | firstname: ['',[Validators.required, Validators.minLength(2), Validators.maxLength(25)] ],
60 | lastname: ['',[Validators.required, Validators.minLength(2), Validators.maxLength(25)] ],
61 | telnum: ['',[Validators.required, Validators.pattern] ],
62 | email: ['', [Validators.required, Validators.email] ],
63 | agree: false,
64 | contacttype: 'None',
65 | message: ''
66 | });
67 | this.feedbackForm.valueChanges
68 | .subscribe(data => this.onValueChanged(data));
69 |
70 | this.onValueChanged(); // (re)set validation messages now
71 | }
72 |
73 | onValueChanged(data?: any) {
74 | if (!this.feedbackForm) { return; }
75 | const form = this.feedbackForm;
76 | for (const field in this.formErrors) {
77 | // clear previous error message (if any)
78 | this.formErrors[field] = '';
79 | const control = form.get(field);
80 | if (control && control.dirty && !control.valid) {
81 | const messages = this.validationMessages[field];
82 | for (const key in control.errors) {
83 | this.formErrors[field] += messages[key] + ' ';
84 | }
85 | }
86 | }
87 | }
88 |
89 | onSubmit() {
90 | this.feedback = this.feedbackForm.value;
91 | console.log(this.feedback);
92 | this.fbservice.submitFeedback(this.feedback);
93 | this.feedbackForm.reset({
94 | firstname: '',
95 | lastname: '',
96 | telnum: '',
97 | email: '',
98 | agree: false,
99 | contacttype: 'None',
100 | message: ''
101 | });
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-blacklist": [
20 | true,
21 | "rxjs",
22 | "rxjs/Rx"
23 | ],
24 | "import-spacing": true,
25 | "indent": [
26 | true,
27 | "spaces"
28 | ],
29 | "interface-over-type-literal": true,
30 | "label-position": true,
31 | "max-line-length": [
32 | true,
33 | 140
34 | ],
35 | "member-access": false,
36 | "member-ordering": [
37 | true,
38 | {
39 | "order": [
40 | "static-field",
41 | "instance-field",
42 | "static-method",
43 | "instance-method"
44 | ]
45 | }
46 | ],
47 | "no-arg": true,
48 | "no-bitwise": true,
49 | "no-console": [
50 | true,
51 | "debug",
52 | "info",
53 | "time",
54 | "timeEnd",
55 | "trace"
56 | ],
57 | "no-construct": true,
58 | "no-debugger": true,
59 | "no-duplicate-super": true,
60 | "no-empty": false,
61 | "no-empty-interface": true,
62 | "no-eval": true,
63 | "no-inferrable-types": [
64 | true,
65 | "ignore-params"
66 | ],
67 | "no-misused-new": true,
68 | "no-non-null-assertion": true,
69 | "no-shadowed-variable": true,
70 | "no-string-literal": false,
71 | "no-string-throw": true,
72 | "no-switch-case-fall-through": true,
73 | "no-trailing-whitespace": true,
74 | "no-unnecessary-initializer": true,
75 | "no-unused-expression": true,
76 | "no-use-before-declare": true,
77 | "no-var-keyword": true,
78 | "object-literal-sort-keys": false,
79 | "one-line": [
80 | true,
81 | "check-open-brace",
82 | "check-catch",
83 | "check-else",
84 | "check-whitespace"
85 | ],
86 | "prefer-const": true,
87 | "quotemark": [
88 | true,
89 | "single"
90 | ],
91 | "radix": true,
92 | "semicolon": [
93 | true,
94 | "always"
95 | ],
96 | "triple-equals": [
97 | true,
98 | "allow-null-check"
99 | ],
100 | "typedef-whitespace": [
101 | true,
102 | {
103 | "call-signature": "nospace",
104 | "index-signature": "nospace",
105 | "parameter": "nospace",
106 | "property-declaration": "nospace",
107 | "variable-declaration": "nospace"
108 | }
109 | ],
110 | "unified-signatures": true,
111 | "variable-name": false,
112 | "whitespace": [
113 | true,
114 | "check-branch",
115 | "check-decl",
116 | "check-operator",
117 | "check-separator",
118 | "check-type"
119 | ],
120 | "directive-selector": [
121 | true,
122 | "attribute",
123 | "app",
124 | "camelCase"
125 | ],
126 | "component-selector": [
127 | true,
128 | "element",
129 | "app",
130 | "kebab-case"
131 | ],
132 | "no-output-on-prefix": true,
133 | "use-input-property-decorator": true,
134 | "use-output-property-decorator": true,
135 | "use-host-property-decorator": true,
136 | "no-input-rename": true,
137 | "no-output-rename": true,
138 | "use-life-cycle-interface": true,
139 | "use-pipe-transform-interface": true,
140 | "component-class-suffix": true,
141 | "directive-class-suffix": true
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/app/dishdetail/dishdetail.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{disha.name | uppercase}}
8 |
9 |
10 |
11 |
12 | {{disha.description}}
13 |
14 |
15 |
16 | BACK
17 | Like
18 | Share
19 |
20 |
21 |
22 |
23 |
24 |
Loading . . . Please Wait
25 |
26 |
27 |
Error
28 | {{errMess}}
29 |
30 |
31 |
32 |
33 |
34 | Comment
35 |
36 | {{dish2.comment}}
37 |
38 | {{dish2.rating}} Stars
39 |
40 |
41 | --{{dish2.author}} {{dish2.date | date}}
42 |
43 |
44 |
45 | {{commentForm.value.comment}}
46 | 5 Stars
47 | --{{commentForm.value.author}}
48 |
49 |
50 |
51 |
Loading . . . Please Wait
52 |
53 |
54 |
Error
55 | {{errMess}}
56 |
57 |
58 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/src/app/services/dish.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Dish } from '../shared/dish';
3 | //import {DISHES} from '../shared/dishes';
4 |
5 | import { Observable } from 'rxjs/Observable';
6 | //这个傻逼of要单独加。
7 | import 'rxjs/add/observable/of';
8 | import 'rxjs/add/operator/map';
9 | import 'rxjs/add/operator/delay';
10 | import { Http, Response } from '@angular/http';
11 | //import { baseURL } from '../shared/baseurl';
12 | import { ProcessHttpmsgService } from './process-httpmsg.service';
13 | //import 'rxjs/add/operator/catch';
14 | import { RestangularModule, Restangular } from 'ngx-restangular';
15 |
16 | @Injectable()
17 | export class DishService {
18 |
19 |
20 | //test just for resolve data using promise
21 | /*getDishes(): Promise {
22 | return Promise.resolve(DISHES);
23 | }
24 |
25 | getDish(id: number): Promise {
26 | return Promise.resolve(DISHES.filter((dish) => (dish.id === id))[0]);
27 | }
28 |
29 | getFeaturedDish(): Promise {
30 | return Promise.resolve(DISHES.filter((dish) => dish.featured)[0]);
31 | }*/
32 |
33 | //test for lantency using promise
34 | /*getDishes(): Promise {
35 | return new Promise(resolve=> {
36 | // Simulate server latency with 2 second delay
37 | setTimeout(() => resolve(DISHES), 2000);
38 | });
39 | }
40 |
41 | getDish(id: number): Promise {
42 | return new Promise(resolve=> {
43 | // Simulate server latency with 2 second delay
44 | setTimeout(() => resolve(DISHES.filter((dish) => (dish.id === id))[0]), 2000);
45 | });
46 | }
47 |
48 | getFeaturedDish(): Promise {
49 | return new Promise(resolve=> {
50 | // Simulate server latency with 2 second delay
51 | setTimeout(() => resolve(DISHES.filter((dish) => dish.featured)[0]), 2000);
52 | });
53 | }*/
54 |
55 | //test observable
56 |
57 |
58 | /*本地http module连接
59 | constructor(private http: Http,
60 | private processHTTPMsgService: ProcessHttpmsgService) { }
61 | getDishes(): Observable {
62 | return this.http.get(baseURL + 'dishes')
63 | .map(res => { return this.processHTTPMsgService.extractData(res); })
64 | .catch(error => { return this.processHTTPMsgService.handleError(error); });
65 | }
66 |
67 | getDish(id: number): Observable {
68 | return this.http.get(baseURL + 'dishes/'+ id)
69 | .map(res => { return this.processHTTPMsgService.extractData(res); })
70 | .catch(error => { return this.processHTTPMsgService.handleError(error); });
71 | }
72 |
73 | getFeaturedDish(): Observable {
74 | return this.http.get(baseURL + 'dishes?featured=true')
75 | .map(res => { return this.processHTTPMsgService.extractData(res)[0]; })
76 | .catch(error => { return this.processHTTPMsgService.handleError(error); });
77 | }
78 |
79 | getDishIds(): Observable {
80 | return this.getDishes()
81 | .map(dishes => { return dishes.map(dish => dish.id) });
82 | }*/
83 |
84 | constructor(private restangular: Restangular,
85 | private processHTTPMsgService: ProcessHttpmsgService) { }
86 | getDishes(): Observable {
87 | return this.restangular.all('dishes').getList();
88 | }
89 |
90 | getDish(id: number): Observable {
91 | return this.restangular.one('dishes',id).get();
92 | }
93 |
94 | getFeaturedDish(): Observable {
95 | return this.restangular.all('dishes').getList({featured: true})
96 | .map(dishes => dishes[0]);
97 | }
98 |
99 | getDishIds(): Observable {
100 | return this.getDishes()
101 | .map(dishes => { return dishes.map(dish => dish.id) })
102 |
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/app/dishdetail/dishdetail.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Inject } from '@angular/core';
2 |
3 | import {Dish} from '../shared/dish';
4 |
5 | import { DishService } from '../services/dish.service';
6 | import { Params, ActivatedRoute } from '@angular/router';
7 | import { Location } from '@angular/common';
8 | import { FormBuilder, FormGroup, Validators} from '@angular/forms';
9 | import { Comment } from '../shared/comment';
10 | import { visibility,expand } from '../animations/app.animation';
11 | import 'rxjs/add/operator/switchMap';
12 |
13 | @Component({
14 | selector: 'app-dishdetail',
15 | templateUrl: './dishdetail.component.html',
16 | styleUrls: ['./dishdetail.component.scss'],
17 | animations: [
18 | visibility(),
19 | expand()
20 | ]
21 | })
22 | export class DishdetailComponent implements OnInit {
23 |
24 | formErrors = {
25 | 'author': '',
26 | 'comment': ''
27 | };
28 |
29 | validationMessages = {
30 | 'author': {
31 | 'required': 'Name is required.',
32 | 'minlength': ' Name must be at least 2 characters long.'
33 | },
34 | 'comment': {
35 | 'required': 'commet is required.'
36 | },
37 | };
38 | //@Input()
39 | disha: Dish;
40 | dishIds: number[];
41 | prev: number;
42 | next: number;
43 | errMess: string;
44 |
45 | commentForm: FormGroup;
46 | commentdata: Comment;
47 | visibility = 'shown';
48 |
49 | constructor(private dishservice: DishService,
50 | private route: ActivatedRoute,
51 | private location: Location,
52 | private db: FormBuilder,
53 | @Inject('BaseURL') private BaseURL) {
54 | this.createForm();
55 | }
56 |
57 | createForm(){
58 | this.commentForm = this.db.group({
59 | rating: 0,
60 | comment: ['', [Validators.required]],
61 | author: ['', [Validators.required, Validators.minLength(2)]],
62 | });
63 | this.commentForm.valueChanges
64 | .subscribe(data => this.onValueChanged(data));
65 | this.onValueChanged();
66 | }
67 |
68 | onValueChanged(data?: any) {
69 | if (!this.commentForm) { return; }
70 | const form = this.commentForm;
71 | for (const field in this.formErrors) {
72 | // clear previous error message (if any)
73 | this.formErrors[field] = '';
74 | const control = form.get(field);
75 | if (control && control.dirty && !control.valid) {
76 | const messages = this.validationMessages[field];
77 | for (const key in control.errors) {
78 | this.formErrors[field] += messages[key] + ' ';
79 | }
80 | }
81 | }
82 | }
83 |
84 | onSubmit() {
85 | //一定是.value才能获取值。所以访问属性的时候也是.value.对应的属性名
86 | this.commentdata = this.commentForm.value;
87 | this.disha.comments.push(this.commentdata);
88 | this.commentdata.date = new Date().toISOString();
89 | console.log(this.commentdata);
90 | this.commentForm.reset({
91 | rating: 0,
92 | comment: '',
93 | author: ''
94 | });
95 | }
96 |
97 | ngOnInit() {
98 | this.createForm();
99 | this.dishservice.getDishIds().subscribe(dishIds => this.dishIds = dishIds);
100 | this.route.params
101 | .switchMap((params: Params) => { this.visibility = 'hidden'; return this.dishservice.getDish(+params['id']); })
102 | .subscribe(dish => { this.disha = dish; this.setPrevNext(dish.id); this.visibility = 'shown'; },
103 | errmess => this.errMess = errmess);
104 | }
105 |
106 | setPrevNext(dishId: number) {
107 | let index = this.dishIds.indexOf(dishId);
108 | this.prev = this.dishIds[(this.dishIds.length + index - 1)%this.dishIds.length];
109 | this.next = this.dishIds[(this.dishIds.length + index + 1)%this.dishIds.length];
110 | }
111 |
112 | goBack(): void {
113 | this.location.back();
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/app/contact/contact.component.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
Contact Us
8 |
9 |
10 |
11 |
12 |
13 |
Location Information
14 |
15 |
16 |
Our Address
17 |
18 | 121, Clear Water Bay Road
19 | Clear Water Bay, Kowloon
20 | HONG KONG
21 | : +852 1234 5678
22 | : +852 8765 4321
23 | :
24 | confusion@food.net
25 |
26 |
27 |
32 |
33 |
34 |
Map of our Location
35 |
36 |
37 |
38 |
39 |
100 |
--------------------------------------------------------------------------------
/src/app/shared/dishes.ts:
--------------------------------------------------------------------------------
1 | import {Dish} from './dish';
2 |
3 | export const DISHES:Dish[] = [
4 | {
5 | id:0,
6 | name: 'Uthappizza',
7 | image: '/assets/images/uthappizza.png',
8 | category: 'mains',
9 | label: 'Hot',
10 | price: '4.99',
11 | featured:true,
12 | description: 'A unique combination of Indian Uthappam (pancake) and Italian pizza, topped with Cerignola olives, ripe vine cherry tomatoes, Vidalia onion, Guntur chillies and Buffalo Paneer.',
13 | comments: [
14 | {
15 | rating: 5,
16 | comment: "Imagine all the eatables, living in conFusion!",
17 | author: "John Lemon",
18 | date: "2012-10-16T17:57:28.556094Z"
19 | },
20 | {
21 | rating: 4,
22 | comment: "Sends anyone to heaven, I wish I could get my mother-in-law to eat it!",
23 | author: "Paul McVites",
24 | date: "2014-09-05T17:57:28.556094Z"
25 | },
26 | {
27 | rating: 3,
28 | comment: "Eat it, just eat it!",
29 | author: "Michael Jaikishan",
30 | date: "2015-02-13T17:57:28.556094Z"
31 | },
32 | {
33 | rating: 4,
34 | comment: "Ultimate, Reaching for the stars!",
35 | author: "Ringo Starry",
36 | date: "2013-12-02T17:57:28.556094Z"
37 | },
38 | {
39 | rating: 2,
40 | comment: "It's your birthday, we're gonna party!",
41 | author: "25 Cent",
42 | date: "2011-12-02T17:57:28.556094Z"
43 | }
44 | ]
45 | },
46 | {
47 | id:1,
48 | name: 'Zucchipakoda',
49 | image: '/assets/images/zucchipakoda.png',
50 | category: 'appetizer',
51 | label: '',
52 | price: '1.99',
53 | featured:false,
54 | description: 'Deep fried Zucchini coated with mildly spiced Chickpea flour batter accompanied with a sweet-tangy tamarind sauce',
55 | comments: [
56 | {
57 | rating: 5,
58 | comment: "Imagine all the eatables, living in conFusion!",
59 | author: "John Lemon",
60 | date: "2012-10-16T17:57:28.556094Z"
61 | },
62 | {
63 | rating: 4,
64 | comment: "Sends anyone to heaven, I wish I could get my mother-in-law to eat it!",
65 | author: "Paul McVites",
66 | date: "2014-09-05T17:57:28.556094Z"
67 | },
68 | {
69 | rating: 3,
70 | comment: "Eat it, just eat it!",
71 | author: "Michael Jaikishan",
72 | date: "2015-02-13T17:57:28.556094Z"
73 | },
74 | {
75 | rating: 4,
76 | comment: "Ultimate, Reaching for the stars!",
77 | author: "Ringo Starry",
78 | date: "2013-12-02T17:57:28.556094Z"
79 | },
80 | {
81 | rating: 2,
82 | comment: "It's your birthday, we're gonna party!",
83 | author: "25 Cent",
84 | date: "2011-12-02T17:57:28.556094Z"
85 | }
86 | ]
87 | },
88 | {
89 | id:2,
90 | name: 'Vadonut',
91 | image: '/assets/images/vadonut.png',
92 | category: 'appetizer',
93 | label: 'New',
94 | price: '1.99',
95 | featured:false,
96 | description: 'A quintessential ConFusion experience, is it a vada or is it a donut?',
97 | comments: [
98 | {
99 | rating: 5,
100 | comment: "Imagine all the eatables, living in conFusion!",
101 | author: "John Lemon",
102 | date: "2012-10-16T17:57:28.556094Z"
103 | },
104 | {
105 | rating: 4,
106 | comment: "Sends anyone to heaven, I wish I could get my mother-in-law to eat it!",
107 | author: "Paul McVites",
108 | date: "2014-09-05T17:57:28.556094Z"
109 | },
110 | {
111 | rating: 3,
112 | comment: "Eat it, just eat it!",
113 | author: "Michael Jaikishan",
114 | date: "2015-02-13T17:57:28.556094Z"
115 | },
116 | {
117 | rating: 4,
118 | comment: "Ultimate, Reaching for the stars!",
119 | author: "Ringo Starry",
120 | date: "2013-12-02T17:57:28.556094Z"
121 | },
122 | {
123 | rating: 2,
124 | comment: "It's your birthday, we're gonna party!",
125 | author: "25 Cent",
126 | date: "2011-12-02T17:57:28.556094Z"
127 | }
128 | ]
129 | },
130 | {
131 | id:3,
132 | name: 'ElaiCheese Cake',
133 | image: '/assets/images/elaicheesecake.png',
134 | category: 'dessert',
135 | label: '',
136 | price: '2.99',
137 | featured:false,
138 | description: 'A delectable, semi-sweet New York Style Cheese Cake, with Graham cracker crust and spiced with Indian cardamoms',
139 | comments: [
140 | {
141 | rating: 5,
142 | comment: "Imagine all the eatables, living in conFusion!",
143 | author: "John Lemon",
144 | date: "2012-10-16T17:57:28.556094Z"
145 | },
146 | {
147 | rating: 4,
148 | comment: "Sends anyone to heaven, I wish I could get my mother-in-law to eat it!",
149 | author: "Paul McVites",
150 | date: "2014-09-05T17:57:28.556094Z"
151 | },
152 | {
153 | rating: 3,
154 | comment: "Eat it, just eat it!",
155 | author: "Michael Jaikishan",
156 | date: "2015-02-13T17:57:28.556094Z"
157 | },
158 | {
159 | rating: 4,
160 | comment: "Ultimate, Reaching for the stars!",
161 | author: "Ringo Starry",
162 | date: "2013-12-02T17:57:28.556094Z"
163 | },
164 | {
165 | rating: 2,
166 | comment: "It's your birthday, we're gonna party!",
167 | author: "25 Cent",
168 | date: "2011-12-02T17:57:28.556094Z"
169 | }
170 | ]
171 | }
172 | ];
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # [](https://github.com/NH1900)
3 | # Catalog
4 | * [Background](#background)
5 | * [Usage](#usage)
6 | * [Content](#content)
7 | * Home
8 | * Log in
9 | * Menu
10 | * Contact us
11 | * About us
12 | * [Future](#future)
13 |
14 | # Background
15 | Built a Online restaurant for students to ordering dishes.
16 | # Usage
17 | ## Restaurant
18 |
19 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.7.1.
20 |
21 | ### Development server
22 |
23 | 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.
24 |
25 | ### Code scaffolding
26 |
27 | 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`.
28 |
29 | ### Build
30 |
31 | 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.
32 |
33 | ### Running unit tests
34 |
35 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
36 |
37 | ### Running end-to-end tests
38 |
39 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
40 |
41 | ### Further help
42 |
43 | 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).
44 |
45 |
46 | # Content
47 | ## Home
48 | In the home page, users can find specialty dishes and introduction ofthe resaurant. Users can directly slide images of dishes to add them to favorites dishes list.
49 | To obtain the data flow from backend, I use RestAPI of Angular to geet the observable of data.
50 | ```Typescript
51 | leader: Leader;
52 | dish: Dish;
53 | promotion: Promotion;
54 | dishErrMess: string;
55 | promotionErrMess: string;
56 | leaderErrMess: string;
57 | constructor(private dishservice: DishService,
58 | private promotionservice: PromotionService,
59 | private leaderservice: LeaderService,
60 | @Inject('BaseURL') private BaseURL) { }
61 |
62 | ngOnInit() {
63 | this.dishservice.getFeaturedDish().subscribe( dish => this.dish = dish,errmess => this.dishErrMess = errmess);
64 | this.promotionservice.getFeaturedPromotion().subscribe(promotion => this.promotion = promotion,errmess => this.promotionErrMess = errmess);
65 | this.leaderservice.getFeaturedLeader().subscribe(leader => this.leader = leader,errmess => this.leaderErrMess = errmess);
66 | }
67 | ```
68 | ## Log in
69 | Users can log in to our restaurant or create an account. After logging in, our system will recommend users some dishes baes on their ordering history.
70 | Defining the format of an account and the valid input of password and username
71 | ```Typescript
72 | describe('LoginComponent', () => {
73 | let component: LoginComponent;
74 | let fixture: ComponentFixture;
75 |
76 | beforeEach(async(() => {
77 | TestBed.configureTestingModule({
78 | declarations: [ LoginComponent ]
79 | })
80 | .compileComponents();
81 | }));
82 |
83 | beforeEach(() => {
84 | fixture = TestBed.createComponent(LoginComponent);
85 | component = fixture.componentInstance;
86 | fixture.detectChanges();
87 | });
88 |
89 | it('should create', () => {
90 | expect(component).toBeTruthy();
91 | });
92 | ```
93 | ## Menu
94 | In Menu page, users can browser dishes to choose what they like
95 | For each dish after clicking corresponding image,user can get into another page introducing dishdetails for that specific dish
96 | In dishdetail page,users can leave message and add comments to that dish.
97 | ```Typescript
98 | createForm(){
99 | this.commentForm = this.db.group({
100 | rating: 0,
101 | comment: ['', [Validators.required]],
102 | author: ['', [Validators.required, Validators.minLength(2)]],
103 | });
104 | this.commentForm.valueChanges
105 | .subscribe(data => this.onValueChanged(data));
106 | this.onValueChanged();
107 | }
108 |
109 | onValueChanged(data?: any) {
110 | if (!this.commentForm) { return; }
111 | const form = this.commentForm;
112 | for (const field in this.formErrors) {
113 | // clear previous error message (if any)
114 | this.formErrors[field] = '';
115 | const control = form.get(field);
116 | if (control && control.dirty && !control.valid) {
117 | const messages = this.validationMessages[field];
118 | for (const key in control.errors) {
119 | this.formErrors[field] += messages[key] + ' ';
120 | }
121 | }
122 | }
123 | }
124 |
125 | onSubmit() {
126 | this.commentdata = this.commentForm.value;
127 | this.disha.comments.push(this.commentdata);
128 | this.commentdata.date = new Date().toISOString();
129 | console.log(this.commentdata);
130 | this.commentForm.reset({
131 | rating: 0,
132 | comment: '',
133 | author: ''
134 | });
135 | }
136 |
137 | ```
138 | ## Contact us
139 | In the contact page, Users can fill out their information to leave a message or directly contact us for some problems during ordering.
140 | Still in this page,users' information will be stored in database.
141 | ```Typescript
142 | createForm() {
143 | this.feedbackForm = this.fb.group({
144 | firstname: ['',[Validators.required, Validators.minLength(2), Validators.maxLength(25)] ],
145 | lastname: ['',[Validators.required, Validators.minLength(2), Validators.maxLength(25)] ],
146 | telnum: ['',[Validators.required, Validators.pattern] ],
147 | email: ['', [Validators.required, Validators.email] ],
148 | agree: false,
149 | contacttype: 'None',
150 | message: ''
151 | });
152 | this.feedbackForm.valueChanges
153 | .subscribe(data => this.onValueChanged(data));
154 |
155 | this.onValueChanged(); // (re)set validation messages now
156 | }
157 |
158 | onValueChanged(data?: any) {
159 | if (!this.feedbackForm) { return; }
160 | const form = this.feedbackForm;
161 | for (const field in this.formErrors) {
162 | // clear previous error message (if any)
163 | this.formErrors[field] = '';
164 | const control = form.get(field);
165 | if (control && control.dirty && !control.valid) {
166 | const messages = this.validationMessages[field];
167 | for (const key in control.errors) {
168 | this.formErrors[field] += messages[key] + ' ';
169 | }
170 | }
171 | }
172 | }
173 |
174 | onSubmit() {
175 | this.feedback = this.feedbackForm.value;
176 | console.log(this.feedback);
177 | this.fbservice.submitFeedback(this.feedback);
178 | this.feedbackForm.reset({
179 | firstname: '',
180 | lastname: '',
181 | telnum: '',
182 | email: '',
183 | agree: false,
184 | contacttype: 'None',
185 | message: ''
186 | });
187 | }
188 | ```
189 | ## About us
190 | Users can find introduction about the restaurant and their owners
191 | # Future
192 | In the future based on customers' requirements I plan to add more functions suach as:self building service ....
193 |
194 |
--------------------------------------------------------------------------------