In Angular 1, it was fairly easy to create a loading directive that replaced content with a spinner and was used like so:
<div isLoading="$scope.contentIsLoading"></div>
Where contentHasLoaded is a simple boolean value you set in your controller after a data call. The directive itself, was simple, most of the work being done in a template:
<div class="spinner" ng-if="$scope.isLoading"></div>
<div ng-transclude ng-if="!$scope.isLoading"></div>
Is there a "clean" way to do this in Angular 2+? By clean I mean 1) within Angular, not using vanilla JS to directly manipulate the DOM and 2) Can be implemented as a single attribute on an existing element?
I did see this article as fallback:Image Loading Directive. However, it's a little more verbose than I would like: using a regular component requires me to wrap all my async content in a new tag rather than just adding an attribute.
What I'm really looking for is something in a structural directive (which are supposed to be designed for "manipulating the DOM.") However, all the examples I've seen are recreations of something like *ngIf, which hides content but does not insert new content. Specifically, can a structural template 1) have a template, or 2) insert a component or 3) insert something as simple as <div class="spinner"></div>
. Here's my best attempt so far:
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[loading]',
inputs: ['loading']
})
export class LoadingDirective {
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) { }
@Input() set loading (isLoading: boolean) {
if (isLoading) {
this.viewContainer.clear();
// INSERT A COMPONENT, DIV, TEMPLATE, SOMETHING HERE FOR SPINNER
} else {
this.viewContainer.clear();
// If not loading, insert the original content
this.viewContainer.createEmbeddedView(this.templateRef);
}
}
}