Angular Upload Form Data & Files

In the previous tutorial, you learnt to create UI to display products list and do pagination. To finish the Angular tutorials to build a simple admin panel, we add functionalities to add products with image files, update and delete existing products from MYSQL database.

Execute the following command to create ProductUpload component. 

ang14-client>ng generate component ProductUpload

product-upload/product-upload.component.ts

import { Component, OnInit } from '@angular/core';
import { ProductService } from '../services/product.service';
import { AuthService } from '../services/auth.service';
import { HttpClient } from '@angular/common/http';
import { ActivatedRoute, Router } from '@angular/router';

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

  constructor( private route: ActivatedRoute,
    private router: Router,
    private authService: AuthService,
    private productService:ProductService,
    private http: HttpClient) { }

  product!:any;
  productIdFromRoute!:number;
  isEditMode: boolean=false;
  // categories list 
  CatList=[
    {'id':1,'name':'Camera & Photo'},
    {'id':2,'name':'Smart Electronics'},
  ];
  // subcategories list
  SubCatList=[
    {'id':1,'name':'Digital Cameras'},
    {'id':2,'name':'Smart Watches'},
  
  ];
  
  uploadfailed: string ="";

  ngOnInit(): void {

    if(!this.authService.isLoggedin()) this.router.navigateByUrl("/login");
    else{

        const routeParams = this.route.snapshot.paramMap;
        const str_id=routeParams.get('id');
        if(str_id!=='add'){ 
          this.isEditMode=true;
          this.productIdFromRoute = Number(str_id);
          this.getProduct(this.productIdFromRoute);
        }
        else{
        this.product={
            "id":0,
            "name":"",
            "description":"",
            "slug": "",
            "price":0.0,
            "category": 0,
            "subcategory":0,
            };
        }
    }
  
  } 
  
// handle form submission
  onSubmit() {
    let form_data = new FormData();
    form_data.append('id', this.product.id);
    form_data.append('name',  this.product.name);
    form_data.append('description',  this.product.description);
    form_data.append('slug', this.product.slug);
    form_data.append('price', this.product.price);
    form_data.append('category', this.product.category);
    form_data.append('subcategory', this.product.subcategory);
   
    if(selectedFile){
        
        form_data.append('image[]', this.selectedFile,this.selectedFile.name);
        
      }


    if(this.isEditMode){ // update product
      this.productService.updateProduct(form_data,this.product.id)
      .subscribe(
        {
        next: response=> {
             let resObj=JSON.parse(JSON.stringify(response));
             if(resObj.status===200){
              console.log("Update sucessfully",resObj.data);
              this.router.navigateByUrl("/products");
             }
             else{
              this.uploadfailed+=resObj.data;
             }
            },
         error: error => {
             console.log("update failed with the errors", error);
         },
         complete:() => {
             console.log("Post Completed");
         }
        })

      
    }
    else { // insert new product
      this.productService.addProductOne(form_data)
      .subscribe(
        {
        next: (response) => { 
          let resObj=JSON.parse(JSON.stringify(response));
          if(resObj.status===200){
            console.log('added product=',response);
            this.router.navigateByUrl("/products");
          }
          else{
             this.uploadfailed=resObj.data; 
          }
          
        },
        error: (e) => {  
          console.error('Request failed with error'+e)
         
        },
        complete: () => {                       
          console.error('Request completed') 
         
        }
      }
        );     
      
    }
  }

// get a product by itd
public getProduct(id:number) {
     
  this.product=this.productService!.getProductById(id)
  .subscribe(
    {
    next: (response) => {  

      this.product = response; 
      console.log('product received',this.product);
    },
    error: (e) => {   
      console.error('Request failed with error'+e)
  
    },
    complete: () => {                       
      console.error('Request completed')
      
    }
  }
    );
   
  
}
// get selected files
selectedFile:any;
selectFile(event: any): void {
  this.selectedFile = event.target.files[0];

}

}

The ProductUpload component simply defines the onSubmit() function to handle product form data & file submission. We create an instance of FormData and call append() method to add  the filled in data to the form.
Since, the same form is used for product inserting and uploading, we need to check if the form is in editing mode or inserting mode in ngOnInit() function when the component is initialized. 

