Angular - How to pass object dynamically to div element using [ngstyle] - html

I'm trying to make grid-row and grid-column to be dynamically populated from API.
component.html
<div *ngFor = "let element of elements">
<div [ngStyle]="styleObject(element.Row,element.Column)">{{element['Tool Name']}}</div>
</div>
component.ts
styleObject(row : String,Column:String): Object {
let obj :Object;
obj = {'grid-row':''+Number(row),'grid-column':''+Number(Column)};
console.log(obj);
return obj.toString;
}
This way in the console it is printing but it's not reflecting in UI. Console Image
It worked properly when I tried to populate the data manually as
<div [ngStyle]="{'grid-column':'9','grid-row':'9'}">{{element['Tool Name']}}</div>
In the Inspect Element mode, the attribute is showing as enter image description here
Where am I making mistake? Thanks in advance.

According to NgStyle docs,
Set a collection of style values using an expression that returns key-value pairs.
<some-element [ngStyle]="objExp">...</some-element>
You need to return styleObject as key-value pair type instead of string type.
styleObject(row : String, Column:String): Object {
let obj :Object;
obj = {'grid-row':''+Number(row),'grid-column':''+Number(Column)};
return obj;
}
You may check out this sample demo.
Updated for grid layout issue
For your grid layout, should have .grid-container and .grid-item.
.grid-container
Wrap the whole div element and turn it to the grid.
.grid-item
The grid item with [ngStyle] have to place here so that it will be positioned based on grid-row and grid-column.
component.html
<div class="grid-container">
<div *ngFor="let element of elements" class="grid-item"
[ngStyle]="styleObject(element.row, element.column)">
{{element['name']}}
</div>
</div>
.component.css
.grid-container {
display: grid;
grid-row-gap: 50px;
grid-template-columns: auto auto auto;
justify-content: center;
background-color: #2196F3;
padding: 10px;
}
.grid-item {
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(0, 0, 0, 0.8);
padding: 20px;
font-size: 30px;
text-align: center;
}
Sample Grid Layout on StackBlitz

Related

Weekday boxes have little lines or spaces between them if you look carefully. These lines or gaps are present in chrome but not in firefox

I am using vite react typescript.
My App component
function App() {
return (
<div>
<DatePicker />
</div>
);
}
My DatePicker component
function DatePicker() {
return (
<div className="datepicker-wrapper">
<input />
<input />
<Calendar />
</div>
);
}
My Calendar Component
function Calendar() {
return (
<div className="datepicker-popper">
<div className="datepicker-weekday">Sunday</div>
<div className="datepicker-weekday">Monday</div>
<div className="datepicker-weekday">Tuesday</div>
<div className="datepicker-weekday">Wednesday</div>
<div className="datepicker-weekday">Thursday</div>
<div className="datepicker-weekday">Friday</div>
<div className="datepicker-weekday">Saturday</div>
</div>
);
}
and finally my css file
body {
margin: 0;
}
.datepicker-wrapper {
position: relative;
font-family: monospace;
display: flex;
gap: 2rem;
}
.datepicker-popper {
position: absolute;
bottom: -0.8rem;
left: 50%;
translate: -50% 100%;
box-sizing: border-box;
padding: 0.3rem;
width: 100%;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
border-radius: 0.6rem;
}
.datepicker-weekday {
display: block;
background-color: palevioletred;
}
check this image
The boxes containing weekdays have weird space or line b/w them in chrome but not in firefox.
I tried making the container flexbox, grid. I also tried changing the div to span but none of them worked.
Here is the codesandbox link : https://codesandbox.io/p/sandbox/wonderful-stitch-deoc00?file=%2Fsrc%2Findex.css&selection=%5B%7B%22endColumn%22%3A1%2C%22endLineNumber%22%3A31%2C%22startColumn%22%3A1%2C%22startLineNumber%22%3A1%7D%5D
Try running this sandbox in chrome and firefox. You will see the difference.
I think you need just use 'normalize.css' file to avoid different results in different browsers. Normalizing file you can take from each site you have googled
EDIT: The problem from my perspective is the translate property.
There is a similar question with a probable explanation of why this happens.
If you add flex-wrap: wrap to .datepicker-wrapper, it will allow the .datepicker-popper to fall below the inputs and you can get rid of all the absolute positioning and translating that cause the white line to appear.
It will also make your code more readable.
here is the code with the above mentioned fixes applied: https://codepen.io/Aga-K-O/pen/RwBqZWq?editors=1100

Decrease padding size as parent element getting smaller

