How to display placeholders in AngularJS for undefined expression values? - javascript

If I have an expression {{ x }} and x is undefined or null, then how can I display a placeholder for it?
I provided one solution in my answer, but I would like to know what other ways there are.
Maybe, also for placeholder for promises.

{{ counter || '?'}}.
Just pure javascript. || can be used as default value. Since it would be different empty messages in each, a generalized directive would not be suitable for many cases.
If you want to apply a different class to empty ones, that's also built-in:
<div ng-class="{empty: !counter}" ng-bind="counter || ?"></div>

I would do it like this, but maybe there is a better way:
angular.module('app').filter('placeholdEmpty', function(){
return function(input){
if(!(input == undefined || input == null)){
return input;
} else {
return "placeholder";
}
}
});
and then use {{ x | placeholdEmpty}}

I do it with ng-show, like this:
<strong>{{username}}</strong>
<span class="empty" ng-show="!username">N/A</span>
Sure, it adds a lot more elements to my view that I might be able to handle differently. I like though how easy it is to clearly see where my placeholder/empty values are, and I can style them differently as well.

Implement default filter:
app.filter('default', function(){
return function(value, def) {
return (value === undefined || value === null? def : value);
};
});
And use it as:
{{ x | default: '?' }}
The advantage of the filter solution over {{ x || '?'}} is that you can distinguish between undefined, null or 0.

Implementing default-ish filters works, but if you're using only numbers you can use angular's own number filter
If the input is null or undefined, it will just be returned. If the
input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or
'-∞' is returned, respectively. If the input is not a number an empty
string is returned.
{{ (val | number ) || "Number not provided"}}

Related

Is there a better solution than adding || [ ] to each field for a null check in ES6

