Hovering over mat-card - html

How to show mat-card-action on hover over card. When i hover over one card it shows action for every card.
<mat-card (mouseover)="hover=true" (mouseleave)="hover=false" [className]="flashcard.privatno ? 'privatno' : 'javno'" *cdkVirtualFor = "let flashcard of flashcards; let i = index" (click)="loadOne(i)">
<mat-card-content>
<p>{{seci(flashcard.pitanje)}}</p>
</mat-card-content>
<mat-card-actions >
<button mat-button *ngIf="hover==true">LIKE</button>
</mat-card-actions>
[Hovering over first card. It should show only LIKE on that card and not on the other][1]
[1]: https://i.stack.imgur.com/36Bsp.png

When you say *ngIf="hover==true" you are comparing all the elements of the for loop with the same variable, that's why all will display/hide at the same time. You need a way to differentiate each element. If your flashcard item have an id, you can try something like this:
.html:
<mat-card (mouseover)="toggleHover(flashcard.id)" (mouseleave)="removeHover()" [className]="flashcard.privatno ? 'privatno' : 'javno'" *cdkVirtualFor = "let flashcard of flashcards; let i = index" (click)="loadOne(i)">
<mat-card-content>
<p>{{seci(flashcard.pitanje)}}</p>
</mat-card-content>
<mat-card-actions >
<button mat-button *ngIf="hoveredElement === flashcard.id ">LIKE</button>
</mat-card-actions>
</mat-card>
.ts
public hoveredElement:any;
toggleHover(id) {
this.hoveredElement = id
}
removeHover() {
this.hoveredElement = null;
}

Related

Angular 8 display one image at a time

