ngFor rendering items on top of each other in resposive carousel - javascript

I'm using Angular 11 and https://www.npmjs.com/package/angular-responsive-carousel responsive carousel. The carousel is populated with mat-cards using an array. But when the page loads for the first time all the cards are rendered on top of each other as shown here
But it should look like this
But after the window is resized and taken back to the original size the carousel renders fine. I want to find a way to have the cards rendered right in the first go.
The HTML code looks like this
<carousel [dots]="true" [cellsToShow]=cellsToShow [height]="carouselHeight" [autoplay]="false"
[autoplayInterval]="2000"
[borderRadius]="2" [pauseOnHover]="true">
<div class="carousel-cell" *ngFor="let item of cards; index as i; trackBy: fun">
<mat-card class="example-card mat-elevation-z0">
<mat-card-header>
<a mat-card-avatar class="example-header-image" [href]="telegramUrl" target="_blank"</a>
<mat-card-title><b>Ultime Offerte</b></mat-card-title>
<mat-card-subtitle><i>Di Offerte Nerd</i></mat-card-subtitle>
</mat-card-header>
<br>
<div class="example-card-image"><img mat-card-image [src]=item[0] alt=""></div>
<mat-card-content>
<div [innerHTML]="item[1]"></div>
<br>
{{item[2]}} {{item[3]}}
</mat-card-content>
</mat-card>
</div>
The CSS looks like this.
.carousel{
position: relative;
z-index: 1;
padding-left: 10%;
padding-right: 10%;
}
.carousel-cells{
height: auto !important;
}
.carousel-cell{
height: auto !important;
}
.example-card {
max-width: 70vw;
text-align: left;
overflow-wrap: break-word;
height: auto !important;
border-style: solid;
border-width: thin;
}

It's just my guess but try to call cdRef.markForCheck() in ngAfterViewInit() lifecycle hook of the component where you use the carousel:
import { Component, AfterViewInit, ChangeDetectorRef } from '#angular/core';
#Component({
...
})
export class MyComponent implements AfterViewInit {
constructor(private cdRef: ChangeDetectorRef) {}
ngAfterViewInit(): void {
this.cdRef.markForCheck();
// also try this.cdRef.detectChanges(); instead of the above
}
}
But much better is to trigger change detection right at the point where the data which you feed to the carousel is ready.

It happens because the carousel renders before the data is added to the cards. Since there is no data during carousel rendering, carousel cells don't take the actual width inside the carousel.
There is a simple solution for this. We need to provide the default width for the carousel-cells. It can be done by adding an attribute provided in the package. 'cellWidth'. Refer the below code.
<carousel [cellsToShow]=5 [cellWidth]=250 [arrows]=false>
<div> Your content </div>
</carousel>
Since the attribute supports property binding we can try giving width of cells according to the screen size from typeScript file. I've not tried with that but the above code worked for me for laptop screen.

Related

Why isn't the width of an image changing after I use "width: 8%;" in CSS?

I'm new to react and I was trying to make a simple website with music notes (just some images) and wanted each note to change color when hovering over it. I know I can do this with :hover, but I wanted to try out useState for practice. I finally got the toggle feature (just the feature that made it change color when hovering) to work, but then in the process, the width got messed up. All other parts of css (position, color etc.) are working so I'm a bit confused as to why the width is staying the original width. Here is the code I have currently. The toggle feature is only on note3 right now because that's the note I was playing around with.
The first bit of code is essentially part of my index.js file with the music note I was working on.
const Body = () => {
const [isNote3, setNote3] = useState("true");
const ToggleNote3 = () =>{
setNote3(!isNote3);
}
const [isB1, setB1] = useState("true");
const ToggleB1 = () =>{
setB1(!isB1);
}
return (
<div>
<div className="sheetmusic">
<img className="sheet" src={sheetmusic} />
</div>
<div className="notes">
<div className={isNote3 ? "note3" : "n3"}>
<img onMouseEnter={ToggleNote3 } src={note1} />
</div>
</div>
</div>
)
}
The second snippet is the relevant css that corresponds with note3.
.n3{
filter: invert(100%) sepia(26%) saturate(5791%) hue-rotate(58deg) brightness(116%) contrast(101%);
left: 25%;
position: absolute;
max-width: 8%;
max-height: auto;
top: 30%;
}
.note3 {
position: absolute;
left: 25%;
max-width: 8%;
max-height: auto;
top: 30%;
}
Here is also a picture of the current situation on my website. (the large note is the one that currently toggles). 3
I've tried playing around with it for a bit and just don't seem to know the issue. Any help would be GREATLY appreciated, thanks so much.
From your CSS snippet both classes note3 and n3 have the same value for max-width so I don't see why the width would change. Try using different values.
Edit: In CSS by default img width and height are set to auto. So what you need to do is add img { max-width: 100%; } to confine all your images to the width of the parent container. See https://codesandbox.io/s/relaxed-mcnulty-p72by?file=/src/styles.css