I am working with a child component where an array item is passed as prop. However, that item also contains another array where there should be null and undefined check before rendering. Adding || [] to each filed didn't seem the best way. Is there a better way that I am missing?
const ChildComponent= ({ newsItem, textColor, newsItemHeadingColor }) => {
const imageFormats = newsItem?.itemImage?.formats;
const imageSrc = useProgressiveImage({ formats: imageFormats });
const history = useHistory();
const { state } = useContextState();
const { schemeMode, colorSchemes } = state;
const { itemTagColor, itemTagTextColor } = getThemeColors({
schemeMode,
colorSchemes,
colors: {
itemTagColor: newsItem?.itemTagColor?.name,
itemTagTextColor: newsItem?.itemTagTextColor?.name,
},
});
return (
<NewsItem
onClick={() => {
const url =
newsItem?.itemTag === 'Lab'
? getPageLink({
page: newsItem?.itemLink || [], <<<<<< this check
})
: newsItem?.itemLink || []; <<<<<< this check
url && history.push(url);
}}
>
<ImageContainer>
<Image src={imageSrc} alt={`${newsItem?.itemImage || []}`} /> <<<<<< this check
</ImageContainer>
<NewsItemTag color={itemTagColor} itemTagTextColor={itemTagTextColor}>
<Body4>{`${newsItem?.itemTag || []}`}</Body4> <<<<<< this check
</NewsItemTag>
<NewsItemName newsItemHeadingColor={newsItemHeadingColor}>{`${
newsItem?.itemTitle || [] <<<<<< this check
}`}</NewsItemName>
<Description textColor={textColor}>{`${
(newsItem.description &&
newsItem?.itemDescription) || [] <<<<<< this check
}`}</Description>
</NewsItem>
);
};
In fact yes, you have many many ways for improving your code and the readabilty of it.
In your <NewItem /> onClick function:
I have nothing to say for the first one, it seems correct since you want the item link or an empty array
For the second one, your are already using the optional chaining operator, the "?". This operator check if there is a value, if not, it returns null. That means that should be enough, since you check after your url variable is defined or not. So simply write that : : newsItem?.itemLink
For the third one, the one on your <Image /> component, First there is no need at all to put everything in backquote. Simply alt={newsItem?.itemImage || []} should work fine. Then, alt attribute is taking a string, so maybe change it with alt={newsItem?.itemImage || ""}
For the three last one, they are basically the same, you have content to display and you want to display nothing if there are empty. The fact here is that your are rendering some elements even if there is no content and this is not an optimized way. The cleanest way in my opinion in order to make your template more readable will be to define variable before your return function and based on this variable, you will display the appropriate section :
Before your return function:
const hasTag = !!newsItem?.itemTag;
const hasTitle = !!newsItem?.itemTitle;
const hasDescription = !!newsItem.description && !!newsItem?.itemDescription;
In your template:
{hasTag && <NewsItemTag color={itemTagColor} itemTagTextColor={itemTagTextColor}>
<Body4>{newsItem.itemTag}</Body4>
</NewsItemTag>}
{hasTitle && <NewsItemName newsItemHeadingColor={newsItemHeadingColor}>
{newsItem.itemTitle}
</NewsItemName>
{hasDescription && <Description textColor={textColor}>
{newsItem.itemDescription}
</Description>
I hope this was clear, hit me up if this wasn't.
Happy coding :)
There are multiple ways that you could handle this in javascript, assume that we have a component that receives a prop called newsItem which is an object
You could do that in the following ways
Using the logical operator or (es6)
const itemLinks = newsItem.links || []
you dont need the optional chaining operator (?.) here if you know that newsItem is an object and not undefined.
This is the shortest syntax, and it works fine as long as links is either undefined or an array
however it becomes problematic if later links become a boolean variable, which will yield incorrect results
This method is not safe, and it becomes cumbersome to handle as properties become nested deeply
Using lodash get method or some other library or a custom solution (es6)
because in es6 the only practical way was to either the logical operator and this was a common problem, custom solutions were brought it, basically any solution should have two advantages over the logical or operator
They should work fine with boolean values
They should be easier to work with deeply nested values
custom someDeepleyNested = get(newsItem, 'property1.property2.property3', 'defaultValue')
// there is no need for property1, property2, property3 to exist at all
Optional chaining operator and Nullish coalescing Operator (stage 4 finished proposals)
Because this was a common problem for all javascript developers,the language had to bring a native solution
const someNestedProperty = newsItem?.property1?.property2?.property3 ?? 'someDefaultValue'
// notice that both the Optional chaining operator and Nullish coalescing Operator, work with both undefined and null
Specific to react: use default props
const Component = ({newsLink}) => {}
Component.defaultProps = {
newsItem: {
itemLinks: []
}
}

how to set an initial value for getter in TypeScript

I have a getter to return a value that is calculated in another function. It has the form like this:
private get numErrors() {
return calculateErrors(key);
}
In my html I have {{ $ctrl.numErrors }}. I am using AngularJS. This renders {{ $ctrl. numErrors }} for a few seconds before the value numerical value is calculated and displayed. Is there a way I can get it to initially display 0 before I get a return value?
You could potentially get around this via ng-cloak.
But instead of having numErrors() as a getter directly returning your other function's results why not do something like this? Have calculateErrors() get called in your logic where you need it to and have it set the value. This will be automatically updated on your template.
numErrors: number = 0;
calculateErrors(key) {
// Do calculation
numErrors = resultOfCalculation;
}
Considering that it is numeric value and it shouldn't be checked if it strictly equals to undefined, it can be
private get numErrors() {
return calculateErrors(key) || 0;
}
Or
{{ $ctrl.numErrors || 0 }}

Cleaner data-bind within Knockout?

I'm new to using Knockout and am doing a very basic implementation that changes the color with a observable. Is there a cleaner way to write the following code?
<div class="selected" data-bind="style: { background: fullHexCode(mainScreenNavigationSelector()) !== false ? fullHexCode(mainScreenNavigationSelector()) : 'white' }"></div>
I have this in multiple spots on my page and they all use different params for the fullHexCode() function. It looks extremely messy. Any help would be great, thanks!
It looks like the logic depends on another observable so you could use a computed observable -- in the snippet below the backgroundColor computed observable depends on the mainScreenNavigationSelector observable.
That's just a simple example, you'll have to adjust it to your specific situation.
var MyViewModel = function () {
this.mainScreenNavigationSelector = ko.observable(false);
this.backgroundColor = ko.computed(function() {
return this.mainScreenNavigationSelector() ? 'green' : 'red';
}, this);
this.toggleColor = function() {
this.mainScreenNavigationSelector(!this.mainScreenNavigationSelector());
}
}
var viewModel = new MyViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="selected" data-bind="style: { 'background-color': backgroundColor }">
TEST
</div>
<button data-bind="click: toggleColor">Toggle Color</button>
You may deduplicate your HTML code by defining methods in your viewmodel. Named computeds are even better as they are naturally memoized, that is evaluated only once if used repeatedly in your HTML.
You may also factorize heavy expressions as with: or let: bindings in the parent node. For example: <div class='some-container' data-bind="let: { v: mainScreenNavigationSelector() }">... bindings based on v here... </div>.
Notice: let is better than with for this purpose. But it's a new binding which will be available in the next release of KO. You can polyfill it with a custom binding.
When JS expressions can't be avoided in your HTML code, try to make them as slick as possible. For example:
<div data-bind="style: {
background: fullHexCode(mainScreenNavigationSelector()) || 'white'
}"></div>
In Javascript, logical operator doesn't return true or false but the actual value of the last evaluated arguments. So:
a || b return a if not "falsy", otherwise b
a && b return b if a not "falsy", otherwise a
The last idiom is useful in KO bindings because contrary to Angular, KO bindings are regular JS expressions. They fail if some null/undefined occurs in a dot sequence (like a.b if a is undefined).
So instead of some tertiary operator hell like data-bind="text: object != null ? (object.a != undefined ? object.a : 'None') : 'None'", just write data-bind="text: object && object.a || 'None'"
Also [] and {} are not falsy, and it's actually a good thing. It allows to write things like data-bind="foreach: foo.optionalArray || ['default', 'stuff']"
However, Number(0) is false. So beware of something like data-bind="with: object.id && 'saved' || 'new'" which may not work as expected if 0 is a valid object id.
Also this last trick. If data-bind="text: name" fails because name is undefined , then "data-bind="text: name || 'anonymous'" will still fail, but "data-bind='text: $data.name || 'anonymous'" will work as expected. As a convention, I write $data.attribute instead of attribute to convey the info about dealing with a optional attribute.

