I inherited some code off someone and I'm having a weird problem with it and can't figure it out. I have a screen that displays a grid of 9 flashcards - randomly selected - which should flip round and reveal a word when clicked (it's for teaching kids words).
It works fine until the Refresh button is pressed, which should select a different random set of nine cards. It does that OK, but the cards then no longer flip when clicked.
It seems that the flashcard component is 'emitting' the click event but after the 'refresh' operation the newly minted 'flashcards' appear to not be listening.
Anyone got any ideas what is going wrong and how I can fix it? Thanks!
The code is made of the page (flashcards.component.html) :
<ion-toolbar>
<ion-button (click)="refreshCards()" color="primary" expand="block" fill="solid" id="refresh">Refresh
</ion-button>
</ion-toolbar>
<div class="app-flash-card-grid">
<!-- flashCardsSelection has the random selection of cards -->
<!-- generate each flippable card -->
<flashCard class="app-flash-card" *ngFor="let flashCards of flashCardsSelection; let i = index">
<div class="flash-card-front">
<img alt="{{flashCards.name}}" src="{{flashCards.filename}}">
</div>
<div class="flash-card-back">
<h1>{{flashCards.name}}</h1>
</div>
</flashCard>
</div>
in flashcards.component.ts :
refreshCards() {
let fcardindex = 0;
let x = 0;
// clear selection
for (fcardindex = 0; fcardindex < 9; fcardindex++) {
this.flashCardsSelection.pop();
}
// populate selection
for (fcardindex = 0; fcardindex < 9; fcardindex++) {
const wordlistsize = this.flashCardsArray.length;
x = Math.round((Math.random() * 100) * wordlistsize / 100);
console.log('x=' + x);
if (this.categoryfilter.length > 0) {
// check the category filter
if (this.categoryfilter.indexOf(this.flashCardsArray[x].category) == -1) {
// not filtered
console.log('not filtered');
// check for duplicates
// is this one already in the selection array?
if (this.selectionContains(this.flashCardsSelection, this.flashCardsArray[x].name)) {
console.log('dupe found');
// if so, decrement the index and go again
fcardindex = fcardindex - 1;
} else {
// if not, add it
console.log('no dupe, adding');
this.flashCardsSelection.push((this.flashCardsArray)[x]);
}
} else {
// filtered out, so decrement index and go again
// console.log('category filtered!');
fcardindex = fcardindex - 1;
}
} else {
// no filter defined
// console.log('no filter defined, adding');
this.flashCardsSelection.push((this.flashCardsArray)[x]);
}
}
}
and the flashcards.component.scss file:
.flip-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
align-items: center;
.flip-item {
display: flex;
width: 50%;
justify-content: center;
align-items: center;
.panel {
float: left;
width: 45vmin;
height: 45vmin;
margin: 8px;
position: relative;
font-size: .8em;
-webkit-perspective: 600px;
perspective: 600px;
display: block;
@media(min-width: 768px) {
width: 32vmin;
height: 32vmin;
}
@media(min-width: 1024px) {
width: 24.5vmin;
height: 24.5vmin;
}
.front {
float: none;
position: absolute;
top: 0;
left: 0;
z-index: 900;
width: inherit;
height: inherit;
border: 1px solid #ccc;
background: #6b7077;
text-align: center;
-webkit-transform: rotateX(0) rotateY(0);
transform: rotateX(0) rotateY(0);
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-transition: all .4s ease-in-out;
transition: all .4s ease-in-out;
}
&.flip {
.front {
z-index: 900;
// border-color: #eee;
background: #333;
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.2);
-webkit-transform: rotateY(179deg);
transform: rotateY(179deg);
}
.back {
z-index: 1000;
background: #4c8dff;
-webkit-transform: rotateX(0) rotateY(0);
transform: rotateX(0) rotateY(0);
}
}
.back {
float: none;
position: absolute;
top: 0;
left: 0;
z-index: 800;
//width: inherit;
//height: inherit;
// border: 1px solid #ccc;
background: #4c8dff;
color: #fff;
-webkit-transform: rotateY(-179deg);
transform: rotateY(-179deg);
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-transition: all .4s ease-in-out;
transition: all .4s ease-in-out;
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
h1 {
margin: 0;
font-size: 18px;
text-transform: capitalize;
}
}
}
}
}
.app-flash-card-grid {
display: flex;
flex-wrap: wrap;
.app-flash-card {
@media screen and (min-width: "768px") {
max-width: 33.33%;
}
max-width: 50%;
width: 100%;
padding: 8px;
.flash-card-back {
h1 {
margin: 0;
font-size: 18px;
text-transform: capitalize;
color: #fff;
}
}
}
}
.pos-center {
margin: auto;
display: block;
}
.sc-ion-buttons-md-h {
width: 85px;
justify-content: flex-end;
}
.flipper {
transition: 0.6s;
transform-style: preserve-3d;
position: relative;
//border: 1px solid #000;
&:after {
content: "";
display: block;
padding-bottom: 100%;
}
.front, .back {
display: flex;
align-items: center;
justify-content: center;
backface-visibility: hidden;
margin: 0;
position: absolute;
top: 0;
left: 0;
height: calc(100% + 3px);
}
.front {
z-index: 2;
transform: rotateY(0deg);
}
.back {
transform: rotateY(180deg);
background: #4c8dff;
h1 {
margin: 0;
font-size: 18px;
text-transform: capitalize;
color: #fff;
}
}
}
flash-card-grid.component.html:
<ng-content></ng-content>
flash-card-grid.component.ts:
import {AfterContentInit, AfterViewInit, Component, ContentChildren, OnChanges, QueryList} from '@angular/core';
import {FlashCardComponent} from '../flash-card/flash-card.component';
@Component({
selector: '.app-flash-card-grid',
templateUrl: './flash-card-grid.component.html',
styleUrls: ['./flash-card-grid.component.scss'],
})
export class FlashCardGridComponent implements AfterViewInit, OnChanges, AfterContentInit {
@ContentChildren(FlashCardComponent)
groups: QueryList<FlashCardComponent>;
ngAfterViewInit() {
console.log('Inside FlashCardGridComponent:ngAfterViewInit');
// this.groups.toArray()[0].flipped = true;
this.groups.toArray().forEach((t) => {
t.flip.subscribe(() => {
this.openGroup(t);
});
});
}
ngAfterContentInit() {
}
openGroup(flashCard) {
// toggle flipped status
if (flashCard.flipped == true) {
flashCard.flipped = false;
} else {
this.groups.toArray().forEach((t) => t.flipped = false);
flashCard.flipped = true;
}
}
}
and the flashcard component itself (flash-card.component.html)
<div class="flip-container" (click)="emitflip()" [class.flipped]="flipped">
<div class="flipper">
<div class="front">
<ng-content select=".flash-card-front"></ng-content>
</div>
<div class="back">
<ng-content select=".flash-card-back"></ng-content>
</div>
</div>
</div>
and flash-card-component.ts:
import {Component, Input, Output, EventEmitter} from '@angular/core';
@Component({
selector: 'flashCard',
templateUrl: './flash-card.component.html',
styleUrls: ['./flash-card.component.scss'],
})
export class FlashCardComponent implements OnInit {
@Input() flipped = false;
@Output() flip: EventEmitter<any> = new EventEmitter<any>();
emitflip() {
console.log('emit');
this.flip.emit();
}
}
and finally the flash-card-component.scss:
.flip-container {
perspective: 1000px;
}
.flip-container.flipped .flipper {
transform: rotateY(180deg);
}
.flip-container, .front, .back {
width: 100%;
}
.flipper {
transition: 0.6s;
transform-style: preserve-3d;
position: relative;
border: 1px solid #dee2e3;
&:after{
content: "";
display: block;
padding-bottom: 100%;
}
.front, .back {
display: flex;
align-items: center;
justify-content: center;
backface-visibility: hidden;
margin: 0;
position: absolute;
top: 0;
left: 0;
height: calc(100% + 4px);
}
.front {
z-index: 2;
transform: rotateY(0deg);
}
.back {
transform: rotateY(180deg);
background: #4c8dff;
h1{
margin: 0;
font-size: 18px;
text-transform: capitalize;
color: #fff;
}
}
}