Tutorial FullStack – Angular 10 + Laravel 8 + MySQL

Angular 10 ( Frontend )

Instala Angular 10

npm install -g @angular/cli@10
ng version

1 – Create app / Crear app

ng new app-tutofox
Would you like to add Angular routing? (y/N): y

Solución de error

https://es.stackoverflow.com/questions/450769/error-en-ng-new-angular-cli

2 – Install bootstrap / Instalar bootstrap

ng add @ng-bootstrap/ng-bootstrap

3 – Create module / Crear module

ng generate module person --routing

4 – Create component / Crear componente

ng generate component person/index
ng generate component person/create
ng generate component person/edit

5 – Create routes / Crear rutas

src/app/person/person-routing.module.ts

/.../

import { IndexComponent } from './index/index.component';
import { CreateComponent } from './create/create.component';
import { EditComponent } from './edit/edit.component';
  
const routes: Routes = [
  { path: 'person', redirectTo: 'person/index', pathMatch: 'full'},
  { path: 'person/index', component: IndexComponent },
  { path: 'person/create', component: CreateComponent },
  { path: 'person/edit/:idPerson', component: EditComponent } 
];

/.../

6 – Create interface / Crear interface

ng generate interface person/person

src/app/person/person.ts

export interface Person {
    id: number;
    name: string;
    email: string;
    phone: number;
}

7 – Create services / Crear servicios

src/app/person/person.service.ts

ng generate service person/person
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

import {  Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

import { Person } from './person';

@Injectable({
  providedIn: 'root'
})
export class PersonService {

  private apiURL = "http://localhost:8000/api/person/";

  httpOptions = {
     headers: new HttpHeaders({
       'Content-Type': 'application/json'
     })
  }

  constructor(private httpClient: HttpClient) { }

  getAll(): Observable<Person[]> {
   return this.httpClient.get<Person[]>(this.apiURL)
   .pipe(
     catchError(this.errorHandler)
   )
 }

 create(person): Observable<Person> {
   return this.httpClient.post<Person>(this.apiURL, JSON.stringify(person), this.httpOptions)
   .pipe(
     catchError(this.errorHandler)
   )
 }

 find(id): Observable<Person> {
   return this.httpClient.get<Person>(this.apiURL + id)
   .pipe(
     catchError(this.errorHandler)
   )
 }

 update(id, person): Observable<Person> {
   return this.httpClient.put<Person>(this.apiURL + id, JSON.stringify(person), this.httpOptions)
   .pipe(
     catchError(this.errorHandler)
   )
 }

 delete(id){
   return this.httpClient.delete<Person>(this.apiURL + id, this.httpOptions)
   .pipe(
     catchError(this.errorHandler)
   )
 }

 errorHandler(error) {
   let errorMessage = '';
   if(error.error instanceof ErrorEvent) {
     errorMessage = error.error.message;
   } else {
     errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
   }
   return throwError(errorMessage);
 }

}

8 – Components / Componentes

Component Index

src/app/person/index/index.component.ts

import { Component, OnInit } from '@angular/core';

import { PersonService } from '../person.service';
import { Person } from '../person';

@Component({
  selector: 'app-index',
  templateUrl: './index.component.html',
  styleUrls: ['./index.component.css']
})
export class IndexComponent implements OnInit {

  persons: Person[] = [];

  // constructor() { }
  constructor(public personService: PersonService) { }

  ngOnInit(): void {
    this.personService.getAll().subscribe((data: Person[])=>{
      this.persons = data;
      console.log(this.persons);
    })
  }

  deletePerson(id){
    this.personService.delete(id).subscribe(res => {
         this.persons = this.persons.filter(item => item.id !== id);
         console.log('Person deleted successfully!');
    })
  }

}

src/app/person/index/index.component.html

<section>

    <div class="d-flex justify-content-between">
      <h4>List person</h4>
      <a routerLink="/person/create" class="btn btn-success">Create New Person</a>
    </div>

    <br>
    
    <table class="table ">
      <tr>
        <th>ID</th>
        <th>Name</th>
        <th>Email</th>
        <th>Phone</th>
        <th width="220px">Action</th>
      </tr>
      <tr *ngFor="let person of persons">
        <td>{{ person.id }}</td>
        <td>{{ person.name }}</td>
        <td>{{ person.email }}</td>
        <td>{{ person.phone }}</td>
        <td>
          <a href="#" [routerLink]="['/person/', 'edit', person.id  ]" class="btn btn-primary">Edit</a>
          <button type="button" (click)="deletePerson(person.id)" class="btn btn-danger">Delete</button>
        </td>
      </tr>
    </table>
 </section>

Component Create

src/app/person/create/create.component.ts

import { Component, OnInit } from '@angular/core';
import { PersonService } from '../person.service';
import { Router } from '@angular/router';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'app-create',
  templateUrl: './create.component.html',
  styleUrls: ['./create.component.css']
})
export class CreateComponent implements OnInit {