Angular js inline conditions - detect number

Data come from db , and this html element is a ng-repeat
{{count}}
My Question
How can i show if else condition that,
If count is null (nothing count value) I would show 0 (count value should be zero) , else show original count value
I hope there are solution like {{count if '' else ''}}
You could have have logic in your view with {{count || 0}} or {{ count ? count : 0 }} but this makes maintainability more difficult and duplicates logic.
A better option would be to create a custom filter. This will allow you to define you condition in a single place and makes it re-usable in many directives but also testable.
.filter('isEmpty', function() {
return function (data) {
return (data === null || data === undefined) ? 0 : data;
}
});
And you would use it like
{{item | isEmpty}}
See fiddle: http://jsfiddle.net/fhfsex4v/2/
EDIT: Here's the docs for filter that will explain what its doing and also how to pass optional extra parameters into your filters and other cool stuff.
https://docs.angularjs.org/guide/filter
How about {{ count ? count : 0 }}

Substring a JSON object value in an ng-repeat

I was wondering what would be the best way to accomplish getting a substring of a value from a JSON object that is being spit out in an ng-repeat. Currently I have:
<tr data-ng-repeat=" item in records | orderBy : '-score' | limitTo : 10 " ng-click="moreInfo(item)">
<td>{{$index+1}}</td>
<td>{{item.name}}</td>
<td>{{item.score}}</td>
</tr>
And what is displayed on the page is user-SmithJoe. I was wondering if it is possible in the html to do something like {{item.name.substring(5,item.name.length())}}. I am sure that doesnt work, but hopefully you understand what I am trying to accomplish from that. I ultimately just want SmithJoe to be the output.
This is the js:
data2.forEach(function(r) {
if (r && r.user && r.user.toLowerCase().indexOf($scope.searchText.toLowerCase()) !== -1) {
$scope.records.push(r);
}
});
It the for each part is just going through all of the returned json objects and adding them to records[] if they contain what I am looking for. So since I did it this way I didn't really see there any way to take a substring # that point?
Thanks.
Add a property to the object when you iterate:
data2.forEach(function(r) {
if (r && r.user && r.user.toLowerCase().indexOf($scope.searchText.toLowerCase()) !== -1) {
r.prettyName = r.name.replace("user-", "");
$scope.records.push(r);
}
});
And output that name:
<td>{{item.prettyName}}</td>
{{item.name.slice(5)}}
should works :)

Categories

Resources