Hoiw can i get y coordinates of my clicked element on the whole page?

I have big data on Y coordinate - there is virtual scroll and a lot of items are on the page on scroll.
When i click on the data then additional data is displayed for the clicked element.
That makes my page unfriendly for users because sometime the additional data is big - and when the additional data is collapsed sometimes scroll automatically goes down and the user needs to scroll a little bit up to see the data...
I need when the user click on the element- automatically page scrolls up or down to that clicked element on the Y coordinate
HTML
<div *ngFor="let mainRow of tableData; let i = index;" class="card" id="mainRow-{{i}}">
<div (click)="renderSubTable(i, mainRow, $event)">
... main data
</div>
<div *ngIf="mainRow.subTableData">
... additional data
</div>
</div>
** what i tried**
TS
i tried with offsetY
renderSubTable(numRow, mainRow, event) {
let y = pos.offsetY;
window.scroll(0,y);
}
but that takes only the Y coordinates of the viewport - not on the whole page.
PageY does not work also.
How can i resolve my issue
jQuery offset gives you the absolute position (relative to the document):
const el = document.getElementById('el');
el.onclick = function() {
const offset = $("#el").offset();
window.scroll(0, offset.top);
console.log(offset);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div style="height: 1000px; background: blue; padding-top:200px">
<div id="el" style="height: 100px; width: 100px; background: red; margin: 0 50px;">
</div>
</div>
Here is a code snippet for Angular
Template
<div style="height: 1000px; background: blue; padding-top:200px">
<div (click)="onClick($event)" style="height: 100px; width: 100px; background: red; margin: 0 50px;">
</div>
</div>
Controller
import { Component } from "#angular/core";
import $ from "jquery";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
onClick(event: MouseEvent) {
const offset = $(event.target).offset();
window.scroll(0, offset.top);
console.log(offset);
}
}
You can test it on Stackblitz
Hard to tell without an exact example (how your HTML is structured), but you should also take a look at:
(Doing on element's api not window api)
https://developer.mozilla.org/en-US/docs/Web/API/Element/scroll
and
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

Dynamic image is not loaded via :src

I am working on a simple Vue app, using vue-cli and webpack for that purpose.
So basicly i have 2 components, a parent and a child component ~
like this:
<template>
<div class="triPeaks__wrapper">
<div class="triPeaks">
<tri-tower class="tower"></tri-tower>
<tri-tower class="tower"></tri-tower>
<tri-tower class="tower"></tri-tower>
</div>
<div class="triPeaks__line">
<tower-line :towerLine="towerLineCards" />
</div>
<tri-pack />
</div>
</template>
the towerLineCards is the important thing there, it is a prop that is passed to the tower-line component, it is basicly a array with 10 elements, it is a array with 10 numbers that are shuffled, so it can be something like that:
[1,5,2,6,8,9,16,25,40,32]
this array is create via beforeMount on the lifecycle.
On the child component:
<template>
<div class="towerLine-wrapper">
<div class="towerLine">
<img v-for="index in 10" :key="index" class="towerLine__image" :src="getImage(index)" alt="">
</div>
</div>
</template>
<script>
export default {
props: {
towerLine: {
type: Array,
required: true
}
},
method: {
getImage (index) {
return '#/assets/images/cards/1.png'
}
}
}
</script>
<style lang="scss">
.towerLine {
display: flex;
position: relative;
top: -90px;
left: -40px;
&__image {
width: 80px;
height: 100px;
&:not(:first-child) {
margin-left: 3px;
}
}
}
</style>
the issue is with the :src image that i am returning via the getImage(), this way it is not working. If i change to just src it works just fine, i did this way just to test, because the number in the path should be dynamic when i got this to work.
What is wrong with this approach? any help?
Thanks
Firstly, you should use a computed property instead of a method for getImage().
And to solve the other problem, you could add require(YOUR_IMAGE_PATH) when you call your specific image or put it inside /static/your_image.png instead of #/assets/images/cards/1.png.

angular 2+ Trying to bind Input parameter in NgStyle

Problem: I want a single component (spacer) that will have width 100% and a height that can be input wherever it appears in the HTML (home.html in this test):
number 1
<spacer height="'200px'"></spacer>
no more
The spacer.html:
<div class="container-fluid spaceContainer" [ngStyle]="{'height': 'height'}">
spacer is here <<<--- this text is just for testing
</div>
The scss:
.spaceContainer {
width: 100%;
border: 1px solid red;
display: flex;
flex-direction: row;
}
Spacer.ts:
import {Component, Input, OnInit} from '#angular/core';
#Component({
selector: 'spacer',
templateUrl: './spacer.component.html',
styleUrls: ['./spacer.component.scss']
})
export class SpacerComponent implements OnInit {
#Input() height: string;
constructor() {
}
ngOnInit() {
console.log('height is '+ this.height);
}
}
When it runs, the console.log gives: height is '200px'
but the height of the red-bordered box is just enough to hold the 'spacer is here' text.
I struggle understanding binding a bit so I've tried:
<spacer height="200px"></spacer>
Console: height is 200px, which I thought would work but no change. Not understanding attr, I tried variants of attr.height.
This has to be easy and may help clear up my misunderstanding of how binding works.
Thanks in advance,
Yogi
Your mistake is located at this line:
[ngStyle]="{'height': 'height'}"
^^^^^^^^^^
it should be just height
You're binding height to string 'height' but you should bind it to height property of your component something like:
[ngStyle]="{'height': height}">?
or
[style.height]="height"

