Sunday, 12 September 2021

My angular component EventEmitter is returning wrong variable value

The code

My Product Component.

This is not complete code. I removed some part of the code like server calls and evrything related to backend.

import {Component, EventEmitter, Input, OnChanges, OnInit, Output} from '@angular/core';
import { DataProvider } from 'src/app/providers/data.provider';
import { AuthService } from 'src/app/services/auth.service';
import { InventoryService } from 'src/app/services/inventory.service';

@Component({
  selector: 'app-wide-product-card',
  templateUrl: './wide-product-card.component.html',
  styleUrls: ['./wide-product-card.component.css']
})
export class WideProductCardComponent implements OnInit,OnChanges  {
  @Input() img:string =  "https://source.unsplash.com/650x940"
  @Input() orderTitle:string = "ArtWork Product"
  @Input() orderDescription:string = "Lorem ipsum dolor sit amet, consectetur\n" +
    "                  adipiscing elit. Curabitur cursus tincidunt\n" +
    "                  commodo. Nunc justo nisi, vestibulum."
  @Input() orderprice:number = 2300;
  @Input() category:string;
  @Input() subcategory:string;
  @Input() productId:string
  @Input() extras:any;
  @Input() orderconfig:string = "Chosen config"
  @Input() quantity:number = 1;
  @Input() identifier:string="";
  @Input() configData:any;
  db:any={};
  @Input() showConfig:boolean = true;
  @Input() showActions:boolean = true;
  @Input() showQuantity:boolean = false;
  @Input() showImageInput:boolean = false;
  @Output() changeQuantity : EventEmitter<any> = new EventEmitter();
  @Output() addImage: EventEmitter<any> = new EventEmitter();
  @Output() removeEvent: EventEmitter<any> = new EventEmitter();
  constructor(public inventoryService: InventoryService,private authService: AuthService,private dataProvider: DataProvider) { }
  changeImage(image){
    if (image.target.files[0].size < 500000){
      this.dataProvider.data=this.identifier;
      const a:any = {productId:this.productId,image:image.target.files[0],refData:this.identifier}
      console.log("Event logging just before emitting the event from function changeImage(event)",this.identifier)
      this.addImage.emit(a);
      this.authService.presentToast("Image added successfully")
    } else {
      this.authService.presentToast("Image size should be less than 500kb",3000)
    }
  }
  removeFromWishlist(){
    this.removeEvent.emit({productId:this.productId,ref:this.identifier})
  }
  removeQuantity(){
    this.quantity=this.quantity-1
    this.changeQuantity.emit({quantity:this.quantity,productId:this.productId,ref:this.identifier});
  }
  addQuantity(){
    this.quantity=this.quantity+1
    this.changeQuantity.emit({quantity:this.quantity,productId:this.productId,ref:this.identifier});
  }
  ngOnInit(){
    console.log('Event value emitted from ngOnInit()',this.identifier)
  } 
  ngOnChanges(){
    console.log('Event value emitted from OnChanges() from child component',this.identifier)
  }
}

My component html

<div class="order-card">
  <img [src]="img" alt="" class="order-img">
  <div class="order">
    <div class="order-details">
      <h4 class="order-title"></h4>
      <p class="order-description"></p>
    </div>
    <div class="order-price-and-btns">
      <p class="order-price">Price: ₹</p>
      <ion-button *ngIf="showActions" class="order-remove" (click)="removeFromWishlist()">Remove</ion-button>
      <ion-button *ngIf="showActions" class="order-view"
        href="./product?productId=">View
        Product</ion-button>
        <ion-item button lines="none" *ngIf="showImageInput">
          <input type="file" name="file" max="1" (change)="changeImage($event)" id="file" class="inputfile" />
          <label for="file">Choose a file</label>
        </ion-item>
    </div>
  </div>
  <div class="order-config">
    <h4 class="chosen-config"></h4>
    <ion-list>
      <ion-item *ngFor="let configitem of configData">
        <ion-label></ion-label>
        <ion-icon name="arrow-forward"></ion-icon>
        <ion-label></ion-label>
      </ion-item>
    </ion-list>
    <div *ngIf="showQuantity">
      <ion-item lines="none">
        <button slot="start" [disabled]="quantity<=1" (click)="removeQuantity()">
          <ion-icon name="remove"></ion-icon>
        </button>
        <ion-label color="primary" ></ion-label>
        <button [disabled]="quantity>=10" slot="end" (click)="addQuantity()">
          <ion-icon name="add"></ion-icon>
        </button>
      </ion-item>
    </div>
  </div>
