I created an attribute directive ImageSpinloaderDirective
to conditionally change the appearance of the user's profile picture, depending on wether there is a picture and if it is already loaded:
- If the
src
attribute's URL can't be resolved (404, etc), a default image should be shown - If the picture is still loading a loading animation should be shown
- If the user has changed, a new profile image is loaded and the spinloader should appear again until loading is finished
My approach is to listen to the DOM-events load
and error
in the ImageSpinloaderDirective
and to set the src
attribute and img-loading
class of the <img>
element accordingly. The img-loading
class sets the images opacitiy to 0 and uses an animated gif as background image.
Now I wanted to change the directive's isLoading
field from the parent component (UserDetailsComponent
) every time a different user is displayed. I thought that using the @ViewChild
annotation within that component would be the right choice to accomplish that. However the binding seems to fail for some reason, since UserDetailsComponent
's imageSpinloaderDirective
is always undefined.
Is it possible at all to use attribute directives with the @ViewChild
annotation? Is there any better/alternative way to bind an attribute directive to a component? Or is this approach totally wrong and should be implemented in a different way?
user-details.component.ts
Component({
moduleId: module.id,
selector: 'user-details',
templateUrl: 'user-details.html',
styleUrls: [
'./../grid/grid-item-details.css',
'./../grid/grid.css'
],
animations: [fadeInOut]
})
export class UserDetailsComponent extends GridItemDetailsComponent<User>
{
public user: User;
public profileImageUrl: string;
@ViewChild(ImageSpinloaderDirective)
public imageSpinloaderDirective: ImageSpinloaderDirective;
public loadDetails(item: User): void
{
this.user = item;
this.imageSpinloaderDirective.isLoading = true;
this.profileImageUrl = `${this.configService.config.profileApiUrl}/image/${item.id}`;
}
}
user-details.html
<div class="row">
<div class="col-lg-12">
<div class="img-loader">
<img id="foo" imgspinloader
class="img-rounded"
src="{{profileImageUrl}}">
</div>
</div>
</div>
image-spinloader.directive.ts
import
{
AfterContentChecked,
Directive,
HostBinding,
HostListener,
Input,
OnInit
} from '@angular/core';
@Directive({
selector: '[imgspinloader]'
})
export class ImageSpinloaderDirective
{
private defaultSrc: string = "assets/img/user-image-invitation-2x.png";
@Input() @HostBinding() src;
@Input() @HostBinding("class.img-loading") isLoading: boolean;
@HostListener('load') complete()
{
this.isLoading = false;
}
@HostListener('error') onError()
{
this.src = this.defaultSrc;
this.isLoading = false;
}
}