How to set Angular 4 background image?

I am trying the following lines to set the background image.but it not works. what are the way set background image in constantly in my application.
app.component.html
<div [ngStyle]="{'background' : 'url(./images/trls.jpg)'}">
<router-outlet></router-outlet>
<alert></alert>
</div>
You can use ngStyle to set background for a div
<div [ngStyle]="{background-image: 'url(./images/' + trls.img + ')'}"></div>
or you can also use built in background style:
<div [style.background-image]="'url(/images/' + trls.img + ')'"></div>
This works for me:
put this in your markup:
<div class="panel panel-default" [ngStyle]="{'background-image': getUrl()}">
then in component:
getUrl()
{
return "url('http://estringsoftware.com/wp-content/uploads/2017/07/estring-header-lowsat.jpg')";
}
Below answer worked for angular 4/5.
In app.component.css
.image{
height:40em; background-size:cover; width:auto;
background-image:url('copied image address');
background-position:50% 50%;
}
Also in app.component.html simply add as below
<div class="image">
Your content
</div>
This way I was able to set background image in Angular 4/5.
If you plan using background images a lot throughout your project you may find it useful to create a really simple custom pipe that will create the url for you.
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'asUrl'
})
export class BackgroundUrlPipe implements PipeTransform {
transform(value: string): string {
return `url(./images/${value})`
}
}
Then you can add background images without all the string concatenation.
<div [ngStyle]="{ background: trls.img | asUrl }"></div>
In Html
<div [style.background]="background"></div>
In Typescript
this.background=
this.sanitization.bypassSecurityTrustStyle(`url(${this.section.backgroundSrc}) no-repeat`);
A working solution.
What is the recommended way to dynamically set background image in Angular 4
this one is working for me also for internet explorer:
<div class="col imagebox" [ngStyle]="bkUrl"></div>
...
#Input() background = '571x450img';
bkUrl = {};
ngOnInit() {
this.bkUrl = this.getBkUrl();
}
getBkUrl() {
const styles = {
'background-image': 'url(src/assets/images/' + this.background + '.jpg)'
};
console.log(styles);
return styles;
}
I wanted a profile picture of size 96x96 with data from api. The following solution worked for me in project Angular 7.
.ts:
#Input() profile;
.html:
<span class="avatar" [ngStyle]="{'background-image': 'url('+ profile?.public_picture +')'}"></span>
.scss:
.avatar {
border-radius: 100%;
background-size: cover;
background-position: center center;
background-repeat: no-repeat;
width: 96px;
height: 96px;
}
Please note that if you write background instead of 'background-image' in [ngStyle], the styles you write (even in style of element) for other background properties like background-position/size, etc. won't work. Because you will already fix it's properties with background: 'url(+ property +) (no providers for size, position, etc. !)'. The [ngStyle] priority is higher than style of element. In background here, only url() property will work. Be sure to use 'background-image' instead of 'background'in case you want to write more properties to background image.
A very easy solution is to declare the image with a loading class and remove this class when image is loaded. You can then customize the placeholder in CSS, as it should be.
HTML :
<img class="persona-avatar loading" #avatar
(load)="avatar.classList.remove('loading'); "
src="/assets/img.png"
alt=""/>
SCSS :
.persona-avatar {
width: 50px;
height: 50px;
&.loading {
background-image: url("data:image/png;base64,iVBORw0K...");
background-size: contain;
}
}
It is better to use a base64 image to not have the same load problem with the placeholder.
This can be done with a custom directive, instead of having a method that will execute in a loop affecting performance.
In my case I would created a BackImgDirective like this:
import {Directive, ElementRef, Input, OnInit, Renderer2} from "#angular/core";
#Directive({ selector: '[appBackImg]'})
export class BackImgDirective implements OnInit {
#Input() appBackImg = ''
constructor(private el: ElementRef, private renderer: Renderer2) {}
ngOnInit() {
this.setBackImg()
}
setBackImg(){
this.renderer.setStyle(this.el.nativeElement, 'background-image', 'url(' + this.appBackImg + ')')
}
}
And use it like this:
<div [appBackImg]="slide.image"> </div>

Categories