</div>

My Parent page

import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { ModalController } from '@ionic/angular';
import { InvoiceDetailComponent } from 'src/app/modals/invoice-detail/invoice-detail.component';
import {
  FormBuilder,
  FormControl,
  FormGroup,
  Validators,
} from '@angular/forms';
import { Router } from '@angular/router';
import { DataProvider } from 'src/app/providers/data.provider';
import { AuthService } from 'src/app/services/auth.service';
import { InventoryService } from 'src/app/services/inventory.service';
import { PaymentService } from 'src/app/services/payment.service';
import { environment } from 'src/environments/environment';
import firebase from 'firebase/app';
import { AngularFireAnalytics } from '@angular/fire/analytics';
import { InvoiceService } from 'src/app/services/invoice.service';
@Component({
  selector: 'app-checkout',
  templateUrl: './checkout.component.html',
  styleUrls: ['./checkout.component.scss'],
})
export class CheckoutComponent implements OnInit {
  dataCopy;
  constructor(
    private afs: AngularFirestore,
    public dataProvider: DataProvider,
    private paymentService: PaymentService,
    private changeRef: ChangeDetectorRef,
    private authService: AuthService,
    private formbuilder: FormBuilder,
    private inventoryService: InventoryService,
    private router: Router,
    private analytics: AngularFireAnalytics,
    public modalController: ModalController,
    private invoiceService: InvoiceService,
  ) {
    this.form = this.formbuilder.group({
      firstName: this.firstName,
      lastName: this.lastName,
      email: this.email,
      phoneNumber: this.phoneNumber,
      addressLine1: this.addressLine1,
      city: this.city,
      pincode: this.pincode,
      state: this.state,
      country: this.country,
      message: this.message,
      santaCredit: this.santaCredit,
    });
  }
  form: FormGroup;
  firstName: FormControl = new FormControl('', [
    Validators.required,
    Validators.minLength(5),
    Validators.pattern('[a-zA-Z ]*'),
  ]);
  lastName: FormControl = new FormControl('', [
    Validators.required,
    Validators.minLength(5),
    Validators.pattern('[a-zA-Z ]*'),
  ]);
  email: FormControl = new FormControl('', [
    Validators.required,
    Validators.minLength(5),
    Validators.email,
  ]);
  phoneNumber: FormControl = new FormControl('', [
    Validators.required,
    Validators.minLength(10),
    Validators.maxLength(10),
    Validators.pattern('[0-9]{10}'),
  ]);
  addressLine1: FormControl = new FormControl('', [
    Validators.required,
    Validators.minLength(5),
  ]);
  city: FormControl = new FormControl('', [
    Validators.required,
    Validators.minLength(5),
    Validators.pattern('[a-zA-Z ]*'),
  ]);
  pincode: FormControl = new FormControl('', [
    Validators.required,
    Validators.pattern('[0-9]*'),
  ]);
  state: FormControl = new FormControl('', [
    Validators.required,
    Validators.minLength(5),
    Validators.pattern('[a-zA-Z ]*'),
  ]);
  country: FormControl = new FormControl('', [
    Validators.required,
    Validators.minLength(5),
    Validators.pattern('[a-zA-Z ]*'),
  ]);
  santaCredit: FormControl = new FormControl(false, [Validators.required]);
  message: FormControl = new FormControl('', [
    Validators.minLength(10),
    Validators.maxLength(100),
  ]);
  orders = [];
  objectKeys = Object.keys;
  quantity = 1;
  offerFlat: number = 0;
  payableAmount = 0;
  WindowRef: any;
  processingPayment: boolean;
  paymentResponse: any = {};
  orderItems = [];
  santaCoins: number = 0;
  imageRequired: any = [];
  imagesValid: boolean = false;
  log(data) {
    // console.log(data);
  }
  addImage(event) {
    console.log('Event recieved by the addImage(event) eventHandler function', event);
    let allValid = true;
    // console.log('imageRequiredLEngth', this.imageRequired,event,this.dataProvider.data);
    this.imageRequired.forEach((data, index) => {
      if (data.ref == event.refData) {
        this.imageRequired[index].imageReference = event.image;
      }
      if (data.imageReference == undefined) {
        allValid = false;
      }
    });
    this.imagesValid = allValid;
    // console.log('imageRequired', this.imageRequired);
  }
  presentInvoice() {
  }

