Commit 4a631a08 authored by Sai Manideep M's avatar Sai Manideep M

Initial commit

parents
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
# See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "pwa-chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}
# Project
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.0.1.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
## Code scaffolding
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`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"project": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/project",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "project:build:production"
},
"development": {
"browserTarget": "project:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "project:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
}
}
}
},
"cli": {
"analytics": "1d0af54f-e4c6-4e61-8999-eb010ba49b5a"
}
}
This diff is collapsed.
{
"name": "project",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^15.0.0",
"@angular/common": "^15.0.0",
"@angular/compiler": "^15.0.0",
"@angular/core": "^15.0.0",
"@angular/forms": "^15.0.0",
"@angular/platform-browser": "^15.0.0",
"@angular/platform-browser-dynamic": "^15.0.0",
"@angular/router": "^15.0.0",
"bootstrap": "^3.4.1",
"rxjs": "^6.6.7",
"rxjs-compat": "^6.6.7",
"tslib": "^2.3.0",
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.1",
"@angular/cli": "~15.0.1",
"@angular/compiler-cli": "^15.0.0",
"@types/jasmine": "~4.3.0",
"jasmine-core": "~4.5.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0",
"typescript": "~4.8.2"
}
}
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { RecipeDetailComponent } from './recipes/recipe-detail/recipe-detail.component';
import { RecipeEditComponent } from './recipes/recipe-edit/recipe-edit.component';
import { RecipesStartComponent } from './recipes/recipes-start/recipes-start.component';
import { RecipesComponent } from './recipes/recipes.component';
import { ShoppingEditComponent } from './shopping-list/shopping-edit/shopping-edit.component';
import { ShoppingListComponent } from './shopping-list/shopping-list.component';
const appRoutes: Routes = [
{path: '', redirectTo: '/recipes', pathMatch: 'full'},
{path: 'recipes', component: RecipesComponent, children: [
{path: '', component: RecipesStartComponent},
{path: 'new', component: RecipeEditComponent},
{path: ':id', component: RecipeDetailComponent},
{path: ':id/edit', component: RecipeEditComponent}
]},
{path: 'shopping-list', component: ShoppingListComponent}
];
@NgModule({
imports: [RouterModule.forRoot(appRoutes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
<app-header></app-header>
<div class="container">
<div class="row">
<div class="col-md-12">
<router-outlet></router-outlet>
</div>
</div>
\ No newline at end of file
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'project'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('project');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.content span')?.textContent).toContain('project app is running!');
});
});
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
import { RecipesComponent } from './recipes/recipes.component';
import { RecipeListComponent } from './recipes/recipe-list/recipe-list.component';
import { RecipeDetailComponent } from './recipes/recipe-detail/recipe-detail.component';
import { RecipeItemComponent } from './recipes/recipe-list/recipe-item/recipe-item.component';
import { ShoppingListComponent } from './shopping-list/shopping-list.component';
import { ShoppingEditComponent } from './shopping-list/shopping-edit/shopping-edit.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { DropdownDirective } from './shared/dropdown.directive';
import { ShoppingListService } from './shopping-list/shopping-list.service';
import { RecipesStartComponent } from './recipes/recipes-start/recipes-start.component';
import { RecipeEditComponent } from './recipes/recipe-edit/recipe-edit.component';
import { RecipeService } from './recipes/recipe.service';
@NgModule({
declarations: [
AppComponent,
HeaderComponent,
RecipesComponent,
RecipeListComponent,
RecipeDetailComponent,
RecipeItemComponent,
ShoppingListComponent,
ShoppingEditComponent,
DropdownDirective,
RecipesStartComponent,
RecipeEditComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
AppRoutingModule,
ReactiveFormsModule
],
providers: [ShoppingListService, RecipeService],
bootstrap: [AppComponent]
})
export class AppModule { }
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a href="#" class="navbar-brand">Recipe Book</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li routerLinkActive="active"><a routerLink="/recipes">Recipes</a></li>
<li routerLinkActive="active"><a routerLink="/shopping-list">Shopping List</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown" appDropdown>
<a style="cursor: pointer;" class="dropdown-toggle" role="button">Manage <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a style="cursor: pointer;">Save Data</a></li>
<li><a style="cursor: pointer;">Fetch Data</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
\ No newline at end of file
import { Component, EventEmitter, Output } from "@angular/core";
@Component({
selector: 'app-header',
templateUrl: './header.component.html'
})
export class HeaderComponent {
}
\ No newline at end of file
<div class="row">
<div class="col-xs-12">
<img
[src]="recipe.imagePath"
alt="{{ recipe.name }}"
class="img-responsive"
style="max-height: 300px">
</div>
</div>
<div class="row">
<div class="col-xs-12">
<h1>{{ recipe.name }}</h1>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div
class="btn-group"
appDropdown>
<button
type="button"
class="btn btn-primary dropdown-toggle">
Manage Recipe <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a (click)="onAddToShoppingList()" style="cursor: pointer;">to Shopping List</a></li>
<li><a style="cursor: pointer;" (click)="onEditRecipe()">Edit Recipe</a></li>
<li><a style="cursor: pointer;" (click)="onDelete()">Delete Recipe</a></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
{{ recipe.description }}
</div>
</div>
<div class="row">
<div class="col-xs-12">
<ul class="list-group">
<li
class="list-group-item"
*ngFor="let ingridient of recipe.ingridients">
{{ingridient.name}} - {{ingridient.amount}}
</li>
</ul>
</div>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RecipeDetailComponent } from './recipe-detail.component';
describe('RecipeDetailComponent', () => {
let component: RecipeDetailComponent;
let fixture: ComponentFixture<RecipeDetailComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RecipeDetailComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(RecipeDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { Recipe } from '../recipe.model';
import { RecipeService } from '../recipe.service';
@Component({
selector: 'app-recipe-detail',
templateUrl: './recipe-detail.component.html',
styleUrls: ['./recipe-detail.component.css']
})
export class RecipeDetailComponent implements OnInit{
recipe: Recipe;
id: number;
constructor(private recipeService: RecipeService,
private route: ActivatedRoute,
private router: Router) {}
ngOnInit() {
this.route.params
.subscribe(
(params: Params) => {
this.id = +params['id'];
this.recipe = this.recipeService.getRecipe(this.id);
}
);
}
onAddToShoppingList() {
this.recipeService.addIngridientsToShoppingList(this.recipe.ingridients);
}
onEditRecipe() {
this.router.navigate(['edit'], {relativeTo: this.route})
// this.router.navigate(['../', this.id, 'edit'], {relativeTo: this.route});
}
onDelete() {
this.recipeService.deleteRecipe(this.id);
this.router.navigate(['/recipes']);
}
}
input.ng-invalid.ng-touched,
textarea.ng-invalid.ng-touched {
border: 1px solid red;
}
\ No newline at end of file
<div class="row">
<div class="col-xs-12">
<form [formGroup]="recipeForm" (ngSubmit)="onSubmit()">
<div class="row">
<div class="col-xs-12">
<button
type="submit"
class="btn btn-success"
[disabled]="!recipeForm.valid">Save</button>
<button type="button" class="btn btn-danger" (click)="onCancel()">Cancel</button>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="name">Name</label>
<input
type="text"
id="name"
formControlName="name"
class="form-control">
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="imagePath">Image URL</label>
<input
type="text"
id="imagePath"
formControlName="imagePath"
class="form-control"
#imagePath>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<img [src]="imagePath.value" class="img-responsive">
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="description">Description</label>
<textarea
type="text"
id="description"
class="form-control"
formControlName="description"
rows="6"></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<input
type="text"
class="form-control">
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12" formArrayName="ingridients">
<div class="row"
*ngFor="let ingridientCtrl of controls; let i = index"
[formGroupName]="i"
style="margin-top: 10px">
<div class="col-xs-8">
<input
type="text"
class="form-control"
formControlName="name"
>
</div>
<div class="col-xs-2">
<input
type="number"
class="form-control"
formControlName="amount"
>
</div>
<div class="col-xs-2">
<button
type="button"
class="btn btn-danger"
(click)="onDeleteIngridient(i)">X</button>
</div>
</div>
<hr>
<div class="row">
<div class="col-xs-12">
<button
class="btn btn-success"
type="button"
(click)="onAddIngridient()">Add Ingridient</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RecipeEditComponent } from './recipe-edit.component';
describe('RecipeEditComponent', () => {
let component: RecipeEditComponent;
let fixture: ComponentFixture<RecipeEditComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RecipeEditComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(RecipeEditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { RecipeService } from '../recipe.service';
@Component({
selector: 'app-recipe-edit',
templateUrl: './recipe-edit.component.html',
styleUrls: ['./recipe-edit.component.css']
})
export class RecipeEditComponent implements OnInit{
id: number;
editMode = false;
recipeForm: FormGroup;
constructor(private route: ActivatedRoute,
private recipeService: RecipeService,
private router: Router) {}
ngOnInit() {
this.route.params
.subscribe(
(params: Params) => {
this.id = +params['id'];
this.editMode = params['id'] != null;
this.inintForm()
}
)
}
onSubmit() {
// const newRecipe = new Recipe(
// this.recipeForm.value['name'],
// this.recipeForm.value['description'],
// this.recipeForm.value['imagePath'],
// this.recipeForm.value['ingridients']);
if(this.editMode){
this.recipeService.updateRecipe(this.id, this.recipeForm.value);
} else {
this.recipeService.addRecipe(this.recipeForm.value);
}
this.onCancel();
}
onAddIngridient() {
(<FormArray>this.recipeForm.get('ingridients')).push(
new FormGroup({
'name': new FormControl(null, Validators.required),
'amount': new FormControl(null, [
Validators.required,
Validators.pattern(/^[1-9]+[0-9]*$/)
])
})
)
}
onDeleteIngridient(index: number) {
(<FormArray>this.recipeForm.get('ingridients')).removeAt(index);
}
onCancel() {
this.router.navigate(['../'], {relativeTo: this.route})
}
private inintForm() {
let recipeName = '';
let recipeImagePath = '';
let recipeDescription = '';
let recipeIngridients = new FormArray([]);
if(this.editMode){
const recipe = this.recipeService.getRecipe(this.id);
recipeName = recipe.name;
recipeImagePath = recipe.imagePath;
recipeDescription = recipe.description;
if(recipe.ingridients){
for(let ingridient of recipe.ingridients){
recipeIngridients.push(
new FormGroup({
'name': new FormControl(ingridient.name, Validators.required),
'amount': new FormControl(ingridient.amount, [
Validators.required,
Validators.pattern(/^[1-9]+[0-9]*$/)
])
})
);
}
}
}
this.recipeForm = new FormGroup({
'name': new FormControl(recipeName, Validators.required),
'imagePath': new FormControl(recipeImagePath, Validators.required),
'description': new FormControl(recipeDescription, Validators.required),
'ingridients': recipeIngridients
});
}
get controls() {
return (<FormArray>this.recipeForm.get('ingridients')).controls;
}
}
<a
style="cursor: pointer;"
[routerLink]="[index]"
routerLinkActive="active"
class="list-group-item clearfix">
<div class="pull-left">
<h4 class="list-group-item-heading">{{ recipe.name }}</h4>
<p class="list-group-item-text">{{ recipe.description }}</p>
</div>
<span class="pull-right">
<img
[src]="recipe.imagePath"
alt="{{recipe.name}}"
class="img-responsive"
style="max-height: 50px;">
</span>
</a>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RecipeItemComponent } from './recipe-item.component';
describe('RecipeItemComponent', () => {
let component: RecipeItemComponent;
let fixture: ComponentFixture<RecipeItemComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RecipeItemComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(RecipeItemComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, EventEmitter, Input } from '@angular/core';
import { Recipe } from '../../recipe.model';
import { RecipeService } from '../../recipe.service';
@Component({
selector: 'app-recipe-item',
templateUrl: './recipe-item.component.html',
styleUrls: ['./recipe-item.component.css']
})
export class RecipeItemComponent {
@Input() recipe: Recipe;
@Input() index: number;
}
<div class="row">
<div class="col-xs-12">
<button class="btn btn-sucess" (click)="onNewRecipe()">New Recipe</button>
</div>
</div>
<hr>
<div class="row">
<div class="col-xs-12">
<app-recipe-item *ngFor="let recipeEl of recipes; let i = index" [recipe]="recipeEl" [index]="i"></app-recipe-item>
</div>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RecipeListComponent } from './recipe-list.component';
describe('RecipeListComponent', () => {
let component: RecipeListComponent;
let fixture: ComponentFixture<RecipeListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RecipeListComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(RecipeListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { Recipe } from '../recipe.model';
import { RecipeService } from '../recipe.service';
@Component({
selector: 'app-recipe-list',
templateUrl: './recipe-list.component.html',
styleUrls: ['./recipe-list.component.css']
})
export class RecipeListComponent implements OnInit, OnDestroy{
recipes: Recipe[];
subscription: Subscription;
constructor(private recipeService: RecipeService,
private router: Router,
private route: ActivatedRoute) {}
ngOnInit() {
this.subscription = this.recipeService.recipesChanged
.subscribe(
(recipes: Recipe[]) => {
this.recipes = recipes;
}
)
this.recipes = this.recipeService.getRecipes();
}
onNewRecipe() {
this.router.navigate(['new'], {relativeTo: this.route});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
import { Ingridient } from "../shared/ingridient.model";
export class Recipe {
public name: string;
public description: string;
public imagePath: string;
public ingridients: Ingridient[]
constructor(name: string, desc: string, imagePath: string, ingridients: Ingridient[]) {
this.name = name;
this.description = desc;
this.imagePath = imagePath;
this.ingridients = ingridients;
}
}
\ No newline at end of file
import { EventEmitter, Injectable } from "@angular/core";
import { Subject } from "rxjs";
import { Ingridient } from "../shared/ingridient.model";
import { ShoppingListService } from "../shopping-list/shopping-list.service";
import { Recipe } from "./recipe.model";
@Injectable()
export class RecipeService {
recipesChanged = new Subject<Recipe[]>();
private recipes: Recipe[] = [
new Recipe(
'Tasty Schnitzel',
'A super-tasty Schnitzel - just awesome!',
'https://upload.wikimedia.org/wikipedia/commons/7/72/Schnitzel.JPG',
[
new Ingridient('Meat', 1),
new Ingridient('French Fries', 20)
]),
new Recipe(
'Big Fat Burger',
'What else you need to say ?',
'https://upload.wikimedia.org/wikipedia/commons/b/be/Burger_King_Angus_Bacon_%26_Cheese_Steak_Burger.jpg',
[
new Ingridient('buns', 2),
new Ingridient('Meat', 1)
])
];
constructor(private slService: ShoppingListService) {}
getRecipes() {
return this.recipes.slice();
}
getRecipe(index: number) {
return this.recipes[index];
}
addIngridientsToShoppingList(ingridients: Ingridient[]) {
this.slService.addIngridients(ingridients);
}
addRecipe(recipe: Recipe) {
this.recipes.push(recipe);
this.recipesChanged.next(this.recipes.slice());
}
updateRecipe(index: number, newRecipe: Recipe) {
this.recipes[index] = newRecipe;
this.recipesChanged.next(this.recipes.slice());
}
deleteRecipe(index: number) {
this.recipes.splice(index, 1);
this.recipesChanged.next(this.recipes.slice());
}
}
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RecipesStartComponent } from './recipes-start.component';
describe('RecipesStartComponent', () => {
let component: RecipesStartComponent;
let fixture: ComponentFixture<RecipesStartComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RecipesStartComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(RecipesStartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component } from '@angular/core';
@Component({
selector: 'app-recipes-start',
templateUrl: './recipes-start.component.html',
styleUrls: ['./recipes-start.component.css']
})
export class RecipesStartComponent {
}
<div class="row">
<div class="col-md-5">
<app-recipe-list></app-recipe-list>
</div>
<div class="col-md-7">
<router-outlet></router-outlet>
</div>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RecipesComponent } from './recipes.component';
describe('RecipesComponent', () => {
let component: RecipesComponent;
let fixture: ComponentFixture<RecipesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RecipesComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(RecipesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { Recipe } from './recipe.model';
@Component({
selector: 'app-recipes',
templateUrl: './recipes.component.html',
styleUrls: ['./recipes.component.css'],
})
export class RecipesComponent implements OnInit{
selectedRecipe: Recipe;
constructor() {}
ngOnInit() {
}
}
import { Directive, HostBinding, HostListener } from "@angular/core";
@Directive({
selector: '[appDropdown]'
})
export class DropdownDirective {
@HostBinding('class.open') isOpen = false;
@HostListener('click') toogleOpen() {
this.isOpen = !this.isOpen;
}
}
\ No newline at end of file
export class Ingridient {
constructor(public name: string, public amount: number){}
}
\ No newline at end of file
<div class="row">
<div class="col-xs-12">
<form (ngSubmit)="onSubmit(f)" #f="ngForm">
<div class="row">
<div class="col-sm-5 form-group">
<label for="name">Name</label>
<input
type="text"
id="name"
class="form-control"
name="name"
ngModel
required>
</div>
<div class="col-sm-2 form-group">
<label for="amount">Amount</label>
<input
type="number"
id="amount"
class="form-control"
name="amount"
ngModel
required
pattern="^[1-9]+[0-9]*$">
</div>
</div>
<div class="row">
<div class="col-xs-12">
<button
class="btn btn-success"
type="submit"
[disabled]="!f.valid">{{ editMode ? 'Update' : 'Add' }}</button>
<button
class="btn btn-danger"
type="button"
(click)="onDelete()"
*ngIf="editMode">Delete</button>
<button class="btn btn-primary" type="button" (click)="onClear()">Clear</button>
</div>
</div>
</form>
</div>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ShoppingEditComponent } from './shopping-edit.component';
describe('ShoppingEditComponent', () => {
let component: ShoppingEditComponent;
let fixture: ComponentFixture<ShoppingEditComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ShoppingEditComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ShoppingEditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
import { Subscribable, Subscription } from 'rxjs';
import { Ingridient } from 'src/app/shared/ingridient.model';
import { ShoppingListService } from '../shopping-list.service';
@Component({
selector: 'app-shopping-edit',
templateUrl: './shopping-edit.component.html',
styleUrls: ['./shopping-edit.component.css']
})
export class ShoppingEditComponent implements OnInit, OnDestroy{
@ViewChild('f', {static: false}) slForm: NgForm;
subscription: Subscription;
editMode = false;
editedItemIndex: number;
editedItem: Ingridient;
constructor(private slService: ShoppingListService) {}
ngOnInit() {
this.subscription = this.slService.startedEditing
.subscribe(
(index: number) => {
this.editedItemIndex = index;
this.editMode = true;
this.editedItem = this.slService.getIngridient(index);
this.slForm.setValue({
name: this.editedItem.name,
amount: this.editedItem.amount
})
}
);
}
onSubmit(form: NgForm) {
const value = form.value;
const newIngridient = new Ingridient(value.name, value.amount);
if(this.editMode){
this.slService.updateIngridient(this.editedItemIndex, newIngridient);
}else{
this.slService.addIngridient(newIngridient);
}
this.editMode = false;
form.reset();
}
onClear() {
this.slForm.reset();
this.editMode = false;
}
onDelete() {
this.slService.deleteIngridient(this.editedItemIndex);
this.onClear();
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
<div class="row">
<div class="col-xs-10">
<app-shopping-edit></app-shopping-edit>
<hr>
<ul class="list-group">
<a
class="list-group-item"
style="cursor: pointer"
*ngFor="let ingridient of ingridients; let i = index"
(click)="onEditItem(i)"
>
{{ ingridient.name }} ({{ ingridient.amount }})
</a>
</ul>
</div>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ShoppingListComponent } from './shopping-list.component';
describe('ShoppingListComponent', () => {
let component: ShoppingListComponent;
let fixture: ComponentFixture<ShoppingListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ShoppingListComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ShoppingListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs-compat';
import { Ingridient } from '../shared/ingridient.model';
import { ShoppingListService } from './shopping-list.service';
@Component({
selector: 'app-shopping-list',
templateUrl: './shopping-list.component.html',
styleUrls: ['./shopping-list.component.css']
})
export class ShoppingListComponent implements OnInit, OnDestroy{
ingridients: Ingridient[];
private igChangeSub: Subscription;
constructor(private slService: ShoppingListService){}
ngOnInit() {
this.ingridients = this.slService.getIngridients();
this.igChangeSub = this.slService.ingridientsChanged
.subscribe(
(ingredients: Ingridient[]) => {
this.ingridients = ingredients;
}
)
}
onEditItem(index: number) {
this.slService.startedEditing.next(index);
}
ngOnDestroy() {
this.igChangeSub.unsubscribe();
}
}
import { EventEmitter } from "@angular/core";
import { Subject } from "rxjs";
import { Ingridient } from "../shared/ingridient.model";
export class ShoppingListService {
ingridientsChanged = new Subject<Ingridient[]>();
startedEditing = new Subject<number>();
private ingridients: Ingridient[] = [
new Ingridient('Apples', 5),
new Ingridient('Tomatoes', 10)
];
getIngridients() {
return this.ingridients.slice();
}
getIngridient(index: number) {
return this.ingridients[index];
}
addIngridient(ingridient: Ingridient) {
this.ingridients.push(ingridient);
this.ingridientsChanged.next(this.ingridients.slice());
}
addIngridients(ingridients: Ingridient[]) {
this.ingridients.push(...ingridients);
this.ingridientsChanged.next(this.ingridients.slice());
}
updateIngridient(index: number, newIngridient: Ingridient) {
this.ingridients[index] = newIngridient;
this.ingridientsChanged.next(this.ingridients.slice());
}
deleteIngridient(index: number) {
this.ingridients.splice(index, 1);
this.ingridientsChanged.next(this.ingridients.slice());
}
}
\ No newline at end of file
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Project</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
/* You can add global styles to this file, and also import other style files */
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts"
],
"include": [
"src/**/*.d.ts"
]
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022",
"useDefineForClassFields": false,
"lib": [
"ES2022",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false
}
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment