How to access (probably unavailable) array indexes elegantly in Angular templates? - javascript

Let's say I have an array of objects:
public arr: {val: string}[] = [];
Now I want to access the first object, but the array can be empty also.
The compiler guesses that this is OK, but it will fail during runtime - obviously:
<app-comp [value]="arr[0].val"></app-comp>
<div>{{ arr[0}.val }}</div>
Using the ?. operator works, but it will throw a warning:
<app-comp [value]="arr[0]?.val"></app-comp>
<div>{{ arr[0}?.val }}</div>
Warning NG8107: The left side of this optional chain operation does not include 'null' or 'undefined' in its type, therefore the '?.' operator can be replaced with the '.' operator
Now the solution that works for me is this:
<app-comp [value]="arr[0] && arr[0].val"></app-comp>
<div>{{ arr[0] && arr[0].val }}</div>
But it bloats the template. Is there a more elegant solution to this problem?
Using *ngIf will only work sometimes, when <app-comp> or the <div> parent can be omitted. Which will not always be the case.

You can use ng-container tag. This tag does not add any element and any parent elements will not be removed.
<ng-container *ngIf="arr[0]; let obj">
<app-comp [value]="obj.val"></app-comp>
<div>{{ obj.val }}</div>
</ng-container>

Related

Conditional Ternary Operator for v-model

I'm trying to do the following in my Vue application:
<template v-slot:cell(selected)="{ item }" >
<b-checkbox
v-model="item.selected != undefined ? item.selected : defaultCheckbox"
></b-checkbox>
</template>
I have to make sure item.selected is not undefined or else I get an error Cannot read properties of undefined (selected). When I use the conditional ternary operator in my v-model, I get a Syntax Error: Unexpected token. Why is that? How do I fix this so that I can still use item.selected even if it's not defined?
You can use v-if/else directives to define checkboxes for both cases:
<b-checkbox v-if="item.selected"
v-model="item.selected"
></b-checkbox>
<b-checkbox v-else
v-model="defaultCheckbox"
></b-checkbox>

How to trim in v-if vue js

A few days, i try to trim the response value in a vue condition.
I need this, when the value is null or empty apply the condition.
<li v-if="item[0].otrodl4.trim() == ''" class="progress-step">
But I got the error
vue.js:597 [Vue warn]: Error in render: "TypeError: Cannot read property 'trim' of null"
The problem is that some values bring only whitespace and no more, that should be considered null or empty.
E.G.
I tried it with filters but I get the same error.
Thanks for read.
Greetings.
Try this:
!item || !item[0] || !item[0].otrodl4 || item[0].otrodl4.trim() == ''
Basically, you have to check that item is defined and not null. And that the first element of that array exists AND that this element has otrodl4 property.
Try this:
item[0] && item[0].otrodl4.trim() == ''
So, the only time it checks if item[0].otrodl4.trim() == '' is if there's a value in the 0 index position of the item array.
It's known as a short-circuit conditional and you can read all about it here.
I have tried with trim() and it is working fine. please try if this can be help to you.
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.js"></script>
</head>
<body>
<div id="app">
<!--if only space in text it will display "hello space" else no dsplay. -->
<p v-if="text.trim()==''">hello whitespace {{ text | trim }}</p>
</div>
<script>
new Vue({
el: '#app',
data: function() {
return {
text: ' ' // added only a space
}
}
});
</script>
</body>
</html>
This is a TypeError where the variable you are trying to manipulate is not what it is thought to be by the developer.
Always happen to a language without proper typing.
Therefore, you can do an optional chaining and check the variable is what it is supposed to be first.
ES2020 has allowed us to use optional chaining to type check.
For example,
v-if="!!item?.[0]?.otrodl4?.trim && item?.[0]?.otrodl4?.trim() == ''"
!! is a type conversion to falsy values like null or undefined or empty string to become false.
It makes sure that otrodl4 has a property method trim before calling the method and therefore won't cause TypeError anymore.

Why is Vue.js not printing an object property even though it exists?

I have this in my template:
<div v-if="errors && errors.name" class="text-danger">{{ errors.name[0] }}</div>
And in the component, I definitely have the errors object defined, it has a name property and that property's value is an array of strings. From dev tools:
Why is the string in the first array member of errors.name not showing then? It looks like the condition errors && errors.name is never true but I can't tell why. I've tried using errors.hasOwnProperty('name') instead but to no avail.
You can't add properties to an object on the fly as Michal mentioned in the comment.
data: function () {
return {
errors: {
name: []
}
}
}
And then with arrays, you cannot assign values directly, use the push method instead and as you initialize it with the above code, you already have an array:
errors.name.push('Please enter a name.');
And lastly in the template, only check the length, as the object is now sure to be present:
<div v-if="errors.name.length > 0" class="text-danger">{{ errors.name[0] }}</div>

What is the proper way to concatenate strings with JavaScript (JSX) template literals when one is after comma

I have two strings which I want to concatenate using template literals. Second one is optional and I want to put it after comma (if exist).
I know I can do something like this (but it doesn't look proper to me):
<div>
{a}
{b ? `, ${b}` : ''}
</div>
You can incapsulate logic to filter empty and joining into the component member (or util function), i.e.:
const concatNotEmptyBy = (separator)
=> (...args)
=> args
.filter(n => n) // filter empty
.join(separator);
and next call it in your component:
<div>
{concatNotEmptyBy(', ')(a, b)}
</div>
however, if it's only 1 place and only 2 variables then your original way is totally ok.

How can I access a variable-referenced array element in Meteor spacebars?

In a spacebars template, I have a javascript array x and an index into it i, eg.
Template.test.helpers({
'foo': function() {
return {
x: ['aa','bb','cc'],
i: 1
}
}
});
I can access fixed elements of x in the template with {{ x.[1] }}:
{{template name="test"}}
{{#with foo}}
{{x.[1]}}
{{/with}}
{{/template}}
But {{ x.[i] }} doesn't work.
How can I access x[i]? Thanks!
One solution is to define a custom helper:
Template.test.helpers({
'getElement': function(a, i) {
return a[i];
}
});
And then in the template, use:
{{ getElement x i }}
With such a basic example, I think your helper solution is the most straightforward; however, the goal should always be to move the logic away from the view layer (helpers & html), which is why if spacebars seems limiting, it's just a gentle reminder to refactor.
For more complex problems, a cleaner approach might be to resolve x[i] before you need to use it in your template, or turn x into an object. For example, save x[i] to a data context, or a module object & access it directly. Referencing things by their array index makes life suck when you revisit the code in a month...

Categories