Cleanly perform null checks in Angular HTML Template - html

I'm wondering if there's a way to shorten the code here:
<td class="value">
{{nonprofitRating?.financialRating?.performanceMetrics?.fundraisingEfficiency == null || ... == undefined ? ... : 'N/A' }}
</td>
to something more clean without adding getters to my .component.ts file. Is there a way to shorten the variable in Angular to something like in the below code block? This is information loaded into an NonprofitRating object from an API fetch call that may or may not be null or undefined, so I would prefer not to have 20 getters or 20 different variables in my .component.ts file for cleanliness purposes.
<td class="value" let fundraisingEfficiency = ...chained undefined check statement...>
{{fundraisingEfficiency == null || ... == undefined ? fundraisingEfficiency.toFixed(2) : 'N/A' }}
</td>

I ended up using ngSwitch to cleanly handle situations where the variable chaining would be too long, and avoid repetition. It also makes it very clean for handling if / else / then statements without mucking up the component.ts file, in my opinion.
<td class="value" [ngSwitch]="nonprofitRating?.accountabilityRating?.accountabilityTests?.materialDiversionOfAssets?.toLowerCase()">
<mat-icon *ngSwitchCase="'pass'" style="color: green" style="color: green">check_circle_outline</mat-icon>
<mat-icon *ngSwitchCase="'remediated'">remove_circle_outline</mat-icon>
<mat-icon *ngSwitchCase="'fail'" style="color: red">cancel</mat-icon>
<p *ngSwitchDefault>N/A</p>
</td>

Related

Pass TemplateRef with data to 3rd party library

I'm using ng-zorro-antd library and I'm trying to pass a ng-template without duplicating HTML.
The component (in the url) expects TemplateRef as an Input property (nzDot).
This is my ng-template:
<ng-template #dotTemplate let-step>
<div [ngSwitch]="step.status">
<span *ngSwitchCase="'active'" nz-icon nzType="info-circle" nzTheme="outline"></span>
<span
*ngSwitchCase="'error'"
nz-icon
nzType="info-circle"
nzTheme="fill"
class="text-volcano-6"
></span>
<span
*ngSwitchCase="'onboarding'"
nz-icon
nzType="info-circle"
nzTheme="outline"
class="text-yellow-6"
></span>
<span
*ngSwitchCase="'syncing'"
nz-icon
nzType="sync"
nzTheme="outline"
class="text-geekblue-6"
></span>
</div>
</ng-template>
What I'm trying to do is basically send this step object (let-step) as variable to the outlet, but I don't see a way to do this, as this is controlled by the ngTemplateOutlet inside of the 3rd party component.
Is there any smart way to do this?
I see that I can make 4 templates and then bind to the property like this:
step.status === 'Active'
? dotActiveTemplate
: step.status === 'syncing'
? dotSyncingTemplate
: step.status === 'onboarding'
? dotOnboardingTemplate
: dotErrorTemplate
But this solution seems bad and I would appreciate help.
Thanks!

TypeError: Cannot read property 'split' of undefined in reactJS

