Angular directive: Changing image background dynamically when hovered upon

From the screenshot provided, it is clear that there is a background image for the page and two thumbnails positioned below the text. The objective is to dynamically change the background image of the page to match the thumbnail image when the mouse hovers over the thumbnail.

The code snippet below shows a directive that attempts to achieve this functionality. The main challenge faced is injecting the thumbnail image source into the element to update the page background. Additionally, an error message indicating "Expected 2 arguments, but got 1" is encountered while using the ViewChild method.

import { Directive, ElementRef, HostListener, ViewChild } from '@angular/core';

@Directive({
  selector: '[dynamicBackgroundImg]'
})
export class DynamicBackgroundImgDirective {
  thumbSRC : string;
  @ViewChild('tourBackgroundImg') tourBackgroundImg:ElementRef;


  constructor(private el: ElementRef) {}

  @HostListener('mouseover') onMouseOver() {
    this.ChangeBackgroundImg();
  }

  @HostListener('mouseleave') onMouseLeave() {

  }

  ChangeBackgroundImg() {
    this.thumbSRC = this.el.nativeElement.getAttribute('src');
    alert(this.thumbSRC);
    this.tourBackgroundImg.nativeElement.setAttribute(this.thumbImgSrc);
  }

}

Here is a condensed version of the corresponding HTML code:

<section class="tours-section" [ngClass]="{ 'load-complete' : viewPage }">
  <img class="component-loader" src="../../../assets/icons/loading.svg">
  <div class="tours-wrapper">
    <div class="tours-right page-title">
      <p>{{pageTitle}}</p>
      <picture>
        <source srcset="{{ toursImageUrl }}?fm=webp" type="image/webp">
        <source srcset="{{ toursImageUrl }}?fm=png" type="image/png">
         <!-- The following tag controls the background image of the page, and should be dynamically updated based on the thumbnail image being hovered over. -->
        <img src="{{ toursImageUrl }}?fm=png" alt="{{ toursImageAlt }}" (load)="imageLoaded()" class="section-background" tourBackgroundImg>
      </picture>
    </div>
    <div class="tours-left">
        <div class="tours-thumbs">
          <div class="tours-container">
            <!-- When a user hovers over a thumbnail, the goal is to extract and use the src of the tour image -->
            <figure class="tour-image-small">
              <picture>
                <img src="assets/images/L1000433-min.JPG" alt="" dynamicBackgroundImg>
                <figcaption></figcaption>
              </picture>
            </figure>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>

Here is a link to a screenshot for better visualization of the desired outcome:

https://i.sstatic.net/T2FmI.jpg

Any insights or suggestions on a different approach to achieving this functionality would be greatly appreciated.

Answer №1

There is a more efficient way to achieve the desired functionality in your directive without the need for @ViewChild.

Consider the following streamlined code:

import { Directive, ElementRef, HostListener, Input, Renderer2 } from '@angular/core';

@Directive({
  selector: '[dynamicBackgroundImg]'
})
export class DynamicBackgroundImgDirective {
  @Input() target: HTMLImageElement;

  constructor(private el: ElementRef<HTMLImageElement>, private renderer: Renderer2) {}

  @HostListener('mouseover') onMouseOver() {
    this.ChangeBackgroundImg();
  }

  @HostListener('mouseleave') onMouseLeave() {

  }

  ChangeBackgroundImg() {
     this.renderer.setAttribute(this.target, 'src', this.el.nativeElement.src);
     this.renderer.addClass(this.target, 'fade-in');
  }

}

In your component template, define the target image using a template variable and bind the dynamicBackgroundImg directive to it to trigger the image change on hover:

<img src="{{ toursImageUrl }}?fm=png" alt="{{ toursImageAlt }}" (load)="imageLoaded()" class="section-background" #tourBackgroundImg>

....

<img src="assets/images/L1000433-min.JPG" dynamicBackgroundImg [target]="tourBackgroundImg">

If you encounter the error Expected 2 arguments, but got 1 related to ViewChild, ensure you are utilizing Angular 8 and pass a configuration object as shown:

@ViewChild('element', { static: false })

For Angular animations or a simple CSS fade effect on the image, you can apply the following styles:

.fade-in {
  animation: fadeIn ease 1s;
}

