javascript - Min Angular-komponent EventEmitter returnerer feil variabelverdi
Koden
Min produktkomponent.
Dette er ikke fullstendig kode. Jeg fjernet noen deler av koden, som serverkall og alt som har med backend å gjøre.
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)
}
}
Min komponent html
<div class="order-card">
<img [src]="img" alt="{{orderTitle}}" class="order-img">
<div class="order">
<div class="order-details">
<h4 class="order-title">{{orderTitle}}</h4>
<p class="order-description">{{orderDescription}}</p>
</div>
<div class="order-price-and-btns">
<p class="order-price">Price: ₹{{orderprice * quantity}}</p>
<ion-button *ngIf="showActions" class="order-remove" (click)="removeFromWishlist()">Remove</ion-button>
<ion-button *ngIf="showActions" class="order-view"
href="./product?productId={{this.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">{{orderconfig}}</h4>
<ion-list>
<ion-item *ngFor="let configitem of configData">
<ion-label>{{configitem.title}}</ion-label>
<ion-icon name="arrow-forward"></ion-icon>
<ion-label>{{configitem.value}}</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" >{{quantity}}</ion-label>
<button [disabled]="quantity>=10" slot="end" (click)="addQuantity()">
<ion-icon name="add"></ion-icon>
</button>
</ion-item>
</div>
</div>
</div>
Min overordnede side
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 😅');
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 😊');
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 av overordnet komponent.
<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>{{order.productName}}</td>
<td>{{order.quantity}}</td>
<td>{{order.productPrice}}</td>
<td>{{order.finalPrice*order.quantity}}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td></td>
<td></td>
<td>Grand Total</td>
<td>{{grandTotal}}</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> 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> Use Your {{santaCoins}} Santa Credit for <strong>₹{{this.santaCoins}}</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>
Problemet
Jeg har en liste over produkter som vises av Wide Product Card-komponenten, og jeg gir den en identifikator-verdi som input som en unik alfanumerisk streng for hvert produkt eller komponent. Problemet er at når jeg logger verdien av this.identifier i ngOnInit()-funksjonen, returnerer den riktig verdi som {'a':b}, men når jeg emitterer verdien this.identifier, emitterer den hendelsen til den første komponenten i DOM-en. Som om det er to elementer, ett under det andre, og hvis jeg klikker på hendelsesknappen i det andre elementet, da er den emiterte hendelsen fra det første elementet.
Hva har jeg prøvd
- Overfører data med dataleverandør.
- Å bruke forskjellige ordboknøkler for verdier (En dum idé, jeg vet).
Bilde referanse
Å verifisere at CSS ikke forstyrrer hendelser.
Og fordi hendelsen utløses av velg fil-knappen, blir hendelsen utløst på riktig element, men hendelsesverdien returneres feil.
Nå i dette tilfellet, når jeg trykker på knappen for å velge fil og velger filen, vil det returnere to ting: én identifikator og ett filobjekt. Og når jeg trykker på velg fil på det andre elementet og velger en fil, returnerer det samme identifikatoren, men et annet filobjekt. Så hendelsesutløseren endrer på en måte verdien av identifier-variabelen hver gang jeg bruker verdien inne i changeImage(event)-funksjonen i produktkomponentfilen.
Konsolllogger
Svar #1
Det er en feil med den tilpassede input-filen og Ionic. Hvordan skjulte du det virkelige input-tekstboksen?
hvis du bruker
<ion-item button>
<input type="file" hidden>
</ion-item>
eller med stil
display: none;
og det er flere forekomster på samme side i Angular, når du klikker på en knapp, utløses hendelsen til den første knappen/inputen i DOM-en.
En løsningen er å bruke CSS-stilen:
.inputfile{
opacity:0;
}
det er noen referanse til egendefinert inndatatekstboks i Ionic her: Hvordan lage et egendefinert filinndata i Ionic 2+ med en knappestil?
OPPDATERING Eksempel på å lage en fungerende egendefinert input-fil-knapp med hendelse i Ionic:
<ion-button>
<input type="file" name="file" id="file" (change)="changeImage($event)" class="inputfile">
<label for="file">Choose Photo</label>
</ion-button>
og CSS-klassen:
.inputfile {
opacity: 0;
position: absolute;
top: 0;
width: 100%;
height: 100%;
left: 0;
z-index: 999;
}
Dette er eksemplet
Svar #2
Først må du logge din @Input() identifier:string=""; i OnChanges() for å være sikker på at du får verdien hver gang den endres.
Sekunder, kan du vise nøyaktig hvor du sendte til din overordnede komponent?