I want to cut a string (it's in an object) in 3 words (after "-"), my application is developed with reactJS, how to fixed this problem ?.
I want to return the line after "-".
NOTE: data is defined and displayed, but i want cut the String (name) after "-"
I want to display the channel like this :
tst ab
deb
azerty bla
problem :
TypeError: Cannot read property 'split' of undefined
object :
email: "tst#gmail.com"
idclient: 74
name: "tst ab - deb - azerty bla"
poste: "PDG"
soct: "entrp"
phone: "1111111"
mypage front :
....
<tr>
<td><i class="fas fa-user"></i> Name</td>
<td> {this.state.clientById.name.split(' - ')} </td>
</tr>
The error might be during the initial render where this.state.clientById might not have name property. If the optional chaining is allowed in your application, you can use the following snippet:
<td> {this.state?.clientById?.name?.split(' - ').join('\n')} </td>
Solution without optional chaining, just in case:
<td> { this.state.clientById
&& this.state.clientById.name
&& this.state.clientById.name.split(' - ').join('\n')
}
</td>
name.split('-') will return an array so you need to store in variable and iteration it
Your component may be rendering before the state is initialized and at that time this.state.clientById.name may still be undefined, try adding a check bofore executing the split like this.
<td> {
this.state.clientById &&
this.state.clientById.name &&
this.state.clientById.name.split(' - ')
}
</td>
This will make sure that this.state.clientById.name is defined before trying to split it.
This issue is occurring due to empty or null property "name" in "clientById". If we try to call split on undefined it will give error.
Try this one to get desired output. It will work in all browser.
<td> {this.state.clientById && this.state.clientById.name && this.state.clientById.name.split(' - ').map(item => {
return (<div> {item} </div>)
} </td>

How to use "v-if" in Vue.js to render a <span> only if parent div has a specific property?

What I'm trying to do is make it so that <span v-if="spinnerToggle && command-id=='updateAllExternalReferences'">Spin</span> only renders when spinnerToggle has a value of true and the parent div has a command-id property of updateAllExternalReferences.
I've been able to get this to work using only the spinnerToggle conditional, but adding the command-id one causes it to give me the following error:
[Vue warn]: Property or method "id" is not defined on the instance
but referenced during render. Make sure that this property is
reactive, either in the data option, or for class-based components, by
initializing the property.
Does v-if not support conditionals that reference properties in the parent div? If so, how should I implement this sort of functionality? Below you'll find the parent div and the respective <span>.
<div class="tool is-activatable"
v-if="config.isVisibleToUser"
#click="dispatch"
:class="[config.cssClass, {'is-active': active, 'is-highlighted': highlight, 'is-button': !context.reordering}]"
:command-id="config.command"
:context-menu-name="contextMenu.name"
:context-menu-details="contextMenu.contextMenuDetails"
:data-id="config.id"
:disabled="disabled"
:data-placement="inMenu ? 'right-top' : config.tooltipPosition || 'bottom'"
:data-original-title="(config.tooltipKey || config.tooltip || config.toolName) | i18next"
:data-expanded-content="(config.expandedTooltipKey || config.expandedTooltip) | i18next" data-html="true"
:data-expand-delay="inMenu ? 0 : config.expandDelay > -1 ? config.expandDelay : 2000"
:data-trigger="config.tooltipTrigger"
:tooltip-dynamic-snippet-id="dynamicSnippetId">
<img v-if="!hasIcon && config.img" :src="config.img" />
<ToolIcon v-if="hasIcon" :icon="config.icon" :iconUri="config.iconUri" :initials="config.iconInitials"
:awaitingData="false" :updateAvailable="config.isNewerVersionAvailable"/>
<span v-if="spinnerToggle && command-id=='updateAllExternalReferences'">Spin</span>
<span class="tool-label" :class="{'hide-in-toolbar': !shouldShowLabel}">
{{ (config.toolDisplayName || config.toolName) | i18next }}
</span>
<ShortcutKeys v-if="inMenu" :shortcut="shortcut" />
<div v-if="context.reordering" class="drag-handle"></div>
</div>
You can't directly reference the attribute from the parent <div> but you can reference the same data that was used to populate it:
<span v-if="spinnerToggle && config.command === 'updateAllExternalReferences'">Spin</span>
If the command-id expression were more complicated you could refactor to use a computed property rather than duplicating the same logic in both places.

AngularJS ng-if Ternary Conditional Attribute Use

Lets assume below sudo code, As you professionals can see I want to use Ternary kind of condition with ng-if to apply different HTML title attribute.
I tried it as below but it did not work
<td>
<span ng-if="isOk == true ? title ="Is Ok" : title ="Is Not Ok"></span>
</td>
I know I can achieve what I want applying ng-if in <td> level using below code
<td ng-if="isOk == true">
<span title ="Is Ok"></span>
</td>
<td ng-if="isOk != true">
<span title ="Is Not Ok"></span>
</td>
But I want to know weather I can use less code like Ternary check and achieve what I want with ng-if?
I thank you professionals in advace
As it was specified in this thread: if else statement in AngularJS templates, you can use ternary condition this way:
<span title="{{isOk ? 'Is Ok' : 'Is Not Ok'}}"></span>

What is the proper way to show different elements based on function return

So basically what I want is to show
'Something1' when the job is not processing
'Something2' when the job is running and status is '0'
'Something3' when the job is running but status is something else
I tried the following code snippet, but it looks like let-status in the outer template will never get assigned. Not sure whether the implementation is correct or not, could anyone give me two cents on how to make this logic work?
Thanks.
<span *ngIf="!isProcessing(); else elseBlock">
Something1
</span>
<ng-template #elseBlock let-status="queryPlaybackStatus()" *ngIf="queryStatus() === '0'; else innerElseBlock">
<span>
Something2
</span>
<ng-template #innerElseBlock>
<span>
Something3
</span>
</ng-template>
</ng-template>
I would suggest defining a string in your component, where you have much better control over your logic. In the component, set the string to the appropriate text.
Then bind to that string in the template.
I don't have all of your needed logic here, but something like this:
isImage = false;
get statusText(): string {
if (!isProcessing()) {
this.isImage = false;
return 'Something1';
} else {
this.isImage = true;
return 'path to image';
}
}
This uses a getter, which provides a way for a component property to have logic.
Then just bind to statusText in the template.
<span *ngIf='!isImage'>
{{statusText}}
</span>
<span *ngIf='isImage>
<img ...>
</span>
<span *ngIf="!isProcessing(); else elseBlock">
Something1
</span>
<ng-container #elseBlock *ngIf="queryStatus() as status">
<span *ngIf="status === '0'; else innerElseBlock">
Something2
</span>
<ng-template #innerElseBlock>
<span>
Something3_with_{{status}}%
</span>
</ng-template>
</ng-container>
So basically this need a magic combination of ng-container and ng-template.
It sounds to me like you want ngSwitch. This allows you to switch based on logic, which you should encapsulate in your component, not your template. First, let's create a property that encapsulates our logic in our component:
public get currentStep(): number {
if (!this.isProcessing) {
return 1;
} else if (this.queryStatus === 0) {
return 2;
} else {
return 3;
}
}
Next, let's bind our ngSwitch statement to this newly-created property:
<div [ngSwitch]="currentStep">
<div *ngSwitchCase="1">
<p>Something1</p>
<div>Put whatever you want in here! Images, etc.</div>
</div>
<div *ngSwitchCase="2">
<p>Something2</p>
<p>Loading....</p>
</div>
<div *ngSwitchCase="3">
<p>Something3</p>
<p>All done!</p>
</div>
</div>
That should get you where you need to go. Since this is simple, I created a stackblitz example that will demonstrate a working version of this. In the example, you can click a button and watch the app cycle through all the steps (I'm using setTimeout to simulate a long-running server query).