I have a grid that draws squares in cells. It has number of rows and number of columns, then it draw the grid cells and check if in each cell there should be a square or not (according to an array) and draws a square if needed.
The HTML end result looks something like this: (lets say I have 1 row and 3 columns and only 2 cells should have squars)
.row {
display: flex;
flex-wrap: wrap;
flex: 10000 1 0%;
}
.column {
display: flex;
flex-wrap: wrap;
max-width: 100px;
min-width: 10px;
padding: 4px;
border: 1px solid grey;
}
.square {
background-color: red;
width: 100%;
aspect-ratio: 1/1;
border-radius: 5px;
}
<div class="row">
<div class="column">
<div class="square"></div>
</div>
<div class="column">
<div class="square"></div>
</div>
<div class="column"></div>
</div>
The rows take the full width of the screen and the size of the columns should be identical between all of the columns and changing by the number of columns on the screen (For example if I have 5 columns they should all be with a width of 100px, but if I have 1000 columns they should all be with a width of 10px).
My problem is that after a certain break point in the column size the padding and border radius seems weird and I want to change their values when I hit that break point.
I can't use #container queries as there are still not fully supported.
If it help I'm using vue 2. but I think a CSS solution will be better in this case.
Trying to address the issue described:
My problem is that after a certain break point in the column size the
padding and border radius seems weird and I want to change their
values when I hit that break point. I can't use #container queries as
there are still not fully supported.
I crafted a little demo that helped me better explore the conditions bringing to such a scenario.
Obtaining border: collapse equivalent on flexbox items
The .row element remains a flexbox container but its flex items instead of having their border set, they are styled with their outline set.
The outline doesn't occupy space and it's expected to "collapse" when colliding with the outline produced by another element.
So to make it sure the layout wasn't affected by styling oddities, in the attempt to show off the borders of the flex items, this demo just relies on 2 key aspects to render those borders:
Setting the gap between the flex items
Setting the outline size expected to cover the gap left between
elements
.row {
gap: var(--col-gap);
}
.column {
outline: var(--col-gap) solid gray;
}
Using ::after for adding content to an element
Plus the red dot is applied as an ::after pseudo element with position:absolute, again to make sure that nothing affected the grid layout:
.column.square::after {
position: absolute;
content: '';
background-color: red;
width: 50%;
aspect-ratio: 1/1;
border-radius: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
The dashboard - exploring the options
Starting from there I added a "dashboard" with position: fixed that remains on top of the page and lets you control:
column width (px): here you set the width changing the cols per row according to the available container space
columns per row: here you set the cols per row changing their width according to the available container space
width
gap between cells (px): the gap between cells on the grid
toggle red dots visibility: will show/hide the red dots proving again that display: none; doesn't change the grid layout that it's depending exclusively by the .column element size set through the custom variable --col-width
toggle counter visibility: will show/hide the counter on top of each flex item
Conclusions so far:
Despite the efforts to minimize any interfence and taking all the steps needed to correctly setup a grid layout depending only on the fixed size of its cells, there are still some rendering issue with sometimes the occurrence of regular mismatching patterns on the border size for some lines. I should say that I only experience the problem on my laptop display and not on my desktop monitor so that's another factor.
I tried with different parameters on my demo and playing with the numbers, considering also the gap. A good and safe layout can be found minimizing potential problems (also raising the border size for example).
I couldn't get further than this using the flex layout.
const container = document.getElementById('container');
//draws the board
emptyElementAndFillWithColumns(container, 100);
//sets some columns randomly as .square
addRandomSquares(container);
//initializes the dashboard with the value coming from the css custom props
let columnsGap = parseInt(getCssCustomProp('col-gap'));
let columnsWidth = parseInt(getCssCustomProp('col-width'));
document.getElementById('gap').value = columnsGap;
document.getElementById('width').value = columnsWidth;
document.getElementById('width').dispatchEvent(new Event('change'));
document.getElementById('cols').value = Math.trunc(container.offsetWidth / (columnsWidth+columnsGap));
//input#width change event handler
document.getElementById('width')
.addEventListener('change', event => {
const width = parseInt(event.target.value);
const newCols = Math.trunc(container.offsetWidth / (width+columnsGap));
setCssCustomProp(container, 'col-width', `${width}px`);
document.getElementById('cols').value = newCols;
});
//input#cols change event handler
document.getElementById('cols')
.addEventListener('change', event => {
const cols = parseInt(event.target.value);
const newWidth = Math.trunc(container.offsetWidth / cols) - columnsGap;
setCssCustomProp(container, 'col-width', `${newWidth}px`);
document.getElementById('width').value = newWidth;
});
//input#gap change event handler
document.getElementById('gap')
.addEventListener('change', event => {
const gap = parseInt(event.target.value);
setCssCustomProp(container, 'col-gap', `${gap}px`);
columnsGap = gap;
});
//input#toggle-dots change event handler
document.getElementById('toggle-dots')
.addEventListener('change', event => {
container.classList.toggle('hide-dots');
});
//input#toggle-counters change event handler
document.getElementById('toggle-counters')
.addEventListener('change', event => {
container.classList.toggle('hide-counters');
});
//sets the --propName custom property at the style of target
function setCssCustomProp(target, propName, value){
target.style.setProperty(`--${propName}`, `${value}`);
}
//gets the --propName custom property value from the rule set on :root
function getCssCustomProp(propName){
const propValue =
getComputedStyle(document.documentElement).getPropertyValue(`--${propName}`);
return propValue;
}
//resets the container and appends a count number of columns
function emptyElementAndFillWithColumns(target, count){
for (i = 0; i <= count; i++) {
const column = document.createElement('div');
column.classList.add('column');
target.append(column);
}
}
//adds the square class to random .column elements in target
function addRandomSquares(target){
target.querySelectorAll('.column').forEach(column => {
if (Math.random() >= 0.5)
column.classList.add('square');
})
}
:root {
--col-width: 100px;
--col-gap: 1px;
}
*,
*::after,
*::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: sans-serif;
}
.row {
display: flex;
flex-wrap: wrap;
gap: var(--col-gap);
counter-reset: itemnr;
}
.column {
position: relative;
display: flex;
flex-wrap: wrap;
width: var(--col-width);
height: var(--col-width);
padding: 4px;
outline: var(--col-gap) solid gray;
}
.column.square::after {
position: absolute;
content: '';
background-color: red;
width: 50%;
aspect-ratio: 1/1;
border-radius: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.dashboard {
position: fixed;
right: 1rem;
top: 2rem;
border: solid darkgray;
padding: 1em;
z-index: 100;
background: gray;
color: white;
font-weight: 600;
font-size: 1.2rem;
opacity: .9;
}
.dashboard > *{
display: grid;
grid-template-columns: 1fr auto;
width: 100%;
gap: 1em;
}
.dashboard label{
}
.dashboard input[type="number"] {
width: 5em;
cursor: pointer;
}
.dashboard input[type="checkbox"] {
width: 1rem;
line-height: 1rem;
cursor: pointer;
}
#container.hide-dots .square::after{
display: none;
}
#container.hide-counters .column::before{
display: none;
}
small{
grid-column: 1 / -1;
font-size:.8rem;
text-align: center;
width: 100%;
margin-bottom: 1rem;
}
.column::before{
position: absolute;
counter-increment: itemnr;
content: counter(itemnr);
font-size: .8rem;
z-index: 10;
font-weight: 600;
}
<div id="container" class="row">
<div class="column square">
</div>
<div class="column"></div>
</div>
<div class="dashboard">
<div>
<label for="width">column width (px):</label>
<input
id="width" type="number" max="100" min="10">
</div>
<div>
<label for="cols">columns per row:</label>
<input
id="cols" type="number" max="50" min="1">
</div>
<div>
<label for="gap">gap between cells (px):</label>
<input
id="gap" type="number" max="10" min="0">
</div>
<div style="margin-top: 1rem;">
<label for="toggle-dots">toggle red dots visibility:</label>
<input id="toggle-dots" type="checkbox" checked>
</div>
<div>
<label for="toggle-counters">toggle counter visibility:</label>
<input id="toggle-counters" type="checkbox" checked>
</div>
</div>
If you want to increase or decrease size of padding you can give padding size in percent (%) that depends on parent element.