  get grandTotal(): number {
    var total = 0;
    this.dataCopy.forEach((order) => {
      total += order.price * order.quantity;
    });
    return total - this.offerFlat;
  }
  get grandHeight(): number {
    var total = 0;
    this.orders.forEach((order) => {
      total += order.finalPrice;
    });
    return total;
  }
  setCashback(event) {
    if (event.detail.checked) {
      this.offerFlat = this.santaCoins;
    } else {
      this.offerFlat = 0;
    }
  }
  ngOnInit() {
    this.dataProvider.showOverlay = false;
    this.inventoryService
      .getUserInfo()
      .ref.get()
      .then((user: any) => {
        this.santaCoins = user.data().totalCashback;
      });
    if (this.dataProvider.checkOutdata) {
      this.dataCopy = this.dataProvider.checkOutdata;
      this.dataProvider.checkOutdata.forEach((prod) => {
        // console.log('prod from checkoutdata', prod);
        var docRef = this.afs.collection('products').ref.doc(prod.productData);
        docRef.get().then((data) => {
          if (data.exists) {
            // console.log('Document data:', data);
            let dat: any = data.data();
            if (dat.imageReference) {
              this.imagesValid = false;
              // console.log('identifier >>>',prod.identifier);
              this.imageRequired.push({
                productId: dat.productId,
                ref: prod.identifier,
                imageReference: undefined,
              });
              // console.log('imageRequired', this.imageRequired,this.imageRequired.length);
            }
            this.orderItems.push({
              name: dat.productName,
              sku: prod.productData,
              units: 1,
              selling_price: prod.price - this.offerFlat,
            });
            // console.log('identifier >>>',prod.identifier);
            dat['finalPrice'] = prod.price;
            dat['selections'] = prod.extrasData;
            dat['quantity'] = prod.quantity;
            dat['ref']=prod.identifier;
            dat['cartId']=prod.cartId;
            let config = []
            for (let key of Object.keys(dat.selections)) {
              let selection = dat.selections[key];
              if (selection.type == 'textSel' || selection.type == 'imgSel'){
                config.push({title:selection.sectionTitle,value:selection.title});
              } else if (selection.type == 'faceCount'){
                config.push({title:'Faces',value:selection.faces});
              }
            }
            dat['config'] = config;
            // console.log('dat data dt',dat);
            this.orders.push(dat);
          } else {
            // console.log('No such document!');
          }
        });
      });
      this.WindowRef = this.paymentService.WindowRef;
    } else {
      this.authService.presentToast('Oh Ohh! Checkout expired &#x1F605;');
      this.router.navigate(['']);
    }
  }
  proceedToPay($event) {
    console.log('imagereq',this.imageRequired)
    console.log('imageVald',this.imagesValid)
    if (this.imagesValid) {
      this.dataProvider.showOverlay = true;
      this.processingPayment = true;
      this.payableAmount = this.grandTotal * 100;
      this.imageRequired.forEach((data, index) => {
        console.log(data);
      })
      // console.log('payable amount', this.payableAmount);
      // this.initiatePaymentModal($event);
      // this.analytics.logEvent('Checkout');
    } else {
      this.authService.presentToast('Please add all images by pressing Choose a file on every product.');
    }
  }

  initiatePaymentModal(event) {
    let receiptNumber = `Receipt#${Math.floor(Math.random() * 5123 * 43) + 10}`;

    let orderDetails = {
      amount: this.payableAmount,
      receipt: receiptNumber,
    };

    this.paymentService.createOrder(orderDetails).subscribe(
      (order) => {
        console.log(
          'TCL: CheckoutComponent -> initiatePaymentModal -> order',
          order
        );
        var rzp1 = new this.WindowRef.Razorpay(
          this.preparePaymentDetails(order)
        );
        this.processingPayment = false;
        rzp1.open();
        event.preventDefault();
      },
      (error) => {
        console.log(
          'TCL: CheckoutComponent -> initiatePaymentModal -> error',
          error
        );
        this.authService.presentToast(error.message);
        this.processingPayment = false;
      }
    );
  }

  preparePaymentDetails(order) {
    var ref = this;
    return {
      key: environment.RAZORPAY_KEY_ID, // Enter the Key ID generated from the Dashboard
      amount: this.payableAmount, // Amount is in currency subunits. Default currency is INR. Hence, 29935 refers to 29935 paise or INR 299.35.
      name: 'Pay',
      currency: order.currency,
      order_id: order.id, //This is a sample Order ID. Create an Order using Orders API. (https://razorpay.com/docs/payment-gateway/orders/integration/#step-1-create-an-order). Refer the Checkout form table given below
      image: 'https://angular.io/assets/images/logos/angular/angular.png',
      handler: function (response) {
        ref.handlePayment(response);
      },
      prefill: {
        name:
          this.form.get('firstName')!.value.toString() +
          this.form.get('lastName')!.value.toString(),
        email: this.form.get('email')!.value,
        contact: this.form.get('phoneNumber')!.value,
      },
      theme: {
        color: '#2874f0',
      },
    };
  }

  handlePayment(response) {
    this.paymentService
      .capturePayment({
        amount: this.payableAmount,
        payment_id: response.razorpay_payment_id,
      })
      .subscribe(
        (res) => {
          if (this.offerFlat > 0) {
            this.inventoryService.updateUserData({ totalCashback: 0 });
          }
          this.paymentResponse = res;
          this.changeRef.detectChanges();
          console.log('success response', this.paymentResponse);
          const shippingDetail = {
            order_id: `Order#${
              Math.floor(Math.random() * 5123435345 * 43) + 10
            }`,
            billing_customer_name: this.form.get('firstName')!.value,
            billing_last_name: this.form.get('lastName')!.value,
            billing_city: this.form.get('city')!.value,
            billing_pincode: this.form.get('pincode')!.value,
            billing_state: this.form.get('state')!.value,
            billing_country: this.form.get('country')!.value,
            billing_email: this.form.get('email')!.value,
            billing_phone: this.form.get('phoneNumber')!.value,
            billing_address: this.form.get('addressLine1')!.value,
            order_items: this.orderItems,
            payment_method: 'Prepaid',
            sub_total: this.grandTotal,
            length: 1,
            height: 1,
            weight: 1,
            breadth: 1,
          };
          console.log('shippingDetail', shippingDetail);
          this.paymentService.shipOrder(shippingDetail).subscribe(
            (res: any) => {
              this.authService.presentToast('Payment Successful &#x1F60A;');
              console.log('shipping Confirmed Detail', res);
              let currentOrder = {
                shippingDetail: res.body,
                products: this.orders,
                orderStage: 'live',
                orderId: res.body.order_id,
                shipment_id: res.body.shipment_id,
                orderConfirmed: false,
                grandTotal:this.grandTotal,
                orderMessage: this.message.value || '',
              };
              this.inventoryService.addUserOrder(currentOrder);

              let detail = {
                name:shippingDetail.billing_customer_name,
                address: shippingDetail.billing_address,
                city: shippingDetail.billing_city,
                state: shippingDetail.billing_state,
                country: shippingDetail.billing_country,
                pincode: shippingDetail.billing_pincode,
                mobile: shippingDetail.billing_phone,
                email: shippingDetail.billing_email,
                discount:{available:true,code:'offerCode',price:120},
                grandTotal:this.grandTotal,
                taxCharges:(this.grandTotal/100)*15,
              }
              this.invoiceService.createInvoice(this.orders,detail);
              this.dataProvider.shippingData =
                currentOrder.shippingDetail.shipment_id.toString();
              this.authService.presentToast('Order Placed Successfully ');
              this.router.navigateByUrl(
                'trackorder?shippingId=' +
                  currentOrder.shippingDetail.shipment_id.toString()
              );
            },
            (error) => {
              this.paymentResponse = error;
              this.authService.presentToast(
                error.message +
                  '\nPlease contact hello santa, to complete your order',
                7000
              );
              console.log('Error occured while completing shipment');
            }
          );
        },
        (error) => {
          this.paymentResponse = error;
          console.log('failed response', this.paymentResponse);
        }
      );
  }
}