  form: FormGroup;

  constructor(
    public personService: PersonService,
    private router: Router
  ) { }

  ngOnInit(): void {

    this.form = new FormGroup({
      name:  new FormControl('', [ Validators.required, Validators.pattern('^[a-zA-ZÁáÀàÉéÈèÍíÌìÓóÒòÚúÙùÑñüÜ \-\']+') ]),
      email: new FormControl('', [ Validators.required, Validators.email ]),
      phone: new FormControl('', [ Validators.required, Validators.pattern("^[0-9]*$") ])
    });

  }

  get f(){
    return this.form.controls;
  }

  submit(){
    console.log(this.form.value);
    this.personService.create(this.form.value).subscribe(res => {
         console.log('Person created successfully!');
         this.router.navigateByUrl('person/index');
    })
  }

}

src/app/person/create/create.component.html

<div>

  <div class="d-flex justify-content-between">
    <h4>Form customer</h4>
    <a href="#" routerLink="/person/index" class="btn btn-primaryt">Back</a>
  </div>

  <hr/>

    <form [formGroup]="form" (ngSubmit)="submit()">

        <div class="form-group row">
            <div class="col-md-6 ">
              <label for="title">Name:</label>
              <input
                formControlName="name"
                id="name"
                type="text"
                class="form-control">
              <div *ngIf="f.name.touched && f.name.invalid" class="alert alert-danger">
                  <div *ngIf="f.name.errors.required">*Name is required.</div>
                  <div *ngIf="f.name.errors.pattern">*The name must only contain letters.</div>
              </div>
            </div>
        </div>

        <div class="form-group row">
            <div class="col-md-6 ">
              <label for="email">Email:</label>
              <input
                formControlName="email"
                id="email"
                type="text"
                class="form-control">
              <div *ngIf="f.email.touched && f.email.invalid" class="alert alert-danger">
                  <div *ngIf="f.email.errors.required">*Email is required.</div>
                  <div *ngIf="f.email.errors.email">*The email must be a valid email address.</div>
              </div>
            </div>
        </div>

        <div class="form-group row">
            <div class="col-md-6 ">
              <label for="phone">Phone:</label>
              <input
                formControlName="phone"
                id="phone"
                type="text"
                class="form-control">
              <div *ngIf="f.phone.touched && f.phone.invalid" class="alert alert-danger">
                  <div *ngIf="f.phone.errors.required">*Phone is required.</div>
                  <div *ngIf="f.phone.errors.pattern">*The phone must only contain numbers.</div>
              </div>
            </div>
        </div>

        <button class="btn btn-primary" type="submit" [disabled]="!form.valid">Submit</button>
    </form>
</div>

Component Edit

src/app/person/edit/edit.component.ts

import { Component, OnInit } from '@angular/core';

import { PersonService } from '../person.service';
import { ActivatedRoute, Router } from '@angular/router';
import { FormGroup, FormControl, Validators} from '@angular/forms';
import { Person } from '../person';

@Component({
  selector: 'app-edit',
  templateUrl: './edit.component.html',
  styleUrls: ['./edit.component.css']
})
export class EditComponent implements OnInit {

  id: number;
  person: Person;
  form: FormGroup;

  constructor(
    public personService: PersonService,
    private route: ActivatedRoute,
    private router: Router
  ) { }

  ngOnInit(): void {
    this.id = this.route.snapshot.params['idPerson'];
    this.personService.find(this.id).subscribe((data: Person)=>{
      this.person = data;
    });

    this.form = new FormGroup({
      name:  new FormControl('', [ Validators.required, Validators.pattern('^[a-zA-ZÁáÀàÉéÈèÍíÌìÓóÒòÚúÙùÑñüÜ \-\']+') ]),
      email: new FormControl('', [ Validators.required, Validators.email ]),
      phone: new FormControl('', [ Validators.required, Validators.pattern("^[0-9]*$") ])
    });

  }

  get f(){
    return this.form.controls;
  }

  submit(){
    console.log(this.form.value);
    this.personService.update(this.id, this.form.value).subscribe(res => {
         console.log('Person updated successfully!');
         this.router.navigateByUrl('person/index');
    })
  }

}

src/app/person/edit/edit.component.html

