angular 2+ Trying to bind Input parameter in NgStyle - javascript

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"

Related

CSS transition with expand/collapse in Angular

I have referred to other questions on SO related to expand/collapse, but the solutions are mostly based on setting height CSS property. I have tried, but those solutions are not applicable to my use case.
I have long string, if its length exceeds a limit I want to clip it by default and expand it by clicking on "Show more". On clicking "Show less", it should collapse the long string. I have implemented the functionality but the transition from collapsed to expanded does not have animation and looks jarring.
I would like to know how the collapsed/expanded transitions could made nicer.
Here's stackblitz reproduction of the same:
https://stackblitz.com/edit/angular-ivy-exalqr?file=src/app/app-expand/app-expand.component.ts
Just use the below css, where you set the width to the number of characters you want to show.
html
.cropper-wrapper {
width: 95px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
transition: all 1s ease-in-out;
}
.isCollapsed {
width: 100% !important;
display: inline-block;
}
css
<span class="cropper-wrapper" [ngClass]="{ isCollapsed: !isCollapsed }"
>{{ str }}
</span>
<span (click)="toggle()">{{ isCollapsed ? 'Show More' : 'Show less' }}</span>
ts
import {
ChangeDetectionStrategy,
Component,
Input,
OnInit,
} from '#angular/core';
#Component({
selector: 'app-expand',
templateUrl: './app-expand.component.html',
styleUrls: ['./app-expand.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppExpandComponent implements OnInit {
isCollapsed = false;
#Input() str;
private _input: string = '';
private _originalInput: string = '';
constructor() {}
ngOnInit() {
this.toggle();
}
toggle() {
if (this.isCollapsed) {
this._input = this._originalInput;
} else {
if (this._originalInput.length > 10) {
this._input = this._originalInput.substring(0, 10) + '...';
}
}
this.isCollapsed = !this.isCollapsed;
}
}
forked stackblitz

ngFor rendering items on top of each other in resposive carousel

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.

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

Angular Template Reference : Bind height of one div to height of another

I want to bind the height of one angular component to the height of a div. I am trying to use template reference to fetch the height.
Is it possible to do so using this method in a clean way? I want to do it something like this in snippet below.
<div [style.height.px]="section1.offsetHeight"></div>
<app-custom-tag #section1>
</app-custom-tag>
I want the height of <div> to be equal to height of <app-custom-tag>
I can't use css column layout for this as these two elements are not positioned together.
I think there are a few extra things you'll have to do.
When you reference a component inside the view, you'll get the class instance. With that in mind, you can inject the ElementRef so you can get information about the native element.
Consider this example:
foo.component.ts
#Component({
selector: 'foo',
template: `foo`,
styles: [`:host { color: red; display: block; height: 300px; } `]
})
export class FooComp {
get offsetHeight () {
return this.el.nativeElement.offsetHeight;
}
constructor (private el: ElementRef) { }
}
app.component.ts
#Component({
selector: 'my-app',
template: `
<foo #f></foo>
{{ f.offsetHeight }}
<br>
<div style="border: 1px solid black" [style.height.px]="f.offsetHeight"></div>
<!-- <div style="border: 1px solid black" [ngStyle]="{ height: f.offsetHeight + 'px' }"></div> -->
<br>
<input #i id="demo" type="text">
{{ i.id }}
`,
})
export class AppComponent { }
When you reference a DOM element in the view, you'll get that DOM element.
ng-run demo.

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