words in an ordered list will not wrap when the input is too large

My words are not wrapping when the input is too long. I have tried using flex-wrap and align-items but to no avail. As soon as the input it too large, it spills over. Here is how it looks below.
Here is my Css file
.recipe{
border-radius: 20px;
box-shadow: 0px 5px 5px black;
margin: 20px;
display: flex;
flex-direction: column;
justify-content: space-around;
background:white;
align-items: center;
min-width: 40%;
flex-wrap: 'wrap';
white-space: unset;
}
.image{
border-radius:50%;
width: 100px;
height: 100px;
}
And here is my javascript file recipe.js
import React from "react";
import style from './recipe.module.css'
const Recipe = ({title, calories, image, ingredients}) => {
const round = Math.round(calories);
return(
<div className = {style.recipe}>
<h1 >{title}</h1>
<ol>
{ingredients.map(ingredient => (
<li>{ingredient.text}</li>
))}
</ol>
<p>calories: {round} </p>
<img className = {style.image} src={image} alt=""/>
</div>
)
}
export default Recipe;
ingredients' parent element is not the div, it's <ol> tag. so you need to make that flex , then you can use flex-wrap, or you proably won't need.
Two things if you want an element's text to wrap:
their container element must have a limited width (but right now your OL lacks one, and is exceeding the width of its parent)
the element with the text to be wrapped should have a style of whitespace: wrap (but currently it doesn't ... or at least not an explicit one, but it's likely the default value already)