I am trying to display an avatar for each object that I have. The object is located in a MongoDB database and it is also displayed one by one. The problem that I have is that if I display the images as I display the objects (let's say I have 3 objects displayed), it shows all the 3 avatars for each object displayed, not just one image for each object. This is my code:
<ng-container *ngFor="let e of InstructorData">
<mat-card class="example-card" *ngIf="e.slope === 'Sinaia'">
<mat-card-header>
<div mat-card-avatar class="margin" *ngFor="let avatar of imagesData1">
<img class="example-header-image" mat-card-image [src]="avatar" >
</div>
<mat-card-title>{{e.name}} {{e.surname}}</mat-card-title>
<mat-card-subtitle>{{e.phoneNumber}}</mat-card-subtitle>
<mat-card-subtitle>{{e.email}}</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<p>{{e.description}}</p>
</mat-card-content>
</mat-card>
</ng-container>
imagesData1: any = [
"./assets/img/ski1.jpg",
"./assets/img/ski2.jpg",
"./assets/img/ski3.jpg"
];
You need to modify your code to below you don't need to iterate over all the images just fetch the image according parent loop key it will always print single image
<ng-container *ngFor="let e of InstructorData; let key = index">
<mat-card class="example-card" *ngIf="e.slope === 'Sinaia'">
<mat-card-header>
<div mat-card-avatar class="margin">
<img class="example-header-image" mat-card-image [src]="imagesData1[key]" >
</div>
<mat-card-title>{{e.name}} {{e.surname}}</mat-card-title>
<mat-card-subtitle>{{e.phoneNumber}}</mat-card-subtitle>
<mat-card-subtitle>{{e.email}}</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<p>{{e.description}}</p>
</mat-card-content>
</mat-card>
</ng-container>

Why mat-select clear the value in this reactive form?

I´m working on an app like google forms, we have a form with questions, we get these questions from an api and dynamically render the form. the problem is that when i want to add validation to the form with angular reactive forms, the mat-select stops working and every time i select a value, the select is clear.
I already try to adapt this https://medium.com/aubergine-solutions/add-push-and-remove-form-fields-dynamically-to-formarray-with-reactive-forms-in-angular-acf61b4a2afe with no luck
template
<mat-horizontal-stepper [linear]="true" #stepper style="background: transparent">
<mat-step *ngFor="let materia of materias;let m = index;" [stepControl]="createForm(materia.id)">
<ng-template matStepLabel>{{materia.descripcion}}</ng-template>
<form [formGroup]="getForm(materia.id)">
<div fxLayout="column" fxLayoutAlign="space-around stretch" fxLayoutGap="10px">
<div>
<mat-card *ngFor="let competencia of materia.competencias">
<mat-card-header>
<mat-card-title>{{competencia.nombre}}</mat-card-title>
</mat-card-header>
<mat-card-content>
<div fxLayout="column" fxLayoutGap="30px" formArrayName="{{createFormArray(materia.id,competencia.id)}}">
<div fxLayout="row">
<i>{{competencia.descripcion}}</i>
</div>
<div fxLayout="column" fxLayoutGap="2px">
<span *ngFor="let competenciaSimple of competencia.competenciasSimples; let i = index;" fxLayout="row">
<mat-form-field appearance="outline"
*ngIf="competenciaSimple !== null && competenciaSimple !== undefined" fxFlex>
<mat-select (selectionChange)="onSelected($event,competenciaSimple)"
[value]="getSelectedValue(competenciaSimple)" [formControl]="createControl(materia.id,competencia.id)" [id]="i">
<span *ngFor="let fase of competenciaSimple.fases">
<mat-option *ngFor="let conductaObservable of fase.conductasObservables"
[value]="conductaObservable">
{{conductaObservable.descripcion}}
</mat-option>
</span>
</mat-select>
</mat-form-field>
<!-- <input type="hidden" value="competenciaSimple" [(ngModel)]="calificacion.conductasObservables[i].competenciaSimple"/> -->
</span>
</div>
</div>
</mat-card-content>
</mat-card>
</div>
<div fxLayout="row">
<button mat-flat-button type="button" matStepperPrevious *ngIf="!isFirstPage" color="primary"
fxFlex="20">ANTERIOR</button>
<span fxFlex="80"></span>
<button mat-flat-button type="button" *ngIf="!isLastPage" color="primary" fxFlex="20">SIGUIENTE</button>
<button mat-flat-button type="button" (click)="onSubmit()" *ngIf="isLastPage" color="accent"
fxFlex="20">TERMINAR</button>
</div>
</div>
</form>
</mat-step>
</mat-horizontal-stepper>
component.ts
forms = new Array<FormGroup>();
getForm(index: number): FormGroup {
return this.forms[index];
}
createForm(index: number): FormGroup {
const form = this.formBuilder.group({});
this.forms[index] = form;
return form;
}
createFormArray(index: number, name: string): string {
const array = this.formBuilder.array([]);
const arrayName = `competencia_${name}`;
this.forms[index].addControl(arrayName, array);
return arrayName;
}
createControl(index: number, arrayName: string): FormControl {
const control = this.formBuilder.control(false);
const an = `competencia_${arrayName}`;
const array = (this.forms[index].get(an));
(array as FormArray).push(control);
return control;
}
getControlId(): string {
this.controlNumber += 1;
return this.controlNumber.toString()
}
The idea is to validate the mat-select as required so the stepper cant advance when no value is selected, that works if a add the Validator, but the mat-select doesn´t persist the value so if i remove the [formControl]="createControl(materia.id,competencia.id)" the mat-select work but no validation result.
There are several popular open source Angular Form Libraries (you can take a look at the source code and see how they dynamically add validation's to a control):
GitHub: ng-dynamic-forms
GitHub: ngx-formly
GitHub: angular-schema-form (AngularJS) - Generate forms from a JSON schema
GitHub: ngx-schema-form - HTML form generation based on JSON Schema
GitHub: angular2-json-schema-form - Angular 2 JSON Schema Form builder

Do different things in the same function depending on which html component called that function

I am making a basic toggle app in angular.I have two toggle buttons.I am displaying a message in toggle.I want to call the same function on toggle change but do different things depending on which button was toggled.Is it possible or do I have to use two different functions? Thanks in advance for the help.
Here is my template code:
<mat-card class="card" fxLayout="column" fxLayoutAlign="center center" >
<form class="example-form" [formGroup]="formGroup" (ngSubmit)="onFormSubmit()" ngNativeValidate>
<mat-action-list>
<mat-list-item > <mat-slide-toggle
(change)="onChange($event);displayMessage($event)" formControlName="Policy1" >
<span class="label">{{message}}</span></mat-slide-toggle></mat-list-item>
<mat-list-item > <mat-slide-toggle (change)="onChange($event)" formControlName="Policy2">Policy2</mat-slide-toggle></mat-list-item>
</mat-action-list>
<p>Form Group Status: {{ formGroup.status}}</p>
<button mat-raised-button [disabled]="disable" class="button" type="submit">Save Settings</button>
</form>
</mat-card>
The funtion that I'm calling from the toggle button:
displayMessage(e){
if(e.checked)
{
this.message = 'Toggled';
}
else
this.message = 'Slide';
}
The $event object from the (change) emitter has a srcElement that you can refer to when you want to find out which element caused the change.
If you want to retrieve the id of the element that called the function you do so with event.srcElement.id
You can pass more arguments to the handler:
<mat-slide-toggle *ngFor="let item of thingsToDo; let i = index"
(change)= "onToggle($event, item.id, i)">
onToggle = (event, id, indexInArray) => { //...

mwlDraggable (Drag & Drop) - minimum length of move

I have a problem with mat-card, that is draggable and also contains some button. Unfortunately, on my PC button (click) don't work at all, on my collegue it works sometimes. We think that it can be caused as click is treated as dragging element. Is there anyway to set minimal length of move (dragging), which causes starting treating object as moved?
It was tested on 2 machines - the same code, different behaviour - one one (click) on button is never executed, on second one - sometimes.
What I found now on my computer - while I'm debugging it (Chrome) - when I move my mouse cursor over cards on one of cases occurring this - I get blue "shadow" over the whole app from debugger, but button is clickable - it works as I expected, otherwise - not.
<mat-card mwlDraggable *ngFor="let item of items; let i = index" [dragActiveClass]="'field-dragged'"
[dropData]="item" (dragEnd)="itemFieldDragEnd($event, item)">
<mat-card-content>
<div class="pull-left m-t-5">
{{item.name}}
<div class="field-meta truncate">
{{item.desc}}
</div>
</div>
<div class="pull-right">
<button mat-icon-button (click)="onItemFieldClick($event, item)" matTooltip="somehint">
<mat-icon class="md-24">arrow_forward_ios</mat-icon>
</button>
</div>
</mat-card-content>
</mat-card>
I want to obtain way to force mwlDraggable to become draggable only if it was moved really by let's say 10px, not before. Or any other solution that would work for that problem.
I found solution on github: https://github.com/mattlewis92/angular-draggable-droppable/issues/53
Exemplary fully implemented solution using above:
<mat-card mwlDraggable *ngFor="let item of items; let i = index" [dragActiveClass]="'field-dragged'"
[dropData]="item" (dragEnd)="itemFieldDragEnd($event, item)"
[validateDrag]="dragThresholdValidator">
<mat-card-content>
<div class="pull-left m-t-5">
{{item.name}}
<div class="field-meta truncate">
{{item.desc}}
</div>
</div>
<div class="pull-right">
<button mat-icon-button (click)="onItemFieldClick($event, item)" matTooltip="somehint">
<mat-icon class="md-24">arrow_forward_ios</mat-icon>
</button>
</div>
</mat-card-content>
</mat-card>
public dragThresholdValidator({ x, y }: any): boolean {
const min_drag_threshold = 5;
return Math.abs(x) > min_drag_threshold || Math.abs(y) > min_drag_threshold;
}

Angular 7 iterate json by button click

As I mentioned in title, I want to iterate by button click over my json response. See below my json file:
{
"id": 2,
"name": "Słownictwo",
"flashcardLists": [
{
"id": 17,
"frontside": "dasfasdv",
"backside": "csdascd"
},
{
"id": 18,
"frontside": "dsadsaad",
"backside": "sdasdadad"
},
{
"id": 19,
"frontside": "dasdsadd",
"backside": "sdaddsa"
}
]
}
FlashcardHTML:
<div class="flip-container" (click)="flip()" [class.flipped]="flipped" >
<mat-card-header></mat-card-header>
<mat-card-content *ngFor="let flashcard of flashcardLists" class="flipper">
<mat-card class="front">
{{flashcard.frontside}}
</mat-card>
<mat-card class="back">
{{flashcard.backside}}
</mat-card>
</mat-card-content>
</div>
I have component where in one time I want only one "frontside" and "backside".
Next, it will be replaced by button clicked which will increase counter but I don't know how to do that. I tried something like this flashcard[0].frontside, but it was KO. Maybe someone has encountered the same problem and can help me.
For all answers thanks in advance
If I understand properly, you wanna be flipping a single card.
Something like this should do:
<mat-card-content *ngFor="let flashcard of flashcardLists" class="flipper">
<mat-card [class.front]="!flashcards[i].isFlipped"
[class.back]="flashcards[i].isFlipped"
(click)="flashcards[i].isFlipped = !flashcards[i].isFlipped "
>
{{ flashcards[i].isFlipped ? flashcard.backside : flashcard.frontside }}
</mat-card>
</mat-card-content>
Just add this map of card states to y0our component:
flashcards: { [key: string]: boolean } = {};
You don't need an *ngFor in your mat-card-content, instead bind it to a flipCard variable, which will change on button iterate click. As for the front or back, use another variable isFront which you'll update on flip button click. something like this:
component.html:
<mat-card>
<mat-card-header></mat-card-header>
<mat-card-content class="flipper">
<div class="front" *ngIf="isFront">
{{flashcard.frontside}}
</div>
<div class="back" *ngIf="!isFront">
{{flashcard.backside}}
</div>
</mat-card-content>
<mat-card-actions>
<button (click)="iterate()" class="btn-flashcard" mat-button mat-raised-button id="previous">
Następna
</button>
<button mat-button (click)="flip()" [class.flipped]="flipped">LIKE</button>
<button mat-button>SHARE</button>
</mat-card-actions>
</mat-card>
component.ts:
flashcardList: any[] // use your flashCard class instead of any
flashcard: any; // use your flashCard class instead of any
isFront = true;
ngOnInit() {
// init first card
// flashcard = first card
}
iterate() {
// logic for getting the next card
// flashcard = next card
// e.g.
let index = this.flashcardList.indexOf(this.flashcard);
this.flashcard = this.flashcardList[index + 1];
}
flip() {
this.isFront = !this.isFront;
}