product-upload/product-upload.component.html
<div class="upload-form">
<form #productForm="ngForm" (ngSubmit)="onSubmit()">
    <input type="hidden" id="id" name="id" [(ngModel)]="product!.id">
    <div class="col-8">
      <label for="name">Name </label>
      <input class="form-control" type="text" id="name" name="name" required [(ngModel)]="product!.name" #name="ngModel">
      
    <div style="color: red;" *ngIf="!name?.valid && (name?.dirty || name?.touched)">
      Enter name
    </div>
   </div>
    <div class="col-8">
        <label for="description">Description </label>
        <input class="form-control" type="text" id="description" name="description" required [(ngModel)]="product!.description" #des="ngModel">
  
      <div style="color: red;" *ngIf="!des?.valid && (des?.dirty || des?.touched)">
        Invalid Description
      </div>
      </div>
      <div class="col-8">
        <label for="price">Price</label>
        <input class="form-control" type="number" id="price" name="price" required [(ngModel)]="product!.price" #pr="ngModel">

      <div style="color: red;" *ngIf="!pr?.valid && (pr?.dirty || pr?.touched)">
        Invalid Price
      </div>
      </div>
      <div class="col-8">
        <label for="product_slug">Slug</label>
        <input class="form-control" type="text" id="slug" name="slug" required [(ngModel)]="product!.slug" #sl="ngModel">
      <div style="color: red;" *ngIf="!sl?.valid && (sl?.dirty || sl?.touched)">
        Invalid Slug
      </div>
      </div>
    
    <div class="col-8">
      <label for="category">Category </label>
      <select #cat="ngModel" class="form-control" id="category" name="category" required [(ngModel)]="product!.category">
        <option [ngValue]="c.id" *ngFor="let c of CatList">
          {{c.name}}
        </option>
      </select>
    </div>
    
    <div class="col-8">
        <label for="subcategory">Sub Category </label>
        <select class="form-control" id="subcategory" name="subcategory" required [(ngModel)]="product!.subcategory"

        >
          <option [ngValue]="sc.id" *ngFor="let sc of SubCatList">
            {{sc.name}}
          </option>
        </select>
      </div>

      <div class="col-8">
        <label class="btn btn-default p-0">
          <input type="file" id="image" name="image" (change)="selectFile($event)" />
        </label>
      </div>  


   <div class="col-3">
      <button class="btn btn-primary" type="submit" [disabled]="!productForm.valid">Submit</button>
   </div>
   </form>
   <div class="col-3">
    {{uploadfailed}}
  </div>
</div>

product-upload/product-upload.component.css
.upload-form{
    padding-left: 15%;
}

Upload product service to add functions to get product by id and handle product inserting, updating, and deleting.
servcies/product.service.ts
// get product by id
  public getProductById(id:number): Observable<any>{
    
    return this.http.get(this.baseUrl+"product/"+id)
    .pipe(
      map((data) => {
       
        let jsonObj=JSON.parse(JSON.stringify(data));
        console.log("data by id=",jsonObj.data);
        return jsonObj.data;
     }),
      catchError((err) => {
        console.error(err);
        throw err;
      }
      )
  
    )

  }
 

// add product
public addProductOne(product: any) {
   
  let headers = new HttpHeaders()
  .set('Authorization', this.token || '{}');
  return this.http.post(this.baseUrl+"products/add", product,{'headers':headers})
  .pipe(
    map((data) => {
     
      console.log("data added=",data);
      return data;
   }),
    catchError((err) => {
      console.error(err);
      throw err;
    }
    )

  )
}
// delete product
public deleteProduct(id:Number) {
  
  let headers = new HttpHeaders()
  .set('Authorization', this.token || '{}');
  this.http.delete(this.baseUrl+"products/"+id+"/delete",{'headers':headers})
    .subscribe(
      {
     next: response=> {
          let resObj=JSON.parse(JSON.stringify(response));
          if(resObj.status===200){
            console.log("Deleted sucessfully: "+resObj.data);
            this.router.navigateByUrl("/product");
          }
          else{
            alert(resObj.data);
          }
      },
      error: err => {
          console.log("Delete failed with the errors",err);
      },
      complete: () => {
          console.log("Post Completed");
      }
    }
    )
}
// update product
public updateProduct(product: any,id:Number) {
 
  let headers = new HttpHeaders()
  .set('Authorization', this.token || '{}');
 
  return this.http.post(this.baseUrl+"products/"+id, product,{'headers':headers})
  .pipe(
    map((data) => {
      
      console.log("data updated=",data);
      return data;
   }),
    catchError((err) => {
      console.error(err);
      throw err;
    }
    )

  )  
  
 
}

Finally update the app-routing.module.ts file to add the last three routes as shown below:
..............................
import { ProductUpoadComponent } from './product-upload/product-upload.component';

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { path: 'register', component: RegisterComponent },
  { path: 'products', component: ProductComponent },
  { path: 'product/:id', component: ProductUploadComponent }, //update route
  { path: 'product/add', component: ProductUploadComponent }, // insert route
  { path: 'products/:id', component: ProductComponent}, // delete route

];
...........................

Now save the project. Make sure Apache and MYSQL are running. Login to the app and you should be able to add new products, update, and delete products.




Video Demo

Comments

Popular posts from this blog

Angular with PHP & MYSQL to display products list & paging

PHP Mysql Database Migration Using Phinx