Is it possible to keep the styling of the original container when dragging one element of a container to another using CDKdraganddrop

I want to be able to add multiple elements from different containers into one container using CDK drag and drop. Is it possible to essentially colour code the different items. Right now when I drag an item it inherits the styling from the container its dropped into and i want it to keep its colour that was in the original container.
HTML
<h2>Studio 6</h2>
<div cdkDropList #donestudio6="cdkDropList" [cdkDropListData]="studio6"
[cdkDropListConnectedTo]="[moviesList, toglist, ppolist]" class="movie-list"
(cdkDropListDropped)="onDrop($event)">
<div class="movie-block" *ngFor="let item of studio6" cdkDrag>{{item}}</div>
</div>
<div id="first" class="bottompanel">
<h2>People</h2>
<div cdkDropList #moviesList="cdkDropList" [cdkDropListData]="MoviesList"
[cdkDropListConnectedTo]="[doneMovieList, donestudio2, donestudio3, donestudio4,
donestudio5, donestudio6, donestudio7]" class="movie-list" (cdkDropListDropped)="onDrop($event)">
<div class="peopleblock" *ngFor="let moviesList of MoviesList" cdkDrag>{{moviesList}}</div>
</div>
CSS
.movie-block {
padding: 10px 5px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
cursor: move;
background: white;
font-size: 12px;
}
.peopleblock {
padding: 10px 5px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
cursor: move;
background: white;
font-size: 12px;
background-color: rgb(53, 201, 206);
}
I'm explain like a closed book. The idea is that the class is applied to the "div" we want drag. To get it, we can add a new property to our objects. Imagine you has some like
people:any[] = ['Alice','George','Peter','John'];
movies:any[] = ['Start Wars','The thing','It'];
We can do some like
this.people=this.people.map(x=>({name:x,type:1}))
this.movies=this.movies.map(x=>({name:x,type:2}))
If the array is an array of object simply add the new variable using a forEach
this.people.forEach(x=>{x.type=1})
Now, we can use two class
.peopleClass
{
background:yellow!important;
}
.movieClass
{
background:red!important;
color:white!important;
}
And use the "type" to add this class to the div
<div class="example-container">
<h2>Studio</h2>
<div
cdkDropList
#studioList="cdkDropList"
[cdkDropListData]="studio"
class="example-list"
(cdkDropListDropped)="drop($event)">
<!--see how we use ngClass to add a class to the div-->
<div class="example-box"
[ngClass]="{'peopleClass':item.type==1,'movieClass':item.type==2}"
*ngFor="let item of studio" cdkDrag>
<span >{{item.name}}
</span>
</div>
</div>
</div>
You can see an example in stackblitz

Grid column line using Display property GRID

Here,in this code I want to understand what does the grid-column-start and grid-column-end specify ?
This is the HTML part of my code.
<div class="grid-container">
<div class="grid-item1">1</div>
<div class="grid-item2">2</div>
<div class="grid-item3">3</div>
<div class="grid-item3">4</div>
<div class="grid-item5">5</div>
<div class="grid-item6">6</div>
<div class="grid-item7">7</div>
<div class="grid-item8">8</div>
<div class="grid-item9">9</div>
This is the CSS part of my code.
.grid-container
{
display: grid;
grid-template-columns: 100px 100px 100px;
grid-gap : 50px;
background-color: black;
padding: 10px;
}
div
{
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(0, 0, 0, 0.8);
padding: 15px;
font-size: 30px;
text-align: center;
}
.grid-item1
{
grid-column-start : 1 ;
grid-column-end : 3;
}
This is the output of the code.
So, what does grid-column-end specify here ?
The grid-column-start property defines on which column-line the item will start.
and
The grid-column-end property defines how many columns an item will span, or on which column-line the item will end.
Please have a look of this below two examples: 1-grid-column-start 2-grid-column-end
So what it basically does is, When you give grid-column-start : 1| grid-column-end : 3. The column will start from the position one and span till the column 3.
The short-hand method of this will be grid-column: 1 / 3; Which starts from 1 and end at the column 3 which will make other columns to move.
See examples here : https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column
Hope this helps.
To understand what grid-template-column property does, check Chrome DevTools and inspect "grid-item1". Shorter method is: grid-column: 1/3; - div spans across two columns; starting at first line and finishing at third --> | col | col |
Instead of using pixels in grid-template-columns property, I suggest using this to avoid non-responsiveness:
grid-template-columns: repeat(12/1fr);
or
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr);