How to populate ng-style with object of CSS - css

I have built an object of CSS that I've defined the CSS property name and value. The goal is to build out a form to manipulate the value and automatically update the CSS on the page visually for the user.
I have it all working except for an elegant way to actually style the item in real time.
Here is the link to example https://codepen.io/uncommonjoe/pen/KKXyzBp
This is the object I created that I'm working with
vm.card = [
{
class: "card",
properties: [
{
name: "background-color",
value: "#FFFFFF"
},
{
name: "border",
value: "1px solid #FFFFFF"
},
{
name: "border-radius",
value: "5px"
},
{
name: "box-shadow",
value: "0 0 26px rgb(0 0 0 / 20%)"
},
{
name: "margin",
value: "0"
},
{
name: "padding",
value: "0"
}
]
}
];
And in the HTML I'm using ng-repeat to build out form controls
<div ng-repeat="card in vm.card">
<b>{{card.class}}</b>
<div ng-repeat="object in card.properties">
<label>{{object.name}}</label>
<input type="text" ng-model="object.value" />
</div>
</div>
This is the example of the item. As you can see, that's not dynamic and reusable.
<div class="card" style="width: 18rem;"
ng-style="{
'background-color': vm.card[0].properties[0].value,
'border': vm.card[0].properties[1].value,
'border-radius': vm.card[0].properties[2].value,
'box-shadow': vm.card[0].properties[3].value,
'margin': vm.card[0].properties[4].value,
'padding': vm.card[0].properties[5].value,
}">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
Go somewhere
</div>
</div>
I need a good way to do an ng-repeat so to speak in the ng-style and pull the name and define that as the CSS property and value as the value.

Define a styler function in your controller and then call it from ng-style:
vm.styler=function(props){
var styles={};
props.forEach(css=>{
styles[css.name]=css.value
})
return styles;
}
and in ng-style: ng-style="vm.styler(vm.cardObj[0].properties)"

Related

vuedraggable custom style for each individual item

I need to have each vuedraggable item to have different styling in the wrapper tag (for example based on the element's index) like so:
<div class="wrapper">
<div class="grid_item" style="grid-row: 1">
I am Item 1
</div>
<div class="grid_item" style="grid-row: 2">
I am Item 2
</div>
<div class="grid_item" style="grid-row: 3">
I am Item 3
</div>
</div>
I know this is a very simple example that doesn't really need the index but suppose a more complex scenario where it is necessary to have access to the index in the draggable component (not its child template).
Suppose the current component looks like this:
<template>
<div class="wrapper">
<draggable
:list="items"
class="grid_item"
item-key="name">
<template #item="{ element }">
I am {{ element.name }}
</template>
</draggable>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: {
draggable,
},
data() {
return {
items: [
{
name: 'Item 1'
},
{
name: 'Item 2'
},
{
name: 'Item 3'
},
],
}
},
methods: {
rowForItem(index) {
return `grid-row: ${index + 1}`;
}
},
}
</script>
<style>
.wrapper {
display: grid;
}
.grid_item {
background-color: gray;
}
</style>
How can I make use of the rowForItem method here?

angular 7 mat-card in only row using flex layout

I work with angular 7 and I use mat-card and flex layout.
I want to place side by side in only row
and the three cards will be centered in the page ( meaning the position of the three card will be in the center )
like this :
I use this code :
.html
<div fxLayout="row wrap" fxLayout.xs="column" fxLayoutAlign="space-around center" fxLayoutGap="25px">
<mat-card class="example-card" *ngFor="let member of members" fxFlex="calc(33%-25px)" fxFlex.sm="calc(50%-25px)" >
<mat-card-header>
<div mat-card-avatar class="example-header-image"></div>
<mat-card-title>{{member.title}}</mat-card-title>
<mat-card-subtitle>{{member.subtitle}}</mat-card-subtitle>
</mat-card-header>
<img mat-card-image [src]="member.url" alt="Photo of {{member.title}}">
<mat-card-content>
<p>
{{member.content}}
</p>
</mat-card-content>
</mat-card>
</div>
.ts
#Component({
selector: 'app-card-view-demo',
templateUrl: './card-view-demo.component.html',
styleUrls: ['./card-view-demo.component.scss']
})
export class CardViewDemoComponent implements OnInit {
constructor() { }
ngOnInit() {
}
members: {title: string, subtitle: string, content: string, url: string}[] = [
{title: 'Title', subtitle: 'Subtitle', content: 'Content here', url: '../../assets/img/app/shiba2.jpg'},
{title: 'Title', subtitle: 'Subtitle', content: 'Content here', url: '../../assets/img/app/shiba2.jpg'},
{title: 'Title', subtitle: 'Subtitle', content: 'Content here', url: '../../assets/img/app/shiba2.jpg'},
];
}
.css
.content {
padding: 16px;
}
.content > mat-card {
width: 200px;
}
but according to this code the cards are vertical and not in the same row.
and each card has 100 % size of the page.
can someone help me to do this by modifying the code of flex layout

css3 transtion not working with v-for in vue2

