Closed bcwhite-code closed 7 months ago
The example does not show all the option, but it has all boxTemplate
itemTemplate
imageTemplate
and thumbTemplate
. in they belong to the gallery config.
Could you add a reproduction? also have thought about trying v12
So, itemTemplate
in a galleryConfig added to a lightbox gallery (by ID) should work in v11 with that option just like it did in v9.
I'll see if I can narrow it down and get more information.
Working through this...
The documentation says:
The code, however, has:
<ng-container *ngIf="load" [ngSwitch]="type">
<ng-container *ngSwitchCase="Types.Image">
<gallery-image [src]="imageData.src"
[alt]="imageData.alt"
[index]="index"
[loadingAttr]="config.loadingAttr"
[loadingIcon]="config.loadingIcon"
[loadingError]="config.loadingError"
(error)="error.emit($event)"></gallery-image>
<div *ngIf="config.imageTemplate" class="g-template g-item-template">
<ng-container *ngTemplateOutlet="config.imageTemplate; context: imageContext"></ng-container>
</div>
</ng-container>
<gallery-video *ngSwitchCase="Types.Video"
[src]="videoData.src"
[mute]="videoData.mute"
[poster]="videoData.poster"
[controls]="videoData.controls"
[controlsList]="videoData.controlsList"
[disablePictureInPicture]="videoData.disablePictureInPicture"
[play]="isAutoPlay"
[pause]="currIndex !== index"
(error)="error.emit($event)"></gallery-video>
<gallery-iframe *ngSwitchCase="Types.Youtube"
[src]="youtubeSrc"
[autoplay]="isAutoPlay"
[loadingAttr]="config.loadingAttr"
[pause]="currIndex !== index"></gallery-iframe>
<gallery-iframe *ngSwitchCase="Types.Iframe"
[src]="data.src"
[loadingAttr]="config.loadingAttr"></gallery-iframe>
<ng-container *ngSwitchDefault>
<div *ngIf="config.itemTemplate" class="g-template g-item-template">
<ng-container *ngTemplateOutlet="config.itemTemplate; context: itemContext"></ng-container>
</div>
</ng-container>
</ng-container>
The ngIf
for config.itemTemplate
is inside a switch case of ngSwitchDefault
meaning that it doesn't get emitted for "any type of item template" but instead only those that are not image, video, youtube, or iframe.
That should mean that if I instead use imageTemplate
then my caption overlay should appear. It fails, however, because the let-data="data"
is not working. The attempt to access data.caption
throws an "undefined" exception.
The images are added to the gallery with this code:
gallery.addImage({
src: this.urlPrefix + parts[0] + this.urlFullPostfix,
thumb: this.urlPrefix + parts[0] + this.urlThumbPostfix,
caption: captionString,
} as ImageItemData)
What is the new method for accessing arbitrary data fields from within a template?
When you use itemTemplate
, you are creating your own type, so you also have to define this type when creating new items https://github.com/MurhafSousli/ngx-gallery/wiki/Custom-Templates#galleryitemdef
Therefore, you should use the gallery.ref.add
not addImage
galleryRef.add({
type: 'my-image-template',
data: {
src: 'IMAGE_SRC_URL',
thumb: 'IMAGE_THUMBNAIL_URL'
alt: 'Test'
}
})
and in template
<div *galleryItemDef="let item; let type = type">
<div *ngIf="type === 'my-image-template'">
<img [src]="item.src">
</div>
<div *ngIf="type === 'my-video-template'">
<video>
<source src="item.src">
</video>
</div>
</div>
I see.
I don't want a custom item since I'm only displaying images so I'll just use imageTemplate
and addImage()
.
This works as expected:
<ng-template #gallerySlide>
<span class="c-img-caption">testing</span>
</ng-template>
But this doesn't, showing nothing:
<ng-template #gallerySlide>
<span *galleryImageDef="let item; let active=active" class="c-img-caption">testing</span>
</ng-template>
As soon as I add *galleryImageDef to any element (span, div, ng-container, etc.), that element and below are not added to the DOM. There are no console messages.
You don't need to wrap them with ng-template
, the following is sufficient:
<span *galleryImageDef="let item; let active=active" class="c-img-caption">testing</span>
Basically using a star before the directive name is a shortcut to produce
<ng-template>
<span [galleryImageDef]="let item; let active=active" class="c-img-caption">testing</span>
</ng-template>
When querying with ViewChild
just use the directive class
@ViewChild(GalleryImageDef) imageDef: GalleryImageDef;
In my component's template, I tried:
...
<span *galleryImageDef="let item; let active=active" class="c-img-caption">testing</span>
...
but
@ViewChild(GalleryImageDef) imageDef: GalleryImageDef;
results in undefined. Even if it had been found, I can't assign that to imageTemplate
in setConfig()
because the types don't match: Type GalleryImageDef is missing the following properties from type TemplateRef
I tried the "expanded" version with [galleryImageDef]
but that is an error: Property galleryImageDef is not provided by any applicable directives nor by span element
For reference, here is my entire component:
import {AfterViewInit, Component, Input, OnChanges, SimpleChanges, TemplateRef, ViewChild} from '@angular/core';
import {CommonModule} from "@angular/common";
import {Gallery, GalleryState, ImageItemData} from "ng-gallery";
import {Lightbox} from "ng-gallery/lightbox";
import {MatIconModule} from "@angular/material/icon";
import {MatTooltipModule} from "@angular/material/tooltip";
import {AppConstants} from "../AppConstants";
import {ImageThumbnailStripComponent} from "./image-thumbnail-strip/image-thumbnail-strip.component";
const GALLERY_ID = "thumbstrip-gallery"
@Component({
standalone: true,
imports: [CommonModule, ImageThumbnailStripComponent, MatIconModule, MatTooltipModule],
selector: 'lightbox-thumbnail-strip',
template: `
<image-thumbnail-strip [urlPrefix]="urlPrefix" [urlPostfix]="urlThumbPostfix" [imageIds]="imageIds"
[orientation]="orientation">
</image-thumbnail-strip>
<ng-template #gallerySlide>
<span *galleryImageDef="let item; let active=active" class="c-img-caption">testing</span>
<!-- span *ngIf="(data.caption??'')!=''" class="c-img-caption">
{{data.caption}}
</span -->
</ng-template>
<ng-template #galleryBox>
<div class="c-btn c-btn-share-tab" title="Send image to another tab." i18n-title>
<mat-icon class="c-btn-icon" (click)="onShareToTabClicked($event)">open_in_browser</mat-icon>
</div>
</ng-template>
`,
styles: [`
:host, lightbox-thumbnail-strip {
display: block;
height: 100%;
width: 100%;
cursor: pointer;
}
.c-img-caption {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
padding: 0.4rem 1rem 0.4rem 1rem;
background-color: antiquewhite;
border: 1px solid black;
color: black;
border-radius: 1.0rem;
font-size: 1.2rem;
font-style: italic;
font-weight: bold;
}
.c-btn {
position: absolute;
left: 0.9em;
width: 2em;
height: 2em;
z-index: 60;
color: white;
opacity: 0.6;
text-shadow: 0 0 2px rgba(0,0,0,0.8);
transition: opacity linear 0.15s;
cursor: pointer;
}
.c-btn-icon {
width: 2em;
height: 2em;
font-size: 2em;
}
.c-btn:hover {
opacity: 1;
}
.c-btn-share-tab {
top: 0.9em;
left: 0.9em;
}
.c-btn-share-pv {
top: 3.9em;
left: 0.9em;
}
`]
})
export class LightboxThumbnailStripComponent implements AfterViewInit, OnChanges {
@Input() urlPrefix: string = ''
@Input() urlFullPostfix: string = ''
@Input() urlThumbPostfix: string = ''
@Input() imageTags: string[] = []
@Input() orientation: 'h'|'v' = 'h'
@ViewChild(ImageThumbnailStripComponent) thumbnailStrip!: ImageThumbnailStripComponent
@ViewChild('gallerySlide') gallerySlide!: TemplateRef<any>
@ViewChild('galleryBox') galleryBox!: TemplateRef<any>
imageIds: string[] = []
currentIndex: number = 0
constructor(private gallery: Gallery, private lightbox: Lightbox) {
}
ngAfterViewInit(): void {
this.thumbnailStrip.clicked$.subscribe((id: string) => {
this.openLightbox(id)
})
this.gallery.ref(GALLERY_ID).indexChanged.subscribe((state: GalleryState) => {
this.currentIndex = state.currIndex || 0
})
}
ngOnChanges(changes: SimpleChanges) {
this.imageIds = this.imageTags.map(tag => tag.split(':', 1)[0])
}
openLightbox(id: string) {
const gallery = this.gallery.ref(GALLERY_ID)
gallery.reset()
gallery.setConfig({
imageTemplate: this.gallerySlide,
boxTemplate: this.galleryBox,
thumb: this.imageTags.length > 1,
loadingStrategy: "lazy",
slidingDirection: "horizontal",
loop: false,
counterPosition: "top",
nav: true,
dots: false,
autoPlay: false,
})
this.lightbox.setConfig({
hasBackdrop: true,
keyboardShortcuts: true,
panelClass: 'fullscreen',
})
for (let tag of this.imageTags) {
let parts = tag.split(':')
if (parts.length == 1) parts[1] = parts.slice(1).join(':')
gallery.addImage({
src: this.urlPrefix + parts[0] + this.urlFullPostfix,
thumb: this.urlPrefix + parts[0] + this.urlThumbPostfix,
caption: (parts.length > 1) ? parts[1] : '',
} as ImageItemData)
}
for (let i = 0; i < this.imageIds.length; i++) {
if (this.imageIds[i].startsWith(id)) {
this.currentIndex = i
this.lightbox.open(i, GALLERY_ID)
break
}
}
}
onShareToTabClicked($event: MouseEvent) {
let parts = this.imageTags[this.currentIndex].split(':')
if (parts.length == 1) parts[1] = parts.slice(1).join(':')
window.open(`${AppConstants.SHOW_PAGE}?b=${parts[0]+this.urlFullPostfix}&c=${encodeURIComponent(parts[1])}`, 'dimg')
}
}
Ahhh... This works:
<ng-template #gallerySlide let-data>
<span class="c-img-caption" *ngIf="(data.caption??'')!=''">{{data.caption}}</span>
</ng-template>
Ofcourse, this code will not work
<ng-template #gallerySlide>
<span *galleryImageDef="let item; let active=active" class="c-img-caption">testing</span>
</ng-template>
Because wrapping content with ng-template
means it will not be rendered, therefore @ViewChild
will return undefined
This is how you should use it
@Component({
standalone: true,
imports: [GalleryModule, CommonModule, ImageThumbnailStripComponent, MatIconModule, MatTooltipModule],
selector: 'lightbox-thumbnail-strip',
template: `
<image-thumbnail-strip [urlPrefix]="urlPrefix" [urlPostfix]="urlThumbPostfix" [imageIds]="imageIds"
[orientation]="orientation">
</image-thumbnail-strip>
<span *galleryImageDef="let item; let active=active" class="c-img-caption">testing</span>
<div *galleryBoxDef class="c-btn c-btn-share-tab" title="Send image to another tab." i18n-title>
<mat-icon class="c-btn-icon" (click)="onShareToTabClicked($event)">open_in_browser</mat-icon>
</div>
`,
})
export class LightboxThumbnailStripComponent implements AfterViewInit, OnChanges {
@ViewChild(GalleryImageDef) imageDef: GalleryImageDef;
@ViewChild(GalleryBoxDef) galleryBox: GalleryBoxDef;
ngAfterViewInit(): void {
this.thumbnailStrip.clicked$.subscribe((id: string) => {
this.openLightbox(id)
})
// Set the config here, you can use setConfig or inside ref function
this.gallery.ref(GALLERY_ID, {
imageTemplate: this.imageDef.templateRef,
boxTemplate: this.galleryBox.templateRef,
loadingStrategy: "lazy",
slidingDirection: "horizontal",
loop: false,
counterPosition: "top",
nav: true,
dots: false,
autoPlay: false,
}).indexChanged.subscribe((state: GalleryState) => {
this.currentIndex = state.currIndex || 0
})
}
openLightbox(id: string) {
const gallery = this.gallery.ref(GALLERY_ID)
gallery.reset()
// Not sure if you need to do it here but you can update the config at any time
gallery.setConfig({
thumb: this.imageTags.length > 1,
})
this.lightbox.setConfig({
hasBackdrop: true,
keyboardShortcuts: true,
panelClass: 'fullscreen',
})
// .....
}
}
Don't forget to import GalleryModule
or its directives individually in the component's imports
As I said, I tried just
<span *galleryImageDef="let item; let active=active" class="c-img-caption">testing</span>
(no surrounding import
missing but there were no build errors. In addition, I could not pass a variable of the form
@ViewChild(GalleryImageDef) imageDef: GalleryImageDef;
to imageTemplate
because of a type mismatch.
But it does work with just
<ng-template #gallerySlide let-data>
<span class="c-img-caption" *ngIf="(data.caption??'')!=''">{{data.caption}}</span>
</ng-template>
I just tried it on my end, it works... They all have the same type when you pass the imageDef.templateRef
, maybe you are passing just imageDef
and thus you get type mismatch
If you provide a reproduction stackblitz I can help with it, but if you are good with ng-template
then we can close this I guess
imageDef.templateRef
would be the difference.
Thanks for all your help!
What is the expected behavior?
With v9, I had working code like this:
with
This worked and included my custom button (on the box) and custom data caption (on the item).
What is the current behavior?
In v11, though the
boxTemplate
of the config still works, theitemTemplate
does not. My custom data caption does not appear.Which versions are you using for the following packages?
Angular: 17.1.1 Angular CDK: 17.1.1 Angular CLI: 17.1.1 Typescript: v5.3.3 Gallery: 11.0.0
Is there anything else we should know?
The Gallery API says, under "available options in
GalleryConfig
":So it seems it should be supported but I did notice that the lightbox template examples page does not include
itemTemplate
in the config.Is there something I'm missing here?