-1

I'm attempting to use $scope.$apply(); in some function that doesn't auto update the html (Exactly this but not with timeout).

But when I use it at the end of my function I get Cannot find name '$scope', so I assumed that the $scope would be the binding of my model (Because of this answer) but when I replace $scope for my binding I get Property '$apply' does not exist on type 'string' (my variable that is binded is a string).

So what can I do to update the html (main problem) or fix the $scope.$apply() solution?

Edit: $scope and $apply() are from AngularJS, not Angular 2+. I'm getting the correct output messages in console, but the UI is not updating and I don't understand the problem.

<div class="col-sm home_title">
    <div class="home_title_box" id="home_title_box"></div>
    <h2 class="home_item">{{current_title}}</h2>
</div>
import { Component } from '@angular/core';

@Component({
  selector: 'app-index',
  templateUrl: './index.component.html',
  styleUrls: ['./index.component.scss']
})
export class IndexComponent {

  constructor() { }

  titles_array:   string[] = ["Doctor", "Medic", "Player"];
  current_title:  string = "None";
  curr_title_i:   number = 0;

  update_title(el: any){
    const resize_ob = new ResizeObserver((entries) => {
      if(entries !== null){
        if(entries[0].contentRect.width === 0){
          for(this.curr_title_i; this.curr_title_i < this.titles_array.length;){
            this.current_title = this.titles_array[this.curr_title_i];

            console.log(this.curr_title_i); // correct output
            console.log(this.current_title); // correct output

            this.curr_title_i++;
            if(this.curr_title_i >= this.titles_array.length){
              this.curr_title_i = 0;
            }
            break;
          }
        }
      }
    });

    resize_ob.observe(el);
  }

  ngOnInit(){
    const elemTitleBox = document.querySelector("#home_title_box");
    if(elemTitleBox !== null){
      this.update_title(elemTitleBox);
    }
  }
}
  • 1
    $scope and $apply was used in AngularJS. there no such thing in Angular 2+ which you are using. Please update the question and provide the whole code of your component, or maybe better - make a stackblitz repro. In code you provided there is no calls of myFunction method so the reason why your template is not updating is not clear – mihan oktavian Mar 15 '23 at 19:22
  • The function is called in ngOnInit() but I just forgot to add it. That is all the relevant component. The post has been edited. The full project is in [github](https://github.com/LautaroColella/YP_PortfolioFrontend/tree/master/src/app/index) @mihanoktavian – LautaroColella Mar 15 '23 at 19:30

3 Answers3

1

Have you tried manually detecting changes?

Add the following to your constructor:

constructor(private cdRef: ChangeDetectorRef) { }

And fire detect changes after your update_title method.

if (elemTitleBox !== null) {
  this.update_title(elemTitleBox);
}

this.cdRef.detectChanges();
Derrick Rose
  • 664
  • 2
  • 9
  • 21
  • Interesting concept that I'm now aware. I made what you say but the problem persist. I have read [this](https://stackoverflow.com/a/66821090/18895342) and I believe that the problem is related to the fast timing of the function since it's changing the view based on the width of an element (that is always monitoring), and the element width is changing every second by a css transition that resizes the object. In the answer I linked it says `if the data changes every second, your application will become slower` maybe thats why angular "turn off" updates to the view. – LautaroColella Mar 17 '23 at 08:36
  • Manually detecting the changes was the correct thinking, but the correct answer wasn't what you provided. Thank you – LautaroColella Mar 17 '23 at 10:43
1

A bit late for a train, but still. using ngZone is a bit advanced thing, and there is no necessity to use it here.

First of all: do not use querySelector. well.. you actually can, but thats not an "angulary" way. use ViewChild.

Second: ngOnInit is called before component dependent DOM is rendered. so youe example was not working because when you called updateTitle method in ngOnInit was undefined. So, I advise you to make a habit to use all the DOM or DOM-dependent manipulations only after ngAfterViewInit hook was fired.

mihan oktavian
  • 570
  • 2
  • 8
0

https://dev.to/christiankohler/how-to-use-resizeobserver-with-angular-9l5

If you are following this example you may have tried to bind the width directly to the class property. Unfortunately the template is not rerendered and keeps the initial value.

The reason is that Angular has monkey-patched most of the events but not (yet) ResizeObserver. This means that this callback runs outside of the zone.

We can easily fix that by manually running it in the zone.

import { Component, NgZone } from '@angular/core';

@Component({
  selector: 'app-index',
  templateUrl: './index.component.html',
  styleUrls: ['./index.component.scss']
})
export class IndexComponent {

  constructor(private zone: NgZone) { }

  titles_array:  string[] = ["Doctor", "Medic", "Player"];
  current_title: string = "Nothing";
  curr_title_i:  number = 0;
  second_after:  boolean = true;
  el:            any = undefined;
  resize_ob:     any = undefined;

  update_title(): void{
    if(this.el !== null){
      this.resize_ob = new ResizeObserver((entries) => {
        this.zone.run(() => {
          if(entries !== null){
            if(this.el.parentElement !== null){
              const parentElemWidth = +getComputedStyle(this.el.parentElement).width.slice(0, -2);
              const elemWidth = +getComputedStyle(this.el).width.slice(0, -2);

              if(Math.round((elemWidth / parentElemWidth) * 100) >= 93){
                if(this.second_after){
                  this.current_title = this.titles_array[this.curr_title_i];
                  this.curr_title_i++;
                  if(this.curr_title_i >= this.titles_array.length){
                    this.curr_title_i = 0;
                  }
                  this.second_after = false;
                  setTimeout(() => this.second_after = true, 1000);
                }
              }
            }
          }
        })
      });

      this.resize_ob.observe(this.el);
    }
  }


  ngOnInit(){
    this.el = document.querySelector("#home_title_box");
    this.update_title();
  }

  ngOnDestroy() {
    this.resize_ob.unobserve(this.el);
  }
}