@keyframes fadeIn {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

The directive now includes functionality to add the fade-in class when hovering over the thumbnail image.

Answer №2

If you're looking to dynamically change the background image when hovering over an image, you can achieve this by using an event listener.

Here's an example code snippet:

HTML:

<div class="app-component">
  <h1>App Component</h1>
  hover the image below to change the background:<br>
  <img #myImage src="https://picsum.photos/200/300"/>
</div>

CSS (Setting up the image change):

.app-component {
  background-image: url("https://img.maxisciences.com/s3/frgsd/1024/coronavirus/default_2020-02-28_2bbef01b-f63a-4a59-b31e-788da4e402bb.jpeg");
}

And TypeScript:

import {
  Component,
  OnInit,
  ElementRef,
  ViewChild,
  Renderer2,
AfterViewInit
} from "@angular/core";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements AfterViewInit {
  @ViewChild("myImage") myImage: ElementRef;
  globalInstance: any;

  constructor(private renderer: Renderer2) {}

  ngAfterViewInit() {

    this.globalInstance = this.renderer.listen(
      this.myImage.nativeElement,
      "mouseover",
      () => {
        this.renderer.setStyle(
          document.body.querySelector('.app-component'),
          "background-image",
          `url(${this.myImage.nativeElement.src})`
        );
      }
    );
  }
}

By binding and listening to the 'mouseover' event on the image, you can dynamically change the background of your target element when the event is triggered.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Retrieve all articles from a user using the TypeORM - findAll function

Is there a way to retrieve all articles of a specific user using the TypeORM package? In Sequelize, I have the following function: async findAllByUser(userUuid: string, findOptions: object): Promise<Article[]> { return await Article.findAll< ...

Customizing Components in Angular 2/4 by Overriding Them from a Different Module

Currently, I am utilizing Angular 4.3 and TypeScript 2.2 for my project. My goal is to develop multiple websites by reusing the same codebase. Although most of the websites will have similar structures, some may require different logic or templates. My p ...

Utilize Firestore for automatic saving of data from Angular Reactive Forms

In my Angular application, I am facing an issue where data entered in a form needs to be instantly saved and updated in a Firestore database. This is crucial because multiple users will be entering data simultaneously on the same form, real-time values are ...

Can a TypeScript interface be exported as the result of a function?

Looking for a way to convert JSON schema to a Typescript interface in a more efficient manner. Here is an example of what the current method looks like: //Input var scriptSchema = { type: 'object', properties: { src: { type: &apo ...

When utilizing the catch function callback in Angular 2 with RxJs, the binding to 'this' can trigger the HTTP request to loop repeatedly

I have developed a method to handle errors resulting from http requests. Here is an example of how it functions: public handleError(err: any, caught: Observable<any>): Observable<any> { //irrelevant code omitted this.logger.debug(err);//e ...

How to apply dynamic values for filtering in TypeScript?

My task is to filter out all Portfolio Lead who have English Competency set to No. var data = [{ "Employee Number": 138, "English Competency": "No", "Portfolio Lead": "x", "Maths Competency": "No" }, { "Employee Number": 1385, ...

Difficulty with two-dimensional arrays in Angular and Typescript

I am currently stuck trying to assign values to a 2-dimensional object array in Angular/Typescript. I have noticed that the last assignment seems to override the previous ones, but I cannot pinpoint why this is happening. Could someone please review my cod ...

The method JSON.stringify is not properly converting the entire object to a string

JSON.stringify(this.workout) is not properly stringifying the entire object. The workout variable is an instance of the Workout class, defined as follows: export class Workout { id: string; name: string; exercises: Exercise[]; routine: Ro ...

Create a chronological timeline based on data from a JSON object

Here is a JSON object generated by the backend: { "step1": { "approved": true, "approvalTime": "10-11-2021", "title": "title 1", "description": "description 1" ...

Angular progressive web applications (PWAs) continuously fluctuating in their stability,

I have encountered a problem with my Angular application where it never reaches a stable state, as indicated by ApplicationRef.isStable only emitting false once. The Angular docs mention that: the application will never be stable if you start any kind o ...

Having trouble getting the Typescript overload arrow function to function properly

(I am implementing strict null checks) The arrow function I have includes overloaded types: type INumberConverter = { (value: number): number; (value: null): null; }; const decimalToPercent: INumberConverter = (value: number | nul ...

Unable to utilize the useState hook in TypeScript (Error: 'useState' is not recognized)

Can you identify the issue with the following code? I am receiving a warning from TypeScript when using useState import * as React, { useState } from 'react' const useForm = (callback: any | undefined) => { const [inputs, setInputs] = useS ...

Developing advanced generic functions in Typescript

I am currently working on a Hash Table implementation in Typescript with two separate functions, one to retrieve all keys and another to retrieve all values. Here is the code snippet I have so far: public values() { let values = new Array<T>() ...

Utilize React to display a Selected button within a whitespace

Currently, I am encountering an issue in React where I have a group of buttons at the bottom. Upon selecting a button, the corresponding text should display at the top within a specified whitespace. However, I am looking to have the whitespace already occu ...

Exploring the power of chaining multiple subscriptions in RxJs 6

I have a project using Angular 6, and I'm currently in the process of upgrading rxjs to version 6. In the previous iteration of my app, there was a specific flow where I needed to make sequential calls to observables: 1. Call an observable 2. Perfor ...

TS2349 emerges when incorporating lazy-loading in React

I've been working on refactoring a React 18 app to incorporate lazy loading, following the guidelines provided in the official documentation: One effective method to implement code-splitting in your application is through the dynamic import() syntax ...

Enhancing Code Completion Feature for Multiline Strings in Visual Studio Code

When attempting to include HTML code in a multiline string using backticks within TypeScript, I've noticed that VS Code doesn't offer auto-completion for the HTML tags. Take this example: @Component({ selector: 'app-property-binding&ap ...

The information from the data source is not getting filled in

I recently started working with Angular (Version 14.2.10) and I am trying to make a REST call to populate data in the UI. However, only the header is displayed without any data showing up. I suspect there is a minor issue that I can't seem to pinpoint ...

Executing JavaScript file using TypeScript code in Node.js

Is it possible to execute a JS file from TypeScript code in Node.js? One way to achieve this is by exposing the global scope and assigning values to it. For example: Global Scope (TypeScript): globalThis.names = ['Joe', 'Bob', 'J ...

Encountering a 404 error with Angular 6 routing after refreshing the page when using an Nginx proxy

I currently have my angular application running within a docker container, exposed on port 83. Additionally, I have a separate spring-boot rest app running inside another docker container, exposed on port 8083. On the host server, there is an Nginx server ...