Html of parent component.

<ion-content [ngStyle]="{'filter': (dataProvider.showOverlay) ? 'blur(10px)' : 'blur(0px)' }">
  <app-header></app-header>
  <div class="home">
    <div class="uk-panel">
      <div class="inner-box" align="center">
        <main id="container">
          <h2 id="checkout-title">Checkout</h2>
          <section id="orders-card">
            <div class="product-row">
              <app-wide-product-card *ngFor="let order of orders" [img]="order.productImages[0].image"
                [orderTitle]="order.productName" [identifier]="order.ref" [configData]="order.config" [showActions]="false" [orderDescription]="order.seoDescription"
                [orderprice]="order.finalPrice" (addImage)="addImage($event)" [productId]="order.productId" [quantity]="order.quantity" [showImageInput]="order.imageReference" [showQuantity]="false"></app-wide-product-card>
            </div>
          </section>
          <section id="orders-table-container">
            <table id="orders-table">
              <thead id="orders-table-head">
                <tr>
                  <th>Product Name</th>
                  <th>Quantity</th>
                  <th>Price</th>
                  <th>Total Price</th>
                </tr>
              </thead>
              <tbody>
                <tr *ngFor="let order of orders">
                  <td></td>
                  <td></td>
                  <td></td>
                  <td></td>
                </tr>
              </tbody>
              <tfoot>
                <tr>
                  <td></td>
                  <td></td>
                  <td>Grand Total</td>
                  <td></td>
                </tr>
              </tfoot>
            </table>
          </section>
          <ion-card>
            <h1 style="text-align: center">
              Delivery info
            </h1>
            <ion-list>
              <form [formGroup]="form" (ngSubmit)="proceedToPay($event)" method="POST">
                <ion-item>
                  <ion-label position="floating">
                    Your First Name
                  </ion-label>
                  <ion-input type="text" placeholder="Your Name" formControlName="firstName" required></ion-input>
                </ion-item>
                <ion-item>
                  <ion-label position="floating">
                    Your Last Name
                  </ion-label>
                  <ion-input type="text" placeholder="Your Name" formControlName="lastName" required></ion-input>
                </ion-item>
                <ion-item>
                  <ion-label position="floating">
                    Your Email
                  </ion-label>
                  <ion-input type="text" placeholder="Enter Your Email" formControlName="email" required>
                  </ion-input>
                </ion-item>
                <ion-item>
                  <ion-label position="floating">
                    Your Phone number
                  </ion-label>
                  <ion-input type="integer" placeholder="Enter Your Phone number" formControlName="phoneNumber"
                    required></ion-input>
                </ion-item>
                <ion-item>
                  <ion-label position="floating">
                    delivery address(including house number)
                  </ion-label>
                  <ion-input type="text" placeholder="delivery address(including house number)"
                    formControlName="addressLine1" required></ion-input>
                </ion-item>
                <ion-item>
                  <ion-label position="floating">
                    city
                  </ion-label>
                  <ion-input type="text" placeholder="enter your city" required formControlName="city">
                  </ion-input>
                </ion-item>
                <ion-item>
                  <ion-label position="floating">
                    Pincode
                  </ion-label>
                  <ion-input type="text" placeholder="Enter Your Pincode" required formControlName="pincode">
                  </ion-input>
                </ion-item>
                <ion-item>
                  <ion-label position="floating">
                    State
                  </ion-label>
                  <ion-input type="text" placeholder="Enter Your State" required formControlName="state">
                  </ion-input>
                </ion-item>
                <ion-item>
                  <ion-label position="floating">
                    Country
                  </ion-label>
                  <ion-input type="text" placeholder="Enter Your country" required formControlName="country">
                  </ion-input>
                </ion-item>
                <ion-item>
                  <input type="checkbox" #checkbox (change)="log(checkbox)">
                  <ion-label>&nbsp;&nbsp;&nbsp;Add a message</ion-label>
                </ion-item>
                <ion-item *ngIf="checkbox.checked">
                  <ion-label position="floating">Your Message</ion-label>
                  <ion-textarea placeholder="Enter your message" formControlName="message"></ion-textarea>
                </ion-item>
                <ion-item *ngIf="santaCoins>0">
                  <ion-checkbox [checked]="false" formControlName="santaCredit" (ionChange)="setCashback($event)"></ion-checkbox>
                  <ion-label>&nbsp;&nbsp;&nbsp;Use Your  Santa Credit for <strong>&#x20B9;</strong> off.</ion-label>
                </ion-item>
                <div align="center">
                  <!-- TODO [disabled]="!form.valid" -->
                  <button type="submit" >
                    <div *ngIf="processingPayment">
                      <ion-spinner name="crescent"></ion-spinner> Processing
                    </div>
                    <div *ngIf="!processingPayment">
                      Proceed to Pay
                    </div>
                  </button>
                </div>
              </form>
            </ion-list>
          </ion-card>
        </main>
      </div>
    </div>
  </div>
  <app-footer></app-footer>
</ion-content>
<div id="overlay" *ngIf="dataProvider.showOverlay">
  <ion-spinner name="crescent" color="tertiary"></ion-spinner>
</div>

The problem

I have a list of product getting displayed by Wide Product Card Component and I am giving it a identifier input value as a unique alphanumeric string for each product or component. The problem is that when I am logging the value of this.identifier in ngOnInit() function it is returning the correct value like {'a':b} but when I am emitting the value this.identifier it is emitting the event of the first component in the DOM. Like if there are two element one below another, and if I click on the event button in second element. Then the emitteed event is of first element.

What have I tried

  1. Transferring data with data provider.
  2. Using different dictionary keys for values (A dumb idea I know).

Image reference

TO verify that the CSS is not interfering with events.

And because the event is triggered by the choose file button the event is firing on the correct element but the event value is returned wrong.

Now in this case when I press the choose file button and choose the file it will return two things one identifier and one file object. And when I press the choose file on the second element and choose a file it returns the same identifier but a different file object. So the event emitter is somehow changing the value of the identifier variable whenever I use the value inside changeImage(event) function in the product component file.

Console logs

enter image description here

enter image description here



from My angular component EventEmitter is returning wrong variable value

No comments:

Post a Comment