1

I am trying to add product images on clicking the add button in screen and to remove using delete button. These should be loaded dynamically when user clicks add or delete button. But when I dynamically inject html and bind click event it's not working. On clicking it, function is not calling. I have simplified the code below. HTML file:

<div class="product_images">
          <div class="imageHeading">
                        <p>
                            Images
                        </p>
                    </div>
                    <div class="kt-form__group">
                        <div class="row">
                            <div class="col-lg-2 imageLabel">Main Image</div>
                            <div class="col-lg-3 imageLabel">Choose Image</div>
                            <div class="col-lg-2 imageLabel">Image</div>
                            <div class="col-lg-2 imageLabel">Actions</div>
                        </div>
                    </div>
                    <div class="imagesContainer" [innerHtml]="containerToAdd | sanitizeHtml">
                    </div>
                </div>

Ts file

// Angular
import { Component, OnInit, ElementRef, ViewChild, ChangeDetectionStrategy, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
// Material
import { SelectionModel } from '@angular/cdk/collections';
import { MatPaginator, MatSort, MatSnackBar, MatDialog, MatRadioButton } from '@angular/material';
import { ProductManagementService } from '../../../../../core/e-commerce/_services/product-management.service';
import { ToastrService } from 'ngx-toastr';
import { ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'kt-product-edit',
  templateUrl: './product-edit.component.html',
  styleUrls: ['./product-edit.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class ProductEditComponent implements OnInit {

previewUrl : any = "/assets/media/images/noimage.jpg";
containerToAdd : string = "";

constructor(
    private products: ProductManagementService,
    private router: Router,
    private route: ActivatedRoute,
    private toastr: ToastrService,
    private cdr: ChangeDetectorRef,
    private FB: FormBuilder,
    ) {

    }
 ngOnInit() {
    this.addImage();
}
addImage(){
     this.containerToAdd = `
      "<div class="kt-form__group image-container container-1">
          <div class="row">
              <div class="col-lg-2"><input type="checkbox" /></div>
              <div class="col-lg-3"><input type="file" accept="image/*" (change)="imagePreview($event)" /></div>
              <div class="col-lg-2"><img [src]="previewUrl" class="prod_image" /></div>
              <div class="col-lg-2">
                  <span class="deleteElement" (click)="deleteImage()">
                    Del
                  </span>
              </div>
          </div>
      </div>"`;
  }
  deleteImage() {
    console.log("deleteImage");
  }
}

When I click that span with that click event, deleteImage() function is not calling.

5
  • move deleteImage outside of addImage. Commented Mar 4, 2020 at 15:50
  • Not sure about Angular but in inline JS you pass a function reference to onlick: <span class="deleteElement" (click)="deleteImage"> Commented Mar 4, 2020 at 15:50
  • @marekful It is correct to use (click)="someFunction()" in angular. Commented Mar 4, 2020 at 15:52
  • @JDunken It's outside addImage function only Commented Mar 4, 2020 at 15:54
  • Using JSX attributes through innerHTML will not be possible and we will not be supporting that. Instead I would recommend conditionally showing buttons using if statements within your render() function. This way you can dynamically add and remove buttons, and use JSX onClick, and have it all type checked. For reference: github.com/ionic-team/stencil/issues/692 Commented Mar 4, 2020 at 16:06

2 Answers 2

2

You are binding to the innerHtml so any angular syntax in the html you are using for binding is not going to work as it is not a compile angular template. So your click binding is invalid. A simple *ngIf should be good.

<div class="product_images">
    <div class="imageHeading">
        <p>
            Images
        </p>
    </div>
    <div class="kt-form__group">
        <div class="row">
            <div class="col-lg-2 imageLabel">Main Image</div>
            <div class="col-lg-3 imageLabel">Choose Image</div>
            <div class="col-lg-2 imageLabel">Image</div>
            <div class="col-lg-2 imageLabel">Actions</div>
        </div>
    </div>
    <div class="imagesContainer" [innerHtml]="containerToAdd | sanitizeHtml">
        <div class="kt-form__group image-container container-1" *ngIf="addImage">
            <div class="row">
                <div class="col-lg-2">
                    <input type="checkbox" />
                </div>
                <div class="col-lg-3">
                    <input type="file" accept="image/*" (change)="imagePreview($event)" />
                </div>
                <div class="col-lg-2"><img [src]="previewUrl" class="prod_image" /></div>
                <div class="col-lg-2">
                    <span class="deleteElement" (click)="deleteImage()">
                    Del
                  </span>
                </div>
            </div>
        </div>
    </div>
</div>
export class MyComponent{
previewUrl : any = "/assets/media/images/noimage.jpg";
public addImage = false

  constructor(
    private products: ProductManagementService,
    private router: Router,
    private route: ActivatedRoute,
    private toastr: ToastrService,
    private cdr: ChangeDetectorRef,
    private FB: FormBuilder) {
  }

  ngOnInit() {
    this.addImage();
  }

  addImage(){
     this.addImage = true;
  }

  deleteImage() {
   this.addImage = false
    console.log("deleteImage");
  }
}

EDIT 1:

<div class="product_images">
    <div class="imageHeading">
        <p>
            Images
        </p>
    </div>
    <div class="kt-form__group">
        <div class="row">
            <div class="col-lg-2 imageLabel">Main Image</div>
            <div class="col-lg-3 imageLabel">Choose Image</div>
            <div class="col-lg-2 imageLabel">Image</div>
            <div class="col-lg-2 imageLabel">Actions</div>
        </div>
    </div>
    <div class="imagesContainer">
        <div class="kt-form__group image-container container-1" *ngFor="let image of images; let index = i">
            <div class="row">
                <div class="col-lg-2">
                    <input type="checkbox" />
                </div>
                <div class="col-lg-3">
                    <input type="file" accept="image/*" (change)="imagePreview($event, image)" />
                </div>
                <div class="col-lg-2"><img [src]="previewUrl" class="prod_image" /></div>
                <div class="col-lg-2">
                    <span class="deleteElement" (click)="deleteImage(i)">
                    Del
                  </span>
                </div>
            </div>
        </div>
    </div>
</div>
export class MyComponent{
previewUrl : any = "/assets/media/images/noimage.jpg";
public images = []

  constructor(
    private products: ProductManagementService,
    private router: Router,
    private route: ActivatedRoute,
    private toastr: ToastrService,
    private cdr: ChangeDetectorRef,
    private FB: FormBuilder) {
  }

  ngOnInit() {
    this.addImage();
  }

  addImage(){
     this.images.push({});
  }

  deleteImage(index: number) {
    this.images.splice(index, 1)
    console.log("deleteImage");
  }

  imagePreview($event, image){
    image.path = event.value;
  }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Actually I need this to dynamic because multiple images should be added. Images div should be added on clicking the add button and removing by clicking the delete button.
In that case, you need an array that stores your data(image,title). in html you use *ngFor to show array data in content. when you click delete, slice from array, when you add just push to array. that's all
array means we should defined how many but here images should added only clicking add button.
@svkks You don't need to know the exact count of the array. Start with an empty array first and then add items to it when use clicks on Add button. In the template, just loop over the array using *ngFor directive. Using innerHTML attribute is not recommended and is not the best approach.
1

i think it helps and you would add some code in your favour:

html:

<div class="product_images">
  <div class="imageHeading">
    <p>
      Images
    </p>
  </div>
  <div class="kt-form__group">
    <div class="row">
      <div class="col-lg-2 imageLabel">Main Image</div>
      <div class="col-lg-3 imageLabel">Choose Image</div>
      <div class="col-lg-2 imageLabel">Image</div>
      <div class="col-lg-2 imageLabel">Actions</div>
    </div>
  </div>
  <div class="imagesContainer" #elementRef>
  </div>
</div>

ts:

@ViewChild("elementRef", { static: true }) deletableItem: ElementRef<
    HTMLDivElement
  >;
  containerToAdd: any;

  previewUrl: any;

  constructor(private sanitizer: DomSanitizer, private renderer: Renderer2) {}

  ngOnInit() {
    this.addImage();
  }

  ngAfterViewInit() {}

  addImage() {
    let span: HTMLSpanElement = this.renderer.createElement("span");
    this.renderer.addClass(span, "deleteElement");
    this.renderer.listen(span, "click", () => {
      console.log("I am going to delete you");
    });
    span.innerHTML = "Del";

    let divHoldsSpan: HTMLDivElement = this.renderer.createElement("div");
    this.renderer.addClass(divHoldsSpan, "col-lg-2");
    this.renderer.appendChild(divHoldsSpan, span);

    let image: HTMLImageElement = this.renderer.createElement("img");
    this.renderer.addClass(image, "prod_image");
    image.src = this.previewUrl;

    let divHoldsImage: HTMLDivElement = this.renderer.createElement("div");
    this.renderer.addClass(divHoldsImage, "col-lg-2");
    this.renderer.appendChild(divHoldsImage, image);

    let imageInput: HTMLInputElement = this.renderer.createElement("input");
    imageInput.type = "file";
    imageInput.accept = "image/*";
    this.renderer.listen(imageInput, "change", event => {
      // console.log("YOUR FILE,SIR:",e.target.files);

      for (let index = 0; index < event.target.files.length; index++) {
        let reader = new FileReader();
        const fileToUpload = event.target.files[index];
        reader.onload = (e: any) => {
          image.src = e.target.result;
        };
        reader.readAsDataURL(event.target.files[index]);
      }
    });

    let divHoldsImageInput: HTMLDivElement = this.renderer.createElement("div");
    this.renderer.addClass(divHoldsImageInput, "col-lg-3");
    this.renderer.appendChild(divHoldsImageInput, imageInput);

    let checkboxInput: HTMLInputElement = this.renderer.createElement("input");
    checkboxInput.type = "checkbox";

    let divHoldsCheckbox: HTMLDivElement = this.renderer.createElement("div");
    this.renderer.addClass(divHoldsCheckbox, "col-lg-2");
    this.renderer.appendChild(divHoldsCheckbox, checkboxInput);

    let divRow: HTMLDivElement = this.renderer.createElement("div");
    this.renderer.addClass(divRow, "row");
    this.renderer.appendChild(divRow, divHoldsCheckbox);
    this.renderer.appendChild(divRow, divHoldsImageInput);
    this.renderer.appendChild(divRow, divHoldsImage);
    this.renderer.appendChild(divRow, divHoldsSpan);

    let divKtForm = this.renderer.createElement("div");
    this.renderer.addClass(divKtForm, "kt-form__group");
    this.renderer.addClass(divKtForm, "image-container");
    this.renderer.addClass(divKtForm, "container-1");
    this.renderer.appendChild(divKtForm, divRow);

    this.renderer.appendChild(this.deletableItem.nativeElement, divKtForm);
  }

  deleteImage() {
    console.log;
  }

stackblitz

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.