I have an angular 4.3.5 application that is getting slower after it is used for a while (~20 minutes).
My scenario is like:
- Rest API and static angular html/css/js running on RaspberryPi B 3
- ~30 RaspberryPI B 3 accessing static angular application through Chromium (versions 58 and 60)
What occurs:
- The Angular's HTTP requests got slower at time pass. Example: from ~100 ms to ~2 seconds
Additional information:
- If I press F5 on Chromium, the Angular application come back to normal
- Angular uses this template https://themeforest.net/item/primer-angular-2-material-design-admin-template/19228165
- Angular uses a Google Chrome/Chromium app, that I wrote, for communication with an Arduino through serial port (Chrome API: chrome.runtime.sendMessage, chrome.runtime.connect and chrome.serial)
- The client, RaspberryPi, has available resources (CPU and memory) when the application get slow
- Angular application stores almost nothing on the browser
The component that presents the problem is the following:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import 'rxjs/add/operator/takeUntil';
import { Subject } from 'rxjs/Subject';
import { SweetAlertService } from 'ng2-cli-sweetalert2';
import { ApiService } from '.././api.service';
import { NFCService } from '.././nfc.service';
@Component({
  selector: 'app-menu',
  templateUrl: './menu.component.html',
  styleUrls: ['./menu.component.scss']
})
export class MenuComponent implements OnInit, OnDestroy {
  private ngUnsubscribe: Subject<void> = new Subject<void>();
  cardId: string;
  userId: string;
  userName: string;
  is_secure_bar: boolean = false;
  requestInProgress = false;
  userBalance: number = 0;
  step: number = 1;
  // showCheckout: boolean = false;
  categories = new Array();
  subcategories = new Array();
  products = new Array();
  cartItems = new Array();
  countCartItems: number = 0;
  totalCartValue: number = 0;
  table_scroller;
  table_scroller_height;
  show_scroller_btns = false;
  constructor(
    public router: Router,
    public route: ActivatedRoute,
    private _nfcService: NFCService,
    private _apiService: ApiService,
    private _swal: SweetAlertService
  ) { }
  ngOnInit() {
    var account = localStorage.getItem('account');
    if (account) {
      // set variable to catch user data
      // JSON.parse(
    } else {
      this.router.navigate(['login']);
    }
    this.route.params
    .takeUntil(this.ngUnsubscribe)
    .subscribe(params => {
      this.cardId = params.id;
      this._apiService.getCardUser(params.id)
      .takeUntil(this.ngUnsubscribe)
      .subscribe(
        response => {
          // SUCCESS
          this.userId = response.data[0].uuid;
          this.userBalance = response.data[0].balance;
          this.userName = response.data[0].name;
        },
        error => {
          // ERROR
          console.log('Failed ;(', error);
        }
      );
    });
    this.getEvents()
    .takeUntil(this.ngUnsubscribe)
    .subscribe(
      response => {
        if (response.data[0].options.sales_auth_after_buy_is_required) {
          this.is_secure_bar = true;
        }
      },
      error => {
        console.log('Erro ao verificar Evento.')
      }
    );
    var categories = localStorage.getItem('cache_categories');
    if (categories) {
      this.categories = JSON.parse(categories);
    } else {
      // this.getCategories();
      this.getCategoriesP()
    }
  }
  //@felipe_todo
  getEvents()
  {
    return this._apiService.getEvents();
    //COMO FAZER LOGOUT ABAIXO
    //localStorage.clear();
  }
  getCategories() {
    this._apiService.getProductsCategories()
      .takeUntil(this.ngUnsubscribe)
      .subscribe(response => {
        // SUCCESS
        this.categories = response.data;
        localStorage.setItem('cache_categories', JSON.stringify(this.categories));
      }, error => {
        // ERROR
        console.log('Failed ;(', error);
      });
  }
  getCategoriesP() {
    let categories;
    this._apiService.getCategories()
      .then(response => categories = response)
      .then(() => {
        this.categories = categories;
        console.log(categories);
      });
  }
  categorySelected(item) {
    this.step = 2;
    var subcategories = localStorage.getItem('cache_subcategories_' + item.uuid);
    if (subcategories) {
      this.subcategories = JSON.parse(subcategories);
    } else {
      // this.getSubcategories(item.uuid);
      this.getSubcategoriesP(item.uuid);
    }
  }
  getSubcategories(uuid) {
    this._apiService.getProductsSubcategories(uuid)
      .takeUntil(this.ngUnsubscribe)
      .subscribe(response => {
        // SUCCESS
        this.subcategories = response.data;
        localStorage.setItem('cache_subcategories_' + uuid, JSON.stringify(this.subcategories));
      }, error => {
        // ERROR
        console.log('Failed ;(', error);
      });
  }
  getSubcategoriesP(uuid) {
    let subcategories;
    this._apiService.getSubcategories(uuid)
      .then(response => subcategories = response)
      .then(() => {
        this.subcategories = subcategories;
        console.log(subcategories);
      });
  }
  subCategorySelected(item) {
    this.step = 3;
    var products = localStorage.getItem('cache_products_' + item.uuid);
    if (products) {
      this.products = JSON.parse(products);
    } else {
      // this.getProducts(item.uuid);
      this.getProductsP(item.uuid);
    }
  }
  getProducts(uuid) {
    this._apiService.getProducts(uuid)
      .takeUntil(this.ngUnsubscribe)
      .subscribe(response => {
        // SUCCESS
        this.products = response.data;
        localStorage.setItem('cache_products_' + uuid, JSON.stringify(this.products));
      }, error => {
        // ERROR
        console.log('Failed ;(', error);
      });
  }
  getProductsP(uuid) {
    let products;
    this._apiService.getProductList(uuid)
      .then(response => products = response)
      .then(() => {
        this.products = products;
        console.log(products);
      });
  }
  addToCard(product) {
    var existentItems = this.cartItems.filter(function(item) {
      return item.uuid === product.uuid
    });
    if (existentItems.length) {
      existentItems[0].quantity += 1
    } else {
      product.quantity = 1;
      this.cartItems.unshift(product);
    }
    let that = this;
    this.calculateTotal();
    setTimeout(function(){
      that.setScroller();
    }, 300);
  }
  removeProduct(index) {
    let product = this.cartItems[index]
    var existentItems = this.cartItems.filter(function(item) {
      return item.uuid === product.uuid
    });
    if (existentItems.length) {
      existentItems[0].quantity -= 1
      if (existentItems[0].quantity == 0) {
        this.cartItems.splice(index, 1);
      }
    } else {
      product.quantity = 1;
      this.cartItems.splice(index, 1);
    }
    this.calculateTotal();
    let that = this;
    setTimeout(function(){
      if (that.table_scroller.offsetHeight < 270) {
        that.show_scroller_btns = false;
      }
    }, 300);
  }
  calculateTotal() {
    this.countCartItems = 0;
    this.totalCartValue = 0;
    var that = this;
    this.cartItems.forEach(function(item) {
      that.countCartItems += item.quantity;
      that.totalCartValue += item.value * item.quantity;
    });
  }
  backStep() {
    if (this.step == 2) {
      this.subcategories = new Array();
    } else if (this.step == 3) {
      this.products = new Array();
    }
    this.step--;
  }
  setScroller() {
    if (this.cartItems.length) {
      if (!this.table_scroller) {
        this.table_scroller = document.querySelector('#table-scroller');
      }else {
        console.log(this.table_scroller.offsetHeight)
        if (this.table_scroller.offsetHeight >= 270) {
          this.show_scroller_btns = true;
        } else {
          this.show_scroller_btns = false;
        }
      }
    }
  }
  scrollDown() {
    (<HTMLElement>this.table_scroller).scrollTop = (<HTMLElement>this.table_scroller).scrollTop+50;
  }
  scrollUp() {
    (<HTMLElement>this.table_scroller).scrollTop = (<HTMLElement>this.table_scroller).scrollTop-50;
  }
  confirmDebit() {
    if (this.requestInProgress) return;
    if (this.userBalance < this.totalCartValue) {
      this._swal.error({ title: 'Salto Insuficiente', text: 'Este cliente não possui saldo suficiente para essa operação.' });
      return;
    }
    this.requestInProgress = true;
    var order = {
      card_uuid: this.cardId,
      event_uuid: 'c7b5bd69-c2b5-4226-b043-ccbf91be0ba8',
      products: this.cartItems
    };
    let is_secure_bar = this.is_secure_bar;
    this._apiService.postOrder(order)
       .takeUntil(this.ngUnsubscribe)
         .subscribe(response => {
        console.log('Success');
        // this.router.navigate(['customer', this.userId]);
        let that = this;
        this._swal.success({
          title: 'Debito Efetuado',
          text: 'O débito foi efetuado com sucesso',
          showCancelButton: false,
          confirmButtonText: 'OK',
          allowOutsideClick: false,
        }).then(function(success) {
          console.log("Clicked confirm");
          if (is_secure_bar) {
            that.logout();
          } else {
            that.router.navigate(['card']);
          }
        });
        this.requestInProgress = false;
      }, error => {
        // ERROR
        console.log('Request Failed ;(', error);
        if (error.status !== 0) {
          // TODO: Should display error message if available!
          this._swal.error({ title: 'Erro', text: 'Ocorreu um erro inesperado ao conectar-se ao servidor de acesso.' });
        } else {
          this._swal.error({ title: 'Erro', text: 'Não foi possível conectar-se ao servidor de acesso. Por favor verifique sua conexão.' });
        }
        this.requestInProgress = false;
      }
      );
  }
  logout() {
    let that = this;
    localStorage.clear();
    that.router.navigate(['login']);
  }
  clearCheckout() {
    this.cartItems = new Array();
    this.calculateTotal();
    this.router.navigate(['card']);
  }
    ngOnDestroy() {
      console.log('uhul')
        this.ngUnsubscribe.next();
        this.ngUnsubscribe.complete();
    }
}
The methods that present slowness each time we access the component are:
getCategories() getSubcategories(uuid) getProducts(uuid) confirmDebit()
For testing purposes we have created a new version for each of these methods, this time working with promises:
getCategoriesP() getSubcategoriesP(uuid) getProductsP(uuid)
Regardless of the version of the called method, the same problem occurs.
 
     
     
     
    