The ViewChild from NgbModalModule in @ng-bootstrap/ng-bootstrap for Angular 6 is causing the modal to return as

I have successfully integrated ng bootstrap into my project, specifically utilizing the modal module to display a contact form. The form includes input fields for email and message, as well as a submit button. You can find the ngbootstrap module I am using here. Everything is displaying correctly. My goal now is to implement a feature where, upon clicking the 'send' button (refer to the template code below), the button will become disabled and show 'sending...' This involves manipulating the properties of the modal component.

I've reviewed several related questions on this topic without success:

Angular 2 @ViewChild annotation returns undefined How to get reference of the component associated with ElementRef in Angular 2

Despite these efforts, I consistently receive 'undefined' when attempting to use it in my component.

@ViewChild('submitButton') submitButton: ElementRef;

Included in my template html is the following:

<button #submitButton
   id="submitButton"
   class="btn btn-success w-100"
   [disabled]="!contactForm.valid"
>Send</button>

Below is the complete component code snippet, where I'm trying to access the submitButton within the onSubmit() method:

import {
    Component,
    OnInit,
    Renderer2,
    ViewChild,
    ElementRef,
    AfterViewInit
} from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { NgbModal, ModalDismissReasons } from '@ng-bootstrap/ng-bootstrap';

import { ContactService } from '../../services/contact.service';

@Component({
    selector: 'app-contact-modal',
    templateUrl: './contact-modal.component.html'
})
export class ContactModalComponent implements OnInit, AfterViewInit {

    closeResult: string;
    contactForm: FormGroup;
    //@ViewChild('submitButton') submitButton;
    @ViewChild('submitButton') submitButton: ElementRef;

    constructor(
        private modalService: NgbModal,
        private contactService: ContactService,
        private renderer: Renderer2
    ) { }

    ngOnInit() {
        this.initForm();
    }

    ngAfterViewInit() {
        console.log(this.submitButton);
    }

    private getDismissReason(reason: any): string {
        if (reason === ModalDismissReasons.ESC) {
          return 'by pressing ESC';
        } else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
          return 'by clicking on a backdrop';
        } else {
          return  `with: ${reason}`;
        }
    }

    private initForm() {
        const email = '';
        const message = '';

        this.contactForm = new FormGroup({
            email: new FormControl(null, Validators.email),
            message: new FormControl(null, Validators.required)
        });
    }

    onSubmit() {

        const email = this.contactForm.value.email;
        const message = this.contactForm.value.message;

        // at this point this.submitButton is UNDEFINED
        console.log(this.submitButton);

        //this.submitButton.nativeElement.style.backgroundColor = 'black';

        this.contactService.sendContactRequest(email, message, (submitSuccess: boolean) => {
            if (submitSuccess) {

                console.log('SUCCESS UPDATE UI');
                this.contactForm.value.email = '';
                this.contactForm.value.message = '';

            } else {
                console.log('ERROR update UI');
            }
        });
    }

    open(content) {
        console.log('in open func');
        this.modalService.open(content, {ariaLabelledBy: 'modal-basic-title'})
        .result
        .then(
            (result) => {
                console.log(`Closed with: ${result}`);
            },
            (reason) => {
                console.log(`Dismissed ${this.getDismissReason(reason)}`);
            }
        );
    }
}

Here is my full template markup:

<a
    class="nav-link"
    (click)="open(content)"
><i class="fas fa-envelope"></i></a>

<ng-template #content let-c="close" let-d="dismiss">

    <div class="modal-header">
        <h4 class="modal-title" id="modal-basic-title">Contact Us</h4>
        <button type="button" class="close" aria-label="Close" (click)="c()">
          <i class="fas fa-times"></i>
        </button>
    </div>

    <div class="modal-body">

        <form [formGroup]="contactForm" (ngSubmit)="onSubmit()">

            <div class="row justify-content-center">

                <div class="col-md-12 col-sm-12">
                    <div class="form-group">
                        <label for="email">Email</label>
                        <input
                            type="text"
                            class="form-control"
                            id="email"
                            formControlName="email"
                        >
                    </div>
                </div>

            </div>

            <div class="row justify-content-center">

                <div class="col-md-12 col-sm-12">
                    <div class="form-group">
                        <label for="message">Message</label>
                        <textarea
                            type="text"
                            class="form-control"
                            id="message"
                            formControlName="message"
                            rows="6"
                        ></textarea>
                    </div>
                </div>

            </div>

            <div class="row justify-content-center">

                <div class="col-md-12 col-sm-12">
                    <button #submitButton
                        id="submitButton"
                        class="btn btn-success w-100"
                        [disabled]="!contactForm.valid"
                    >Send</button>
                </div>

            </div>

            <div class="row">
                <div class="col-md-12 col-sm-12">
                    <div *ngIf="sentMessage" class="alert alert-success" role="alert">
                        {{ sentMessage }}
                    </div>
                </div>
            </div>

        </form>

    </div>

</ng-template>

Any assistance on this matter would be highly appreciated.

Answer №1

I am currently working on implementing a feature where, upon clicking the 'send' button (refer to the template code below), the button will be disabled and display the text 'sending...'. Essentially, I am aiming to modify the properties of the modal component through this action.

//this.submitButton.nativeElement.style.backgroundColor = 'black';

From what I gather from your question (and looking at your code), it seems like you want to achieve three things:

  1. Disable the button after clicking send
  2. Display the text 'sending...' on the button
  3. Change the button background color to black (as per your code)

You can accomplish the first two tasks using data binding (Types of data binding). For the third task, I have found a workaround that involves utilizing the DOM instead of Angular's @ViewChild method to make it work smoothly.

  1. In your typescript file, you can use a boolean variable (e.g., ''isDataSent: boolean'') which you can OR with the ''!isValid'' in the HTML
  2. You can define the displayed text in the .ts file using a variable and utilize Data Binding to display it
  3. You can access the DOM element by id, set the color, and remove the ''@ViewChild('submitButton') submitButton: ElementRef;'' line. If you prefer handling everything via DOM manipulation, this approach should work for you :-)

Below is a snippet of sample code with certain lines omitted for clarity:

Replace the HTML-Button with:

          <button
              id="submitButton"
              class="btn btn-success w-100"
              [disabled]="!contactForm.valid || isDataSent"
          >{{submitButtonText}}</button>

In the typescript file:

export class ContactModalComponent implements OnInit, AfterViewInit {
  // ... code ...
  isDataSent: boolean;
  submitButtonText: string;
  // ... code ...
  ngOnInit() {
    //... code ...
    this.isDataSent = false;
    this.submitButtonText = 'Send';
    //... code ...
  }
  // ... code ...
  onSubmit() {
    const email = this.contactForm.value.email;
    const message = this.contactForm.value.message;

    const submitButton = document.getElementById('submitButton');
    if (submitButton) {
      submitButton.style.backgroundColor = 'black';
    } 
    this.submitButtonText = 'sending...';
    this.isDataSent = true;
    
    this.contactService.sendContactRequest(email, message, (submitSuccess: boolean) => {
        if (submitSuccess) {

            console.log('SUCCESS UPDATE UI');
            this.contactForm.value.email = '';
            this.contactForm.value.message = '';

        } else {
            console.log('ERROR update UI');
        }
    });
  }
  //... code ...
}

I am working with Angular CLI version 11.2.8 and Node version 14.16.0.
I hope this explanation helps. If you have any suggestions for improving my response, feel free to share. -Alex

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

ESLint is unable to locate the OnInit function within the "@angular/core" module

There seems to be an error: import { Component, OnInit, Input, ViewChild, ElementRef } from "@angular/core"; OnInit not found in '@angular/core'eslintimport/named The code appears to be functioning correctly, so perhaps there is a m ...

Conquering Challenges Across Multiple Disciplines

Is there a solution to solving the cross domain issues that arise when trying to fetch data from a different web server on the client-side, in violation of the Same Origin policy? ...

Discover various ways to encapsulate Vue.js components

I am currently in the process of developing a website that utilizes classic multiple pages powered by blade templates. However, I am looking to incorporate vuejs2 components for more dynamic functionalities. Although things are running smoothly, I am faci ...

The status of the S3 file upload using a presigned URL is showing as (cancelled)

I'm currently facing an issue with uploading a file to Amazon S3 using the Angular client. I have successfully generated a Presigned URL using a NodeJs application server. However, when attempting to upload the file to the presigned URL, the upload fa ...

Is it achievable to selectively target a particular class without resorting to utilizing an ID within DOM manipulation?

Is there a way to apply a function to a specific class without requiring an id? For example, in CSS hover effects, where multiple elements share the same class but only the one being hovered over is affected. function toggleHeight(elementId, arrowId) { ...

Would it be expected for these two JQuery functions to exhibit identical behaviors?

If I have the following two JQuery functions - The first one is functional: $("#myLink_931").click(function () { $(".931").toggle(); }); The second one, however, does not work as expected: $("#myLink_931").click(function () { var class_name = $(thi ...

"Utilizing a function from an external file within the Main App function - a step-by-step guide

I am currently learning React.js and I have come across a challenge that I'm unable to solve easily. I am trying to use functions instead of classes in my code, but I am struggling with using these functions as components within my Main function. Here ...

What is the best way to utilize two values in Vue.js 2?

When I attempt to structure my component in the following way: <script> export default { template: '\ <select class="form-control" v-on:change="search">\ <option v-for="option in option ...

Adding JSON data to a MySQL column in JSON format with the help of NodeJS

I currently have a MySQL table set up with the following specifications: CREATE TABLE `WorkOrders` ( `workorder_id` int(11) NOT NULL AUTO_INCREMENT, `intertype_id` int(11) NOT NULL, `equipment_id` int(11) NOT NULL, `reason_id` int(11) NOT NULL ...

When integrating react-router 5 and redux 7, there is an issue where the state is not being reset when navigating to a new route using react-router's <Link

My current setup includes the following versions: `"react-router": "^5.2.0",` `"react-router-domreact-router": "^5.2.0",` I'm unsure if my setup is compatible with React-router 5 as I was using a version prior ...

Monitor for the specific parameter in the incoming GET request

My application is using the POST method to submit jobs remotely. After submitting a job, I receive a unique job ID from the POST request that allows me to check the status of the job using a GET request. $http.get('http://localhost:8090/jobs/'+i ...

Ways to showcase information retrieved from an API onto an Angular Material card

The process involves calling two different APIs. The first API gathers user information such as name, ID, and the IDs of applications where the user is registered. The second API retrieves details from each application based on its ID. Despite successfully ...

The HTML must be loaded prior to the execution of my JavaScript function

In the controller, there is a function that sets the true value to a variable. function setShow() { return this.isShow === 'Foo'; } The value of this.isShow is set to 'Foo' Within the template, there is <div ng-if = "vm.setShow( ...

What sets apart computed, state, and action in MobX?

Currently, I am delving into MobX and grappling with the concepts of computed, state, and action individually. Do they serve similar purposes? One aspect of MobX that intrigues me is deciphering the disparity between computed properties and state. My unde ...

Verifying callback type in Typescript based on another argument's validity

There is a JavaScript function that I am working with: const fn = (cb, param) => { cb(param); }; This function is meant to be called in two ways within TypeScript: const cb0 = () => {}; fn(cb0); const cb1 = (param: string) => { }; fn(cb1, &a ...

"Is it possible to access variables declared in the main app.js file from separate route files in Node.js Express 2.5.5 and if so

Recently, I've started using the latest version of Express (2.5.5) which now automatically creates a ./routes directory in addition to ./views and ./public In the routes directory, there is an index.js file that includes: /* * GET home page. */ e ...

Can the router accommodate multiple loadChildrens at once?

I recently upgraded to the latest version of angular 2 and discovered an interesting lazy load feature utilizing loadChildren. Let me illustrate with a simple example export const routes: Routes = [ { path: 'crisis', loadChildren: 'app/c ...

In the process of developing a custom Vue component library with the help of Rollup and VueJS 3

My goal is to develop a custom Vue component library using rollup and Vue.js. The process went smoothly with Vue2, but I encountered issues parsing CSS files with Vue3. To address this, I updated the dependencies in the package.json file. package.json { ...

Once the image has been observed, what steps can be taken to close it?

I wish to implement a functionality where clicking on the view button will enlarge the image, and another click on the page will return it to its original size. import * as React from 'react'; import Box from '@mui/material/Box'; imp ...

Execute a function when an RXJS Observable has completed its operation

Using the RXJS Observable has been smooth sailing so far, but I now find myself needing to not only react to observer.next() but also when observer.complete() is called. How can I capture the OnComplete event of an RXJS Observable? The documentation for ...