Angular calling async pipe makes http call that many time - rxjs

I am using async pipe in the template. If you see my below code i am using async pipe in two different places. Because it is used in two places, it gets subscribed two time and there are two http calls made. How to avoid this one single call with async pipe? I was able to achieve without async by subscribe in ngOnInit() but would like to know how to achieve with single async pipe in my case?
<div class="flex-group space-between">
<h1>Heading</h1>
<div *ngIf="historyYears$ | async as historyYears; else loadingComponent">
<aa-alert alertClass="info" alertText="You have no training history" *ngIf="historyYears.length === 0"></aa-alert>
</div>
</div>
<div *ngIf="historyYears$ | async as historyYears; else loadingComponent">
<!--rest of code goes here -->
</div>
<ng-template #loadingComponent>
<app-loading></app-loading>
</ng-template>

Here are a couple ways to prevent your http call from firing twice:
1. Use the shareReplay operator
Component:
public historyYears$ = this.someService.historyYears$.pipe(shareReplay());
This will cause multiple subscriptions to the same observable to share a single subscription.
2. Use a single async pipe
<ng-container *ngIf="historyYears$ | async as historyYears">
<div class="flex-group space-between">
<h1>Heading</h1>
<div *ngIf="historyYears; else loadingComponent">
<aa-alert alertClass="info" alertText="You have no training history" *ngIf="historyYears.length === 0"></aa-alert>
</div>
</div>
<div *ngIf="historyYears; else loadingComponent">
<!--rest of code goes here -->
</div>
<ng-template #loadingComponent>
<app-loading></app-loading>
</ng-template>
</ng-container>
Here we move the *ngIf that uses the async pipe up to a common parent of all elements that need access to historyYears.

Related

How does alpineJS re-reder a template on data update?

How does AlpineJS re-render a template ?
<script>let contacts = [];</script>
<div x-data="{ persons: contacts }">
<template x-for=" person in persons ">
<div x-text=" person "></div>
</template>
<button x-on:click="contacts.push('Jack'); persons = contacts; console.log(persons.length);">Click</button>
</div>
I was expecting the div to have multiple text of Jack on click.
In your case/example contacts are out of Alpine's scope, once the component is initiated it's in it's own bubble, anything outside out the component scope x-data won't trigger a re-render.
If you change contacts.push('Jack'); to persons.push('Jack');, that will trigger a re-render.
Maybe it's not the most efficient way but I've had a similar problem, to resolve it I used the get function inspired from this.
My use case is the following:
JS:
get updatedState() {
var config_state = [];
#update your data here
return config_state;
}
HTML:
<template x-for="config in updatedState">
#Do what you want here
</template>
Each time your data changes, your loop will update the data

How to initiate an action to `component` from `Router`

In my page, the router taking care of page transition. but before i go for next page, I would like to validate the form fields. for that I use the cp-validation addon.
But when the next button clicked, I would like to ask my child component to do the validation and send back the result to router. how to do this?
I tried to validate the model within router but not works. if my try is not correct way please let me know the correct approach to handle this scenario.
my router bhs:
<div class="balanceEmi rdc-scroll-content has-subheader rdc-view">
{{cs2i-select-tenure selectedCreditCard=model}}//my component
//which requie to know the form validation
</div>
<div class="rdc-view___footer rdc-view___footer---stickey">
{{rdc-button default="CANCEL" type="secondary" action="redirect"}}
{{#if enableNext}}
{{rdc-button default="NEXT" type="primary" action="goToNext"}}
{{else}}
{{rdc-button default="NEXT" type="primary" disabled="true"}}
{{/if}}
<div class="formValidateBeforeNextBtn" {{action 'formValidateBeforeNext'}}>//let this actin call my component to do the validaiton else le tme know the correct way
<!-- empty for validation button-->
</div>
</div>
I recommend registering the validate component to a factory, then you can call this from wherever you are in your application like a router.
Read the Ember.js docs here

Dynamics charts using chartist and angular with ng-repeate

I now and I can show a chart with dynamic data but a fixed number of chart. When I want to show a dynamic number of charts, something happens with ng-repeate. I said something happens, because if in mycharts[0].container.outerHTML I had the html that I need to show the graph (generated by the library), and if I copy and paste in a fixed place in my html, it will show the graph. My ng-repeate code looks as follow:
<div class="row" ng-controller="nodesDataTablesCtrl as nodeCtrl" >
<div ng-repeat="(index, node) in nodeCtrl.nodes" on-finish-render>
<div class="row">
<div class="col-lg-12">
<div id="{{ node.name }}_MEMORY" class="ct-chart snp_dynamic_chart"></div>
</div>
</div>
</div>
</div>
I found a solution, I'm not sure if is a bug of ng-repeate or Chartist.
In my ng-repeate I use nodeCtrl.nodes, which is an array that I receive from an http.get, my solution was create an range function, wich is, a function that receive a Number and return a list from 0 to n-1. Instead of passing the nodeCtrl.nodes which is updated everytime I make a request, I update a variable numberOfNodes with the range function, I mean, my new ng-repeat will be as follow:
<div ng-repeat="(index, node) in nodeCtrl.numberOfNodes" on-finish-render>
and in the success function of my request I do:
numberOfNodes = range(nodeCtrl.nodes.length)
which from my point of view make that ng-repeate don't update in some way internally.
Is important to se that programatically it shouldn't be differen but ...

Scripts not working on partial view after Ajax call

I have called scripts on _Layout.cshtml page and my Index.cshtml page has partial view into it. So on page load, SignalR scripts working perfect on partial view, on page end I make another ajax request and load the partial view with another data filled in that and embed under already displayed data, and then the SignalR does not work on the newly embedded record.
This is my index page code:
<div class="col-md-6">
<div class="profile-body">
<div class="row infinite-scroll">
#Html.Partial("_AlbumRow", Model)
</div>
</div>
</div>
This is my partial View Code:
#model IEnumerable<SmartKids.Lib.Core.ViewModels.FileMediaAlbumsVM>
#foreach (var item in Model)
{
<div class="widget">
<div class="block rounded">
<img src="#Url.Content(item.ImageUrl)" alt="#item.Title">
<input type="button" data-image-id="#item.imageId" class="btn btn-sm btn-default">Like</input>
</div>
</div>
}
Kindly help me how to resolve this issue that after making an ajax request I am not able to get those SignalR working. Here is more to say when I put the SignalR scripts on PartialView that works but it also sucks that on each ajax request there is again SignalR loaded on the page and when I click on LIke button it makes many calls to the function behind it.
Kindly help me to resolve this issue, I am stuck at this point since 1 week.
Here is signalR Code:
$(".btn.btn-sm.btn-default").on("click", function () {
var imageId = $(this).attr("data-image-id");
albumClient.server.like(imageId);
});
Problem: You are binding event to elements directly, So when you remove this element and replace it with a different one the events are also removed along with that element, This is something like strongly coupled.
Solution: Use Jquery event delegation. This will make sure the events will be triggered on the current elements and also all the elements that can come in future.
syntax is as below.
$(document).on("click", ".btn.btn-sm.btn-default",function () {
var imageId = $(this).attr("data-image-id");
albumClient.server.like(iamgeId);
});
NOTE: This was never a singlaR issue, it was Jquery issue.
Efficient Way: The problem in using $(document).on("click"... is that when ever there is a click happening on the entire page the Jquery framework will bubble the events from the clicked element upwards(its parent, and its parent and so on..) unless the element specified in the selector arrives, So its kind of performance hit as we don't want this check's to run if we are clicking outside the required area ( button .btn.btn-sm.btn-default in this example).
So best practice is to bind this event delegation to the closest parent possible which will not be removed, <div class="row infinite-scroll"> in this question. So that only when the click happens within this element the event bubbling will happen and also will be stopped once it reaches the parent element,it acts kind of a boundary for event bubbling.
$('.row.infinite-scroll').on("click", ".btn.btn-sm.btn-default",function () {
var imageId = $(this).attr("data-image-id");
albumClient.server.like(iamgeId);
});

Angular 2 - Using pipe in (click) event

My question is probably simple but just can't find the way to use pipe within an event like (click) for example. Something like this:
<button (click)="quizAnswers(answer?.texte | translate | async)"></button>
I always get an error. I tried to wrap it with () or {} or []...
There are some workaround like putting the content in an attribute and then get it on the event with this.attribute but I'm sure there is a proper way !
Thanks in advance for your help
A workaround would be to call your pipes in the click handler function instead:
function quizAnswers(answer)
{
let translatePipe= new TranslatePipe();
...
return translatePipe.transform(answer?.texte);
}
I just got through this same issue. Action expressions can't contain the async pipe. However, you can use a hidden <input> element to hold the latest value of the promise/observable stream, and then access that value anywhere.
<input #dummy style="{display: none}" type="text" value="{{ myAsyncSource | async }}">
<a (click)="myFunction(dummy.value)">{{ dummy.value }}</a>
For your case of <button> there's actually a one-line solution that eliminates the need for the dummy <input>, posted in this solution:
<button type="button" #item value="{{i$ | async}}" (click)="buttonClicked(item.value)">BUTTON</button>

Resources