The interconnectivity between ngAfterViewInit in Angular's LifeCycle and observables

enable.service.ts

@Injectable({
  providedIn: 'root'
})
export class EnableService {
  isEnabled$ = from(this.client.init()).pipe(
    switchMap(() => this.client.getEnabled()),
    map(([enabled, isAdmin]) => ({enabled: true, isAdmin: false})),
    share()
  ); // The observable returns enable and isAdmin values

  constructor(
    // Some constructor
  ) {}
}

main.component.ts

export class MainComponent implements OnInit, AfterViewInit, OnDestroy {
  @ViewChild('mymap') containerRef: ElementRef<HTMLDivElement>;

  isAdmin: boolean = false;
  isEnable: boolean = false;

  constructor(
    private enableService: EnableService,
  ) {
    this.enableService.isEnabled$.subscribe(res => {this.enable = res.enabled; this.isAdmin = res.isAdmin});
  }

  async ngAfterViewInit(): Promise<void> {
    // A hack to wait for the enableService subscription to get the actual value, otherwise containerRef #mymap may not exist
    await new Promise(resolve => setTimeout(resolve, 1000));

    // Execute third party sdk initialization only if containerRef exists
    if (this.containerRef) {
      // Third party SDK initialization code here
    }
  }

main.component.html

<div #mymap *ngIf="!isEnable || isAdmin"></div>

This code currently works with a 1-second delay in ngAfterViewInit. However, what if the enableService takes longer to retrieve the value? It could potentially break the functionality.

Is there a way to ensure that ngAfterViewInit is executed after the enableService retrieves the value?

Or perhaps there is a better approach that avoids using the 1-second delay. Setting the initial value of isEnable to false and then changing it to true could cause issues, similarly setting it to true when it's already true might cause errors. Moving the observable subscription from the constructor to ngOnInit did not solve the issue.

Answer №1

Is this effective?

Add a nested component within the NgIf directive to ensure that the ngOnInit lifecycle hook is triggered after the *ngIf condition evaluates to true!

typescript

get isEnabled$ {
    return this.enableService.isEnabled$;
}

html

<div #mymap *ngIf="!(isEnabled$ | async).isEnable || (isEnabled$ | async).isAdmin">
    <child-component></child-component>
</div>

Answer №2

To enhance your containerRef, consider implementing a setter method:

private _containerRef: ElementRef<HTMLDivElement>; 

@ViewChild('mymap') set content(content: ElementRef) {
  this._containerRef = content;
  if (content) {            
    // Perform any necessary operations with the reference
   }
}

The setter function automatically captures all changes when an element appears or disappears, eliminating the need for manual synchronization through a service call.

Answer №3

When using EnableService, the client service calls for methods like init and getEnable may sometimes return expected responses with a slight delay. This delay is usually less than the 1-second timeout set in ngAfviewInit.

If the service takes longer than 1 second to respond, the HTML content might not receive the necessary data on time, resulting in the issue you are currently facing.

While utilizing async pipes for data binding in your HTML is a recommended approach, there is another method that requires minimal code modifications:

In the main.component.ts file:

export class MainComponent implements OnInit, AfterViewInit, OnDestroy {
  @ViewChild('mymap') containerRef: ElementRef<HTMLDivElement>;

  isAdmin: boolean = false;
  isEnable: boolean = false;
  enableServiceCalled: boolean = false;

  constructor(
   private enableService: EnableService,
  ) {
   this.enableService.isEnabled$.subscribe(res => { 
   this.isEnable = res.enabled; 
   this.isAdmin = res.isAdmin;
   this.enableServiceCalled = true;
  });
  }

  async ngAfterViewInit(): Promise<void> {
    if (this.containerRef) {
     // Code execution based on the existence of containerRef
     }
    }
   }

In the main.component.html file:

<div #mymap *ngIf="enableServiceCalled && (!isEnable || isAdmin)"></div>

By using the enableServiceCalled boolean, you ensure that the required data is already available for use in your HTML elements. This solution aims to address your current challenge effectively.

Hopefully, this explanation proves helpful to you.

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

Enhance your websites' search functionality with jQuery autocomplete using AJAX

I am attempting to implement dynamic autocomplete values for a text box. Here is my solution: echo json_encode($res) 0: {type_name: "name1"} 1: {type_name: "name2"} 2: {type_name: "name3"} 3: {type_name: "name4"} 4: {type_name: "name5"} Below is the co ...

Tips for adjusting the time interval on an automatic slideshow when manual controls are in play

I'm having trouble with my slideshow. I want it to run automatically and also have manual controls, but there are some issues. When I try to manually control the slides, it takes me to the wrong slide and messes up the timing for the next few slides. ...

The completion event for Ajax and Json does not activate Google Maps

I'm currently facing an issue with my Google Maps functionality. Despite trying various solutions that involve hidden tabs, the problem persists. The trouble lies in the fact that my ajax function successfully posts and retrieves data, but fails to tr ...

Turn off the scrollbar without losing the ability to scroll

Struggling with disabling the HTML scrollbar while keeping the scrolling ability and preserving the scrollbar of a text area. Check out my code here I attempted to use this CSS: html {overflow:hidden;} Although it partially worked, I'm not complete ...

Updating serialized $_POST array with new key/value pair using jQuery AJAX

Is there a way to insert additional values into a serialized $_POST array before sending an AJAX request using jQuery? Here's the situation: $('#ajax-preview').on('click', function(e) { e.preventDefault(); var formData = ...

Testing HTTP requests on a form click in Vue.js 2 - Let's see how

Within my component, I have the following method: methods:{ ContactUs(){ this.$http.post("/api/contact-us").then((res)=>{ ///do new stuff },(err)=>{ //do new stuff }) ...

Utilizing the fs module in Node.js

Hello friends! Currently I am faced with an issue while trying to import the fs module in nodejs. Initially, I utilized require to import it like so: const fs = require('fs'); Everything was functioning smoothly until recently when it suddenly ...

Establish a connection between the MySql database in WHM (phpmyadmin) and a node.js application

Our team has been working on setting up the database connection with Mysql using node.js in Cpanel. Although I didn't create the database myself, I have all the necessary information such as the host, user, password, name of the database, and port nu ...

Is there a way to include all images from a local/server directory into an array and then utilize that array variable in a different file?

I'm currently using Netbeans version 8.0.1 with PHP version 5.3 Here is a PHP file example: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/199 ...

Send a line break character as a parameter to the function

I'm currently working on a function that creates a string using a specified separator passed in as an argument. When utilizing the \n character as a separator, the output doesn't match my expectation. let concatenate = function(first, secon ...

Tips for effectively sharing custom validators across different modules

After creating a password validator based on a tutorial, I attempted to use it on multiple forms within different parts of my application. However, I encountered an error stating: Type PasswordValidator is part of the declarations of 2 modules: SignupMod ...

Can Material UI be defined as a peerDependency while maintaining its types as a DevDependency?

Is there a way to set Material UI as a peerDependency while keeping its types as DevDependency? I'm currently working on a component library using React + Typescript, with components based on Material UI and Rollup as the module bundler. For example ...

Can someone explain the method for displaying or concealing a menu based on scrolling direction?

https://i.stack.imgur.com/tpDx0.jpg I want to hide this menu when scrolling down and show it when scrolling up. The code for my menu bot is: <script> var previousScroll = 0; $(window).scroll(function(event){ v ...

how to execute Vue-Js method just one time

Is there a way to add a random number (ranging from 0 to the price of an item) to one of the data properties in Vue.js, but only once when the page is initially loaded? I am unable to use mounted and computed because I need to pass a parameter to the funct ...

Navigating to specific rows in a primeng virtualscroll table using the scrollToIndex

Utilizing Primeng virtual scroll table in Angular to manage large datasets in an array. I am interested in using the scrollToIndex. Is there an equivalent of cdk-virtual-scroll-viewport in Primeng table? I need the functionality where, upon a user clicking ...

What is the best way to display just one record that has the lowest score out of all the records?

I need help with displaying only 1 record from the DL list that has the lowest score, instead of showing all records. In the example on stackblitz, you can see that for the first record, the DL scores are: 54, 20, and updated. Instead of displaying all 3 ...

Issue with FullPage.js scrollOverflow feature not properly accommodating loaded content

I am currently working on a full-page website and have opted to utilize the Fullpage.js plugin. However, I seem to be facing some challenges when it comes to loading content through an AJAX request. The pages are being populated with JSON content, but for ...

Error message occurs during compilation of basic Vue file in Webpack

When I execute webpack watch in the VS2017 task runner, it displays the following error: ERROR in ./wwwroot/js/src/App.vue Module build failed: SyntaxError: Unexpected token { at exports.runInThisContext (vm.js:53:16) at Module._compile (module.js:373:25) ...

Points in an array being interpolated

I am currently working with data points that define the boundaries of a constellation. let boundaries = [ { ra: 344.46530375, dec: 35.1682358 }, { ra: 344.34285125, dec: 53.1680298 }, { ra: 351.45289375, ...

What is the best way to include message body in CDATA using strophe?

I have a task to create messages in a specific format by using the following code: $msg({to: 'user', from: 'me', type: 'chat'}).c("body").t('some data'); This code generates the message structure as follows: <m ...