<div class="container">
  
    <div class="d-flex justify-content-between">
      <h4>Edit person</h4>
      <a href="#" routerLink="/person/index" class="btn btn-primaryt">Back</a>
    </div>

    <hr>

    <form [formGroup]="form" (ngSubmit)="submit()">


        <div class="form-group row">
            <div class="col-md-6 ">
              <label for="title">Name:</label>
              <input
                formControlName="name"
                [(ngModel)]="person.name"
                id="name"
                type="text"
                class="form-control">
              <div *ngIf="f.name.touched && f.name.invalid" class="alert alert-danger">
                  <div *ngIf="f.name.errors.required">*Name is required.</div>
                  <div *ngIf="f.name.errors.pattern">*The name must only contain letters.</div>
              </div>
            </div>
        </div>

        <div class="form-group row">
            <div class="col-md-6 ">
              <label for="email">Email:</label>
              <input
                formControlName="email"
                [(ngModel)]="person.email"
                id="email"
                type="text"
                class="form-control">
              <div *ngIf="f.email.touched && f.email.invalid" class="alert alert-danger">
                  <div *ngIf="f.email.errors.required">*Email is required.</div>
                  <div *ngIf="f.email.errors.email">*The email must be a valid email address.</div>
              </div>
            </div>
        </div>

        <div class="form-group row">
            <div class="col-md-6 ">
              <label for="phone">Phone:</label>
              <input
                [(ngModel)]="person.phone"
                formControlName="phone"
                id="phone"
                type="text"
                class="form-control">
              <div *ngIf="f.phone.touched && f.phone.invalid" class="alert alert-danger">
                  <div *ngIf="f.phone.errors.required">*Phone is required.</div>
                  <div *ngIf="f.phone.errors.pattern">*The phone must only contain numbers.</div>
              </div>
            </div>
        </div>

        <button class="btn btn-primary" type="submit" [disabled]="!form.valid">Update</button>
    </form>
</div>

9 – Configure person module / Configurar modulo person

src/app/person/person.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { PersonRoutingModule } from './person-routing.module';

import { IndexComponent } from './index/index.component';
import { CreateComponent } from './create/create.component';
import { EditComponent } from './edit/edit.component';

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

@NgModule({
  declarations: [
    IndexComponent,
    CreateComponent,
    EditComponent
  ],
  imports: [
    CommonModule,
    PersonRoutingModule,
    FormsModule,
    ReactiveFormsModule
  ]
})
export class PersonModule { }

10 – Configure App / Configurar App

src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { PersonModule } from './person/person.module';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    PersonModule,
    HttpClientModule,
    NgbModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

src/app/app.component.html

<div class="container" style="padding:20px;">
	<h1 style="text-align:center;">
		<a href="/person/index"> Full Stack - Angular 10 & Laravel 8 </a>
	</h1>
	<hr>
  <main>
  <router-outlet></router-outlet>
  </main>
</div>

Laravel 8 ( Backend )

1 – Create project / Crear proyecto

composer create-project --prefer-dist laravel/laravel backend

2 – Configure Database / Configurar Base de datos

.env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=name_database
DB_USERNAME=user_database
DB_PASSWORD='password_database'

3 – Create migrate / Crear migracion

php artisan make:migration create_person_table 

database/migrations/2021_05_17_011217_create_person_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePersonTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('person', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email');
            $table->bigInteger('phone');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('person');
    }
}

Run migrate / ejecutar migrate

 php artisan migrate

4 – Create Model / Crear Modelo

php artisan make:model Person

app/Models/Person.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Person extends Model
{
    use HasFactory;

    protected $table = "person";

    protected $fillable = [
      'name',
      'email',
      'phone'
    ];
 
}

5 – Create Controller / Crear Controlador

php artisan make:controller API/PersonController

app/Http/Controllers/API/PersonController.php

<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
Use App\Models\Person;
Use Log;

class PersonController extends Controller
{
    // https://carbon.now.sh/
    public function getAll(){
      $data = Person::get();
      return response()->json($data, 200);
    }

    public function create(Request $request){
      $data['name'] = $request['name'];
      $data['email'] = $request['email'];
      $data['phone'] = $request['phone'];
      Person::create($data);
      return response()->json([
          'message' => "Successfully created",
          'success' => true
      ], 200);
    }

    public function delete($id){
      $res = Person::find($id)->delete();
      return response()->json([
          'message' => "Successfully deleted",
          'success' => true
      ], 200);
    }

    public function get($id){
      $data = Person::find($id);
      return response()->json($data, 200);
    }

    public function update(Request $request,$id){
      $data['name'] = $request['name'];
      $data['email'] = $request['email'];
      $data['phone'] = $request['phone'];
      Person::find($id)->update($data);
      return response()->json([
          'message' => "Successfully updated",
          'success' => true
      ], 200);
    }
}

6 – Create routes / Crear rutas

routes/api.php

use App\Http\Controllers\API\PersonController;

Route::prefix('person')->group(function () {
    Route::get('/',[ PersonController::class, 'getAll']);
    Route::post('/',[ PersonController::class, 'create']);
    Route::delete('/{id}',[ PersonController::class, 'delete']);
    Route::get('/{id}',[ PersonController::class, 'get']);
    Route::put('/{id}',[ PersonController::class, 'update']);
});

GitHub

https://github.com/artyom-developer/angular-10-laravel-8

2 comentarios en «Tutorial FullStack – Angular 10 + Laravel 8 + MySQL»

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *