├── .editorconfig
├── .gitignore
├── .vscode
├── extensions.json
├── launch.json
└── tasks.json
├── README.md
├── angular-15-crud-example.png
├── angular.json
├── package-lock.json
├── package.json
├── src
├── app
│ ├── app-routing.module.ts
│ ├── app.component.css
│ ├── app.component.html
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── components
│ │ ├── add-tutorial
│ │ │ ├── add-tutorial.component.css
│ │ │ ├── add-tutorial.component.html
│ │ │ ├── add-tutorial.component.spec.ts
│ │ │ └── add-tutorial.component.ts
│ │ ├── tutorial-details
│ │ │ ├── tutorial-details.component.css
│ │ │ ├── tutorial-details.component.html
│ │ │ ├── tutorial-details.component.spec.ts
│ │ │ └── tutorial-details.component.ts
│ │ └── tutorials-list
│ │ │ ├── tutorials-list.component.css
│ │ │ ├── tutorials-list.component.html
│ │ │ ├── tutorials-list.component.spec.ts
│ │ │ └── tutorials-list.component.ts
│ ├── models
│ │ ├── tutorial.model.spec.ts
│ │ └── tutorial.model.ts
│ └── services
│ │ ├── tutorial.service.spec.ts
│ │ └── tutorial.service.ts
├── assets
│ └── .gitkeep
├── favicon.ico
├── index.html
├── main.ts
└── styles.css
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://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 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
3 | "recommendations": ["angular.ng-template"]
4 | }
5 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
3 | "version": "0.2.0",
4 | "configurations": [
5 | {
6 | "name": "ng serve",
7 | "type": "pwa-chrome",
8 | "request": "launch",
9 | "preLaunchTask": "npm: start",
10 | "url": "http://localhost:4200/"
11 | },
12 | {
13 | "name": "ng test",
14 | "type": "chrome",
15 | "request": "launch",
16 | "preLaunchTask": "npm: test",
17 | "url": "http://localhost:9876/debug.html"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
3 | "version": "2.0.0",
4 | "tasks": [
5 | {
6 | "type": "npm",
7 | "script": "start",
8 | "isBackground": true,
9 | "problemMatcher": {
10 | "owner": "typescript",
11 | "pattern": "$tsc",
12 | "background": {
13 | "activeOnStart": true,
14 | "beginsPattern": {
15 | "regexp": "(.*?)"
16 | },
17 | "endsPattern": {
18 | "regexp": "bundle generation complete"
19 | }
20 | }
21 | }
22 | },
23 | {
24 | "type": "npm",
25 | "script": "test",
26 | "isBackground": true,
27 | "problemMatcher": {
28 | "owner": "typescript",
29 | "pattern": "$tsc",
30 | "background": {
31 | "activeOnStart": true,
32 | "beginsPattern": {
33 | "regexp": "(.*?)"
34 | },
35 | "endsPattern": {
36 | "regexp": "bundle generation complete"
37 | }
38 | }
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angular 15 example project: CRUD with Rest API
2 |
3 | Build an Angular 15 CRUD example App to consume Rest APIs, display, modify & search data.
4 |
5 | Tutorial Application in that:
6 | - Each Tutorial has id, title, description, published status.
7 | - We can create, retrieve, update, delete Tutorials.
8 | - There is a Search bar for finding Tutorials by title.
9 |
10 | 
11 |
12 | Run `ng serve --port 8081` for a dev server. Navigate to `http://localhost:8081/`. The app will automatically reload if you change any of the source files.
13 |
14 | For instruction, please visit:
15 | > [Angular 15 example: CRUD with Rest API](https://www.bezkoder.com/angular-15-crud-example/)
16 |
17 | More Practice:
18 | > [Angular 15 Pagination example | ngx-pagination](https://www.bezkoder.com/angular-15-pagination-ngx/)
19 |
20 | > [Angular 15 JWT Authentication & Authorization with Web API](https://www.bezkoder.com/angular-15-jwt-auth/)
21 |
22 | > [Angular 15 File upload example with Progress bar](https://www.bezkoder.com/angular-15-file-upload/)
23 |
24 | > [Angular 15 Multiple Files upload example with Progress Bar](https://www.bezkoder.com/angular-15-multiple-file-upload/)
25 |
26 | > [Angular 15 Form Validation example](https://www.bezkoder.com/angular-15-form-validation/)
27 |
28 | Fullstack with Node:
29 |
30 | > [Angular 15 + Node Express + MySQL example](https://www.bezkoder.com/angular-15-node-js-express-mysql/)
31 |
32 | > [Angular 15 + Node Express + PostgreSQL example](https://www.bezkoder.com/angular-15-node-js-express-postgresql/)
33 |
34 | > [Angular 15 + Node Express + MongoDB example](https://www.bezkoder.com/angular-15-node-js-express-mongodb/)
35 |
36 | > [Angular 15 + Node Express: File upload example](https://www.bezkoder.com/angular-15-node-express-file-upload/)
37 |
38 | Fullstack with Spring Boot:
39 |
40 | > [Angular 15 + Spring Boot example](https://www.bezkoder.com/spring-boot-angular-15-crud/)
41 |
42 | > [Angular 15 + Spring Boot + MySQL example](https://www.bezkoder.com/spring-boot-angular-15-mysql/)
43 |
44 | > [Angular 15 + Spring Boot + PostgreSQL example](https://www.bezkoder.com/spring-boot-angular-15-postgresql/)
45 |
46 | > [Angular 15 + Spring Boot + MongoDB example](https://www.bezkoder.com/spring-boot-angular-15-mongodb/)
47 |
48 | > [Angular 15 + Spring Boot: File upload example](https://www.bezkoder.com/angular-15-spring-boot-file-upload/)
49 |
50 | Fullstack with Django:
51 | > [Angular + Django example](https://www.bezkoder.com/django-angular-13-crud-rest-framework/)
52 |
53 | > [Angular + Django + MySQL](https://www.bezkoder.com/django-angular-mysql/)
54 |
55 | > [Angular + Django + PostgreSQL](https://www.bezkoder.com/django-angular-postgresql/)
56 |
57 | > [Angular + Django + MongoDB](https://www.bezkoder.com/django-angular-mongodb/)
58 |
59 | Security:
60 | > [Angular 15 + Spring Boot: JWT Authentication and Authorization example](https://www.bezkoder.com/angular-15-spring-boot-jwt-auth/)
61 |
62 | > [Angular 15 + Node.js Express: JWT Authentication and Authorization example](https://www.bezkoder.com/node-js-angular-15-jwt-auth/)
63 |
64 | Serverless with Firebase:
65 | > [Angular 15 Firebase CRUD with Realtime DataBase](https://www.bezkoder.com/angular-15-firebase-crud/)
66 |
67 | > [Angular 15 Firestore CRUD example](https://www.bezkoder.com/angular-15-firestore-crud/)
68 |
69 | > [Angular 15 Firebase Storage: File Upload/Display/Delete example](https://www.bezkoder.com/angular-15-firebase-storage/)
70 |
71 | Integration (run back-end & front-end on same server/port)
72 | > [How to integrate Angular with Node Restful Services](https://bezkoder.com/integrate-angular-12-node-js/)
73 |
74 | > [How to Integrate Angular with Spring Boot Rest API](https://bezkoder.com/integrate-angular-12-spring-boot/)
--------------------------------------------------------------------------------
/angular-15-crud-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bezkoder/angular-15-crud-example/6005468f5fd437e25dd44e1e39d850cbe2f213e7/angular-15-crud-example.png
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "angular-15-crud": {
7 | "projectType": "application",
8 | "schematics": {},
9 | "root": "",
10 | "sourceRoot": "src",
11 | "prefix": "app",
12 | "architect": {
13 | "build": {
14 | "builder": "@angular-devkit/build-angular:browser",
15 | "options": {
16 | "outputPath": "dist/angular-15-crud",
17 | "index": "src/index.html",
18 | "main": "src/main.ts",
19 | "polyfills": [
20 | "zone.js"
21 | ],
22 | "tsConfig": "tsconfig.app.json",
23 | "assets": [
24 | "src/favicon.ico",
25 | "src/assets"
26 | ],
27 | "styles": [
28 | "src/styles.css"
29 | ],
30 | "scripts": []
31 | },
32 | "configurations": {
33 | "production": {
34 | "budgets": [
35 | {
36 | "type": "initial",
37 | "maximumWarning": "500kb",
38 | "maximumError": "1mb"
39 | },
40 | {
41 | "type": "anyComponentStyle",
42 | "maximumWarning": "2kb",
43 | "maximumError": "4kb"
44 | }
45 | ],
46 | "outputHashing": "all"
47 | },
48 | "development": {
49 | "buildOptimizer": false,
50 | "optimization": false,
51 | "vendorChunk": true,
52 | "extractLicenses": false,
53 | "sourceMap": true,
54 | "namedChunks": true
55 | }
56 | },
57 | "defaultConfiguration": "production"
58 | },
59 | "serve": {
60 | "builder": "@angular-devkit/build-angular:dev-server",
61 | "configurations": {
62 | "production": {
63 | "browserTarget": "angular-15-crud:build:production"
64 | },
65 | "development": {
66 | "browserTarget": "angular-15-crud:build:development"
67 | }
68 | },
69 | "defaultConfiguration": "development"
70 | },
71 | "extract-i18n": {
72 | "builder": "@angular-devkit/build-angular:extract-i18n",
73 | "options": {
74 | "browserTarget": "angular-15-crud:build"
75 | }
76 | },
77 | "test": {
78 | "builder": "@angular-devkit/build-angular:karma",
79 | "options": {
80 | "polyfills": [
81 | "zone.js",
82 | "zone.js/testing"
83 | ],
84 | "tsConfig": "tsconfig.spec.json",
85 | "assets": [
86 | "src/favicon.ico",
87 | "src/assets"
88 | ],
89 | "styles": [
90 | "src/styles.css"
91 | ],
92 | "scripts": []
93 | }
94 | }
95 | }
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-15-crud",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "watch": "ng build --watch --configuration development",
9 | "test": "ng test"
10 | },
11 | "private": true,
12 | "dependencies": {
13 | "@angular/animations": "^15.0.0",
14 | "@angular/common": "^15.0.0",
15 | "@angular/compiler": "^15.0.0",
16 | "@angular/core": "^15.0.0",
17 | "@angular/forms": "^15.0.0",
18 | "@angular/platform-browser": "^15.0.0",
19 | "@angular/platform-browser-dynamic": "^15.0.0",
20 | "@angular/router": "^15.0.0",
21 | "bootstrap": "^4.6.2",
22 | "rxjs": "~7.5.0",
23 | "tslib": "^2.3.0",
24 | "zone.js": "~0.12.0"
25 | },
26 | "devDependencies": {
27 | "@angular-devkit/build-angular": "^15.0.3",
28 | "@angular/cli": "~15.0.3",
29 | "@angular/compiler-cli": "^15.0.0",
30 | "@types/jasmine": "~4.3.0",
31 | "jasmine-core": "~4.5.0",
32 | "karma": "~6.4.0",
33 | "karma-chrome-launcher": "~3.1.0",
34 | "karma-coverage": "~2.2.0",
35 | "karma-jasmine": "~5.1.0",
36 | "karma-jasmine-html-reporter": "~2.0.0",
37 | "typescript": "~4.8.2"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } from '@angular/router';
3 | import { TutorialsListComponent } from './components/tutorials-list/tutorials-list.component';
4 | import { TutorialDetailsComponent } from './components/tutorial-details/tutorial-details.component';
5 | import { AddTutorialComponent } from './components/add-tutorial/add-tutorial.component';
6 |
7 | const routes: Routes = [
8 | { path: '', redirectTo: 'tutorials', pathMatch: 'full' },
9 | { path: 'tutorials', component: TutorialsListComponent },
10 | { path: 'tutorials/:id', component: TutorialDetailsComponent },
11 | { path: 'add', component: AddTutorialComponent }
12 | ];
13 |
14 | @NgModule({
15 | imports: [RouterModule.forRoot(routes)],
16 | exports: [RouterModule]
17 | })
18 | export class AppRoutingModule { }
--------------------------------------------------------------------------------
/src/app/app.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bezkoder/angular-15-crud-example/6005468f5fd437e25dd44e1e39d850cbe2f213e7/src/app/app.component.css
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 | import { RouterTestingModule } from '@angular/router/testing';
3 | import { AppComponent } from './app.component';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(async () => {
7 | await TestBed.configureTestingModule({
8 | imports: [
9 | RouterTestingModule
10 | ],
11 | declarations: [
12 | AppComponent
13 | ],
14 | }).compileComponents();
15 | });
16 |
17 | it('should create the app', () => {
18 | const fixture = TestBed.createComponent(AppComponent);
19 | const app = fixture.componentInstance;
20 | expect(app).toBeTruthy();
21 | });
22 |
23 | it(`should have as title 'angular-15-crud'`, () => {
24 | const fixture = TestBed.createComponent(AppComponent);
25 | const app = fixture.componentInstance;
26 | expect(app.title).toEqual('angular-15-crud');
27 | });
28 |
29 | it('should render title', () => {
30 | const fixture = TestBed.createComponent(AppComponent);
31 | fixture.detectChanges();
32 | const compiled = fixture.nativeElement as HTMLElement;
33 | expect(compiled.querySelector('.content span')?.textContent).toContain('angular-15-crud app is running!');
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/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.css']
7 | })
8 | export class AppComponent {
9 | title = 'Angular 15 CRUD example';
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 | import { FormsModule } from '@angular/forms';
4 | import { HttpClientModule } from '@angular/common/http';
5 |
6 | import { AppRoutingModule } from './app-routing.module';
7 | import { AppComponent } from './app.component';
8 | import { AddTutorialComponent } from './components/add-tutorial/add-tutorial.component';
9 | import { TutorialDetailsComponent } from './components/tutorial-details/tutorial-details.component';
10 | import { TutorialsListComponent } from './components/tutorials-list/tutorials-list.component';
11 |
12 | @NgModule({
13 | declarations: [
14 | AppComponent,
15 | AddTutorialComponent,
16 | TutorialDetailsComponent,
17 | TutorialsListComponent
18 | ],
19 | imports: [
20 | BrowserModule,
21 | AppRoutingModule,
22 | FormsModule,
23 | HttpClientModule
24 | ],
25 | providers: [],
26 | bootstrap: [AppComponent]
27 | })
28 | export class AppModule { }
29 |
--------------------------------------------------------------------------------
/src/app/components/add-tutorial/add-tutorial.component.css:
--------------------------------------------------------------------------------
1 | .submit-form {
2 | max-width: 400px;
3 | margin: auto;
4 | }
--------------------------------------------------------------------------------
/src/app/components/add-tutorial/add-tutorial.component.html:
--------------------------------------------------------------------------------
1 |
36 |
--------------------------------------------------------------------------------
/src/app/components/add-tutorial/add-tutorial.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AddTutorialComponent } from './add-tutorial.component';
4 |
5 | describe('AddTutorialComponent', () => {
6 | let component: AddTutorialComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ AddTutorialComponent ]
12 | })
13 | .compileComponents();
14 |
15 | fixture = TestBed.createComponent(AddTutorialComponent);
16 | component = fixture.componentInstance;
17 | fixture.detectChanges();
18 | });
19 |
20 | it('should create', () => {
21 | expect(component).toBeTruthy();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/src/app/components/add-tutorial/add-tutorial.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { Tutorial } from 'src/app/models/tutorial.model';
3 | import { TutorialService } from 'src/app/services/tutorial.service';
4 |
5 | @Component({
6 | selector: 'app-add-tutorial',
7 | templateUrl: './add-tutorial.component.html',
8 | styleUrls: ['./add-tutorial.component.css']
9 | })
10 | export class AddTutorialComponent {
11 |
12 | tutorial: Tutorial = {
13 | title: '',
14 | description: '',
15 | published: false
16 | };
17 | submitted = false;
18 |
19 | constructor(private tutorialService: TutorialService) { }
20 |
21 | saveTutorial(): void {
22 | const data = {
23 | title: this.tutorial.title,
24 | description: this.tutorial.description
25 | };
26 |
27 | this.tutorialService.create(data)
28 | .subscribe({
29 | next: (res) => {
30 | console.log(res);
31 | this.submitted = true;
32 | },
33 | error: (e) => console.error(e)
34 | });
35 | }
36 |
37 | newTutorial(): void {
38 | this.submitted = false;
39 | this.tutorial = {
40 | title: '',
41 | description: '',
42 | published: false
43 | };
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/src/app/components/tutorial-details/tutorial-details.component.css:
--------------------------------------------------------------------------------
1 | .edit-form {
2 | max-width: 400px;
3 | margin: auto;
4 | }
--------------------------------------------------------------------------------
/src/app/components/tutorial-details/tutorial-details.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Tutorial
4 |
5 | {{ currentTutorial.title }}
6 |
7 |
8 |
9 | {{ currentTutorial.description }}
10 |
11 |
12 |
13 | {{ currentTutorial.published ? "Published" : "Pending" }}
14 |
15 |
16 |
20 | Edit
21 |
22 |
23 |
24 |
25 |
26 |
Please click on a Tutorial...
27 |
28 |
29 |
30 |
31 |
89 |
90 |
91 |
92 |
Cannot access this Tutorial...
93 |
94 |
95 |
--------------------------------------------------------------------------------
/src/app/components/tutorial-details/tutorial-details.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { TutorialDetailsComponent } from './tutorial-details.component';
4 |
5 | describe('TutorialDetailsComponent', () => {
6 | let component: TutorialDetailsComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ TutorialDetailsComponent ]
12 | })
13 | .compileComponents();
14 |
15 | fixture = TestBed.createComponent(TutorialDetailsComponent);
16 | component = fixture.componentInstance;
17 | fixture.detectChanges();
18 | });
19 |
20 | it('should create', () => {
21 | expect(component).toBeTruthy();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/src/app/components/tutorial-details/tutorial-details.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, OnInit } from '@angular/core';
2 | import { TutorialService } from 'src/app/services/tutorial.service';
3 | import { ActivatedRoute, Router } from '@angular/router';
4 | import { Tutorial } from 'src/app/models/tutorial.model';
5 |
6 | @Component({
7 | selector: 'app-tutorial-details',
8 | templateUrl: './tutorial-details.component.html',
9 | styleUrls: ['./tutorial-details.component.css']
10 | })
11 | export class TutorialDetailsComponent implements OnInit {
12 |
13 | @Input() viewMode = false;
14 |
15 | @Input() currentTutorial: Tutorial = {
16 | title: '',
17 | description: '',
18 | published: false
19 | };
20 |
21 | message = '';
22 |
23 | constructor(
24 | private tutorialService: TutorialService,
25 | private route: ActivatedRoute,
26 | private router: Router) { }
27 |
28 | ngOnInit(): void {
29 | if (!this.viewMode) {
30 | this.message = '';
31 | this.getTutorial(this.route.snapshot.params["id"]);
32 | }
33 | }
34 |
35 | getTutorial(id: string): void {
36 | this.tutorialService.get(id)
37 | .subscribe({
38 | next: (data) => {
39 | this.currentTutorial = data;
40 | console.log(data);
41 | },
42 | error: (e) => console.error(e)
43 | });
44 | }
45 |
46 | updatePublished(status: boolean): void {
47 | const data = {
48 | title: this.currentTutorial.title,
49 | description: this.currentTutorial.description,
50 | published: status
51 | };
52 |
53 | this.message = '';
54 |
55 | this.tutorialService.update(this.currentTutorial.id, data)
56 | .subscribe({
57 | next: (res) => {
58 | console.log(res);
59 | this.currentTutorial.published = status;
60 | this.message = res.message ? res.message : 'The status was updated successfully!';
61 | },
62 | error: (e) => console.error(e)
63 | });
64 | }
65 |
66 | updateTutorial(): void {
67 | this.message = '';
68 |
69 | this.tutorialService.update(this.currentTutorial.id, this.currentTutorial)
70 | .subscribe({
71 | next: (res) => {
72 | console.log(res);
73 | this.message = res.message ? res.message : 'This tutorial was updated successfully!';
74 | },
75 | error: (e) => console.error(e)
76 | });
77 | }
78 |
79 | deleteTutorial(): void {
80 | this.tutorialService.delete(this.currentTutorial.id)
81 | .subscribe({
82 | next: (res) => {
83 | console.log(res);
84 | this.router.navigate(['/tutorials']);
85 | },
86 | error: (e) => console.error(e)
87 | });
88 | }
89 |
90 | }
--------------------------------------------------------------------------------
/src/app/components/tutorials-list/tutorials-list.component.css:
--------------------------------------------------------------------------------
1 | .list {
2 | text-align: left;
3 | max-width: 750px;
4 | margin: auto;
5 | }
--------------------------------------------------------------------------------
/src/app/components/tutorials-list/tutorials-list.component.html:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
Tutorials List
22 |
23 | -
29 | {{ tutorial.title }}
30 |
31 |
32 |
33 |
36 |
37 |
43 |
44 |
--------------------------------------------------------------------------------
/src/app/components/tutorials-list/tutorials-list.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { TutorialsListComponent } from './tutorials-list.component';
4 |
5 | describe('TutorialsListComponent', () => {
6 | let component: TutorialsListComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ TutorialsListComponent ]
12 | })
13 | .compileComponents();
14 |
15 | fixture = TestBed.createComponent(TutorialsListComponent);
16 | component = fixture.componentInstance;
17 | fixture.detectChanges();
18 | });
19 |
20 | it('should create', () => {
21 | expect(component).toBeTruthy();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/src/app/components/tutorials-list/tutorials-list.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Tutorial } from 'src/app/models/tutorial.model';
3 | import { TutorialService } from 'src/app/services/tutorial.service';
4 |
5 | @Component({
6 | selector: 'app-tutorials-list',
7 | templateUrl: './tutorials-list.component.html',
8 | styleUrls: ['./tutorials-list.component.css']
9 | })
10 | export class TutorialsListComponent implements OnInit {
11 |
12 | tutorials?: Tutorial[];
13 | currentTutorial: Tutorial = {};
14 | currentIndex = -1;
15 | title = '';
16 |
17 | constructor(private tutorialService: TutorialService) { }
18 |
19 | ngOnInit(): void {
20 | this.retrieveTutorials();
21 | }
22 |
23 | retrieveTutorials(): void {
24 | this.tutorialService.getAll()
25 | .subscribe({
26 | next: (data) => {
27 | this.tutorials = data;
28 | console.log(data);
29 | },
30 | error: (e) => console.error(e)
31 | });
32 | }
33 |
34 | refreshList(): void {
35 | this.retrieveTutorials();
36 | this.currentTutorial = {};
37 | this.currentIndex = -1;
38 | }
39 |
40 | setActiveTutorial(tutorial: Tutorial, index: number): void {
41 | this.currentTutorial = tutorial;
42 | this.currentIndex = index;
43 | }
44 |
45 | removeAllTutorials(): void {
46 | this.tutorialService.deleteAll()
47 | .subscribe({
48 | next: (res) => {
49 | console.log(res);
50 | this.refreshList();
51 | },
52 | error: (e) => console.error(e)
53 | });
54 | }
55 |
56 | searchTitle(): void {
57 | this.currentTutorial = {};
58 | this.currentIndex = -1;
59 |
60 | this.tutorialService.findByTitle(this.title)
61 | .subscribe({
62 | next: (data) => {
63 | this.tutorials = data;
64 | console.log(data);
65 | },
66 | error: (e) => console.error(e)
67 | });
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/src/app/models/tutorial.model.spec.ts:
--------------------------------------------------------------------------------
1 | import { Tutorial } from './tutorial.model';
2 |
3 | describe('Tutorial', () => {
4 | it('should create an instance', () => {
5 | expect(new Tutorial()).toBeTruthy();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/src/app/models/tutorial.model.ts:
--------------------------------------------------------------------------------
1 | export class Tutorial {
2 | id?: any;
3 | title?: string;
4 | description?: string;
5 | published?: boolean;
6 | }
--------------------------------------------------------------------------------
/src/app/services/tutorial.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { TutorialService } from './tutorial.service';
4 |
5 | describe('TutorialService', () => {
6 | let service: TutorialService;
7 |
8 | beforeEach(() => {
9 | TestBed.configureTestingModule({});
10 | service = TestBed.inject(TutorialService);
11 | });
12 |
13 | it('should be created', () => {
14 | expect(service).toBeTruthy();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/src/app/services/tutorial.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 | import { Observable } from 'rxjs';
4 | import { Tutorial } from '../models/tutorial.model';
5 |
6 | const baseUrl = 'http://localhost:8080/api/tutorials';
7 |
8 | @Injectable({
9 | providedIn: 'root'
10 | })
11 | export class TutorialService {
12 |
13 | constructor(private http: HttpClient) { }
14 |
15 | getAll(): Observable {
16 | return this.http.get(baseUrl);
17 | }
18 |
19 | get(id: any): Observable {
20 | return this.http.get(`${baseUrl}/${id}`);
21 | }
22 |
23 | create(data: any): Observable {
24 | return this.http.post(baseUrl, data);
25 | }
26 |
27 | update(id: any, data: any): Observable {
28 | return this.http.put(`${baseUrl}/${id}`, data);
29 | }
30 |
31 | delete(id: any): Observable {
32 | return this.http.delete(`${baseUrl}/${id}`);
33 | }
34 |
35 | deleteAll(): Observable {
36 | return this.http.delete(baseUrl);
37 | }
38 |
39 | findByTitle(title: any): Observable {
40 | return this.http.get(`${baseUrl}?title=${title}`);
41 | }
42 | }
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bezkoder/angular-15-crud-example/6005468f5fd437e25dd44e1e39d850cbe2f213e7/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bezkoder/angular-15-crud-example/6005468f5fd437e25dd44e1e39d850cbe2f213e7/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular15Crud
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
2 |
3 | import { AppModule } from './app/app.module';
4 |
5 |
6 | platformBrowserDynamic().bootstrapModule(AppModule)
7 | .catch(err => console.error(err));
8 |
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 | @import "~bootstrap/dist/css/bootstrap.css";
--------------------------------------------------------------------------------
/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/app",
6 | "types": []
7 | },
8 | "files": [
9 | "src/main.ts"
10 | ],
11 | "include": [
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": "./",
6 | "outDir": "./dist/out-tsc",
7 | "forceConsistentCasingInFileNames": true,
8 | "strict": true,
9 | "noImplicitOverride": true,
10 | "noPropertyAccessFromIndexSignature": true,
11 | "noImplicitReturns": true,
12 | "noFallthroughCasesInSwitch": true,
13 | "sourceMap": true,
14 | "declaration": false,
15 | "downlevelIteration": true,
16 | "experimentalDecorators": true,
17 | "moduleResolution": "node",
18 | "importHelpers": true,
19 | "target": "ES2022",
20 | "module": "ES2022",
21 | "useDefineForClassFields": false,
22 | "lib": [
23 | "ES2022",
24 | "dom"
25 | ]
26 | },
27 | "angularCompilerOptions": {
28 | "enableI18nLegacyMessageIdFormat": false,
29 | "strictInjectionParameters": true,
30 | "strictInputAccessModifiers": true,
31 | "strictTemplates": true
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/spec",
6 | "types": [
7 | "jasmine"
8 | ]
9 | },
10 | "include": [
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------