I use transtion in vue2. I added transition in css. Vue template show two box. One way use v-for and array, another way is use variable. btn2 is effective but btn1 not.
<style lang="sass">
.item
width: 120px
height: 120px
background-color: bisque
transition: margin-left 500ms
</style>
<template>
<div>
<div class="item" v-for="item in list" :key="item.index" :style="{marginLeft: item.index + 'px'}">{{ item.value }}</div>
<div class="item" :style="{marginLeft: left + 'px'}">123</div>
<button #click="addone">btn1</button>
<button #click="addtwo">btn2</button>
</div>
</template>
<script>
export default {
name: 'Heap',
data() {
return {
left: 100,
list: [
{
value: 12,
index: 10
}
]
}
},
methods: {
addone() {
this.list[0]['index']+=10
},
addtwo() {
this.left+=10
}
}
}
</script>
You are using the code :key="item.index" on your first div. Your code then updates that same index.
When a key's value changes, the component it is attached to re-renders. You are not seeing the animation occur because instead of dynamically incrementing the CSS, you are effectively just re-rendering the element with the new CSS.
The purpose of a key is to help Vue keep track of the identity of a given node in a list. It lets Vue know which nodes it can keep and patch up and which ones need to be rendered again.
You should use a static, non-changing value as a key where possible. In the following example I have added an id property to your object and used that as the key.
<style lang="sass">
.item
width: 120px
height: 120px
background-color: bisque
transition: margin-left 500ms
</style>
<template>
<div>
<div
v-for="item in list"
:key="item.id"
class="item"
:style="{marginLeft: item.index.toString() + 'px'}"
>
{{ item.value }}
</div>
<div class="item" :style="{marginLeft: left.toString() + 'px'}">123</div>
<button #click="addone">btn1</button>
<button #click="addtwo">btn2</button>
</div>
</template>
<script>
export default {
name: 'Example',
data() {
return {
left: 100,
list: [
{
id: '1',
value: 12,
index: 10,
},
],
};
},
methods: {
addone() {
this.list[0].index += 10;
},
addtwo() {
this.left += 10;
},
},
};
</script>

creating overlapping user icons

I'm trying to create a a component that overlaps over the user icons just as shown in the photo. I've seen this being used on google. If I have a list of user icons, how do I overlap them over each other?
I want something like this .
<template>
<div >
<ul>
<li v-for="user in userList" :key="user.key">
<user-icon-component :name="user.name" :image="user.picture"></user-icon-component>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "UserList",
props: {
userList: {
type: Object,
default: null,
},
},
};
</script>
<style>
</style>
The icon component is just an <img> tag with a user prop:
Vue.component('user-icon-component', {
props: ['user'],
template: `
<img :src="user.picture" width="32" height="32" />
`
})
Give the <li>s position: absolute and the <ul> position: relative to pull them out of the normal document flow. Set the left position on each <li> as a calculation from the index of the loop:
<ul class="icon-container">
<li v-for="(user, key, i) in userList" :key="user.key"
class="icon" :style="{ left: `${i * 20}px` }">
<user-icon-component :user="user"></user-icon-component>
</li>
</ul>
Here's a demo:
Vue.component('user-icon-component', {
props: ['user'],
template: `
<img :src="user.picture" width="32" height="32" />
`
})
/***** APP *****/
new Vue({
el: "#app",
data() {
return {
userList: {
'Bob': { name: 'Bob', key: 1, picture: 'https://www.flaticon.com/svg/static/icons/svg/3084/3084430.svg' },
'Mary': { name: 'Mary', key: 2, picture: 'https://www.flaticon.com/svg/static/icons/svg/3084/3084431.svg' },
'Paul': { name: 'Paul', key: 3, picture: 'https://www.flaticon.com/svg/static/icons/svg/3084/3084452.svg' },
}
}
},
});
.icon-container {
position: relative;
}
.icon {
position: absolute;
}
ul {
list-style-type: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul class="icon-container">
<li v-for="(user, key, i) in userList" :key="user.key"
class="icon" :style="{ left: `${i * 20}px` }">
<user-icon-component :user="user"></user-icon-component>
</li>
</ul>
</div>
I liked the idea in your question and took it as a challenge for myself and here is the result:
vue overlapping avatars component
basically the approach I took was to use the component's props as style in style binding. there are some scoped style as well but I think they can be set in the style binding as well if needs be (probably the code could be cleaner).
user prop is an array of objects that contains this property: img: 'imageURL' and using a v-for on a div element with:
:style="{ backgroundImage: `url(${user.img})`}"
we can set the images.
as for the overlapping part, divs have position: relative and using the index of v-for, the style binding becomes like this:
:style="{backgroundImage: `url(${user.img})`, left: `-${i*15}px`}"
which shifts every element to the left by 15px except for the first one.
here is the image of the final result:
Thanks for your question, it was fun :)

ngClass one conditional and other from variable

I have the following data structure:
this.filters = [
{
key: 'filter1',
active: true,
class: 'filter1Class'
},
{
key: 'filter2',
active: false,
class: 'filter2Class'
},
{
key: 'filter3',
active: true,
class: 'filter3Class'
}
];
And with this I want to create some icons in my html, every icon will have each own css class and it can be enabled or not depending on the active flag in the model.
My classes are quite simples, every filterNClass is just a color and the one I applied to set it enabled or nor is an opacity value:
.disabled {
opacity: 0.2;
}
.filter1Class {
color: #1993a0;
}
.filter2Class {
color: #720053;
}
.filter3Class {
color: #000055;
}
this is my html:
<div *ngFor="let filter of filters">
<mat-icon [ngClass]="filter.class"
[ngClass]="{disabled: !filter.active}">alarm_on
</mat-icon>
</div>
in this way, the second ngClass is not working, it does if I remove the first one, but I'm not able to manage both at the same time.
How can it be done?
I finally solved it by including a div:
<div *ngFor="let filter of filters">
<div [ngClass]="filter.class">
<mat-icon
[ngClass]="{disabled: !filter.active}">alarm_on
</mat-icon>
</div>
</div>
you can write so:
<div *ngFor="let filter of filters">
<mat-icon [ngClass]="{disabled: !filter.active,filter.class }">alarm_on
</mat-icon>
</div>
You will need to use object array syntax and include all checks under the same ngclass attribute as follows:
<div *ngFor="let filter of filters">
<mat-icon [ngClass]="{filter.class : true, 'disabled' : !filter.active }">alarm_on
</mat-icon>
</div>

Resources