Conditional ng-class style applies to all divs - css

I have a list of card numbers for a patient that displays on a modal. If the card is set to be the default card then it should have a grey background - this is based on a value of true for the object.
<td ng-repeat="obj in paymentAndShipping">
<div ng-click="setDefaultPaymentMethod(obj.ElectronicPaymentAccountType, obj.ElectronicPaymentAccountID)" ng-class="{'chosencard' : obj.PreferredAccount }">
<span ng-show="obj.PreferredAccount" class="glyphicon glyphicon-ok"></span>
<p>{{obj.ElectronicPaymentAccountType}} {{trimCardNum(obj.CreditCardNumber)}}</p>
<p>Exp: {{obj.ExpirationDate}}</p>
</div>
</td>
Important piece:
ng-class="{'chosencard' : obj.PreferredAccount }"
Relevant CSS:
.chosencard {
background-color: #gray-lighter;
}
Here you can see that I iterate over the array and retrieve the objects using an ng-repeat and then apply an ng-class on the div inside the table cell to determine if the style is applied. The odd thing is that I do the same exact thing on the span inside the div and it reacts as I expect but the actual div doesn't - why is that?
Here's a screenshot of the behaviour.
P.S. When I click on an individual div the grey background disppears from all the others except the div that I clicked on. And of the four objects that you see above only one of them has a value of true and that's the last card.

I can't see how you selecting payment methods but you can do that like in demo below.
var app = angular.module('app', []);
app.controller('homeCtrl', function($scope) {
$scope.paymentAndShipping = [
{
ElectronicPaymentAccountType: "a",
CreditCardNumber: "155652",
ExpirationDate: "01/01/2014",
ElectronicPaymentAccountID: 1
}, {
ElectronicPaymentAccountType: "b",
CreditCardNumber: "155652",
ExpirationDate: "01/11/2014",
ElectronicPaymentAccountID: 2
}, {
ElectronicPaymentAccountType: "c",
CreditCardNumber: "1545652",
ExpirationDate: "21/01/2414",
ElectronicPaymentAccountID: 3
}, {
ElectronicPaymentAccountType: "d",
CreditCardNumber: "1554652",
ExpirationDate: "31/01/2024",
ElectronicPaymentAccountID: 4
}
];
$scope.PreferredAccount = $scope.paymentAndShipping[0];
$scope.setDefaultPaymentMethod = function(index) {
$scope.PreferredAccount = $scope.paymentAndShipping[index];
}
});
.chosencard {
background-color: yellow;
}
td {
border:solid 1px grey
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="homeCtrl">
<table>
<tr>
<td ng-repeat="obj in paymentAndShipping">
<div ng-click="setDefaultPaymentMethod($index)" ng-class="{'chosencard' :PreferredAccount==obj }">
<span ng-show="obj.PreferredAccount" class="glyphicon glyphicon-ok"></span>
<p>{{obj.ElectronicPaymentAccountType}} {{obj.CreditCardNumber}}</p>
<p>Exp: {{obj.ExpirationDate}}</p>
</div>
</td>
</tr>
</table>
<h3>PreferredAccount :</h3> {{PreferredAccount | json }}
</div>
</div>

Related

how to bold the first <p> tags in array

i've try using ::first-line but it bold all my p tags in array , the result i would like to want is bold only first p tags which is "Hello"
<div v-for="item in first" :key="item.id">
<p class="cat_name" >{{item.name}}</p>
</div>
<style scoped>
.cat_name >>> p::first-line
{
font-weight: bold;
}
</style>
Way 1- Use loop index
As you said if you only want to bold the first element then you can simply use the index of the loop and assign the bold class only to the first element.
Working demo-
new Vue({
el: "#app",
data() {
return {
first: [
{
id: 1,
name: "Hello"
},
{
id: 2,
name: "Bye"
},
{
id: 3,
name: "Nice to meet you"
}
]
}
}
})
.cat_name
{
font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(item,index) in first" :key="item.id" class="list">
<p :class="{'cat_name': index == 0}" >{{item.name}}</p>
</div>
</div>
Way 2- Use :first-child rule
:first-child CSS rule will also work but remember "The :first-child CSS pseudo-class represents the first element among a group of sibling elements." That means all p elements should look like this-
<p>Hello</p>
<p>Bye</p>
<p>Nice to meet you.</p>
But if resolve your current loop HTML, it will look like this-
<div>
<p>Hello</p>
</div>
<div>
<p>Bye</p>
</div>
<div>
<p>Nice to meet you</p>
</div>
Where all p elements are wrapped inside an individual div element which makes them no longer siblings to each other and that's why the :first-child CSS rule will apply to all p elements because every p element is the first child of its parent (div) element.
So, if you want to go this way then loop directly on p elements.
Working demo-
new Vue({
el: "#app",
data() {
return {
first: [
{
id: 1,
name: "Hello"
},
{
id: 2,
name: "Bye"
},
{
id: 3,
name: "Nice to meet you"
}
]
}
}
})
.list p:first-child {
font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="list">
<div>
<p v-for="(item,index) in first" :key="item.id">{{item.name}}</p>
</div>
</div>
</div>
Note-
If looping directly on p elements is not possible then way 1 is recommended.
Try this
<div v-for="item in first" :key="item.id" class="list">
<p class="cat_name" >{{item.name}}</p>
</div>
<style scoped>
.list p:first-child
{
font-weight: bold;
}
</style>

How to apply a class to a specific label in a row based on truthy value

I'm new to Vue 3. I have a table with rows. See screenshot below. Each row has a checkbox and a label in it. All I want to do is upon initial loading of the page view, have the correct class apply to the label depending on whether the child.status of that row is true-thy or not. Child status true is present. False = absent. Problem: when I initially load the page view all the labels are green regardless whether the child.status false or true.
Currently the Vue 3 code I have got so far works great, except for initial load. If I check the checkbox, I get the data I need and the label gets its green background. If I uncheck a checkbox, the green class disappears. Check it again and the green background reappears - Great. All's well except for initial page load where all the labels are green.
To help you understand I included a screenshot. You will notice behind each label there is a 1 or 0 to indicate whether it is truthy are not. You will see that even the false ones are green.
<template>
<div class="container">
<h5 class="heading ml-4">Children Absent / Present</h5>
<div class="border_charts">
<table class="table ">
<thead>
<tr class="col-4">
<th class="tableHeading col-4">First Name</th>
<th class="tableHeading col-4">Last Name</th>
<th class="tableHeading col-4 ">Absent / Present</th>
</tr>
</thead>
<tbody>
<tr v-for="child in Children" :key="child.child_id">
<td class="col-4"><a :href="'/getchild/'+ child.child_id">{{child.childFirstName}}</a></td>
<td class="col-4"> {{child.childLastName}}</td>
<td class="col-4">
<div class="form-check form-switch" >
<input
type="checkbox"
class="form-check-input"
role="switch"
:id="child.child_id"
v-model="child.status"
:true-value="1"
:false-value="0"
#change="updateChild(child)"
>
<label
class="form-check-label"
v-bind:for="child.child_id "
:class="{present:child.status}"
:true-value="1"
:false-value="0"
> {{ child.status ? absentPresent : absentPresent }}
</label>
{{child.status}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
props: {
Children:{
Children:Array
},
},
data() {
return {
active:false,
inputClass: 'present',
absentPresent:'Absent/Present'
}
},
methods:{
updateChild(child){
this.changedChild = child;
console.log( this.changedChild);
this.inputClass = 'present'
this.active = ! this.active
},
},
}
</script>
<style scoped>
.present{
background-color: #5ba17a;
border-radius:0.25rem;
width:auto;
color:white;
text-align: center;
margin: 2px;
padding: 0px 5px 0px 5px;
}
.absent{
width:auto;
color:darkblue;
text-align: center;}
</style>
My guess is that child.status is initially loaded as string, so it is '0' instead of 0, and as with any non-empty string, Boolean('0') is true.
Check if you need to convert the values to numbers after loading, or if you can change the data source to send you numbers instead of strings.

How can I construct a single string from JSON Array using ngFor in Angular

I have the following data:
[
{
"name": "AAA",
"desc": "XXXXXXXX"
},
{
"name": "BBB",
"desc": "YYYYYYY"
},
...
]
I am trying to display on the web as AAA, BBB, ... with the following code:
<mat-grid-tile colspan="3">
<div class="text-grid" *ngFor="let value of data; let f=first">
<div *ngIf="f">
{{value.name}}
</div>
<div *ngIf="!f">
, {{value.name}}
</div>
</div>
</mat-grid-tile>
But the output result is overlapped. How can I achieve my expected result?
Edited
After checking the CSS, it was a issue from CSS:
.text-grid {
position: absolute;
left: 5px;
color: royalblue;
}
I tried to use either text-align: left (which does not work within Angular Material grid) or width: 100%(which result in a large gap between the items).
Any suggestion on how to tweak the CSS?
Probably you need to wrap your content within mat-grid-list , like below -
<mat-grid-list cols="1">
<mat-grid-tile>
<div class="text-grid" *ngFor="let value of data; let last=last">
{{value.name}} <span *ngIf='!last'>,</span>
</div>
</mat-grid-tile>
</mat-grid-list>
Working Stackblitz
Extra Tip: No need to add two *ngIf, you can check for last index in the *ngFor as above.
Wrap mat-grid-list around mat-grid-tile and you need to use <span> instead of <div> as div is block element. see below
<mat-grid-list cols="1">
<mat-grid-tile>
<div class="text-grid" *ngFor="let value of data; let f = first">
<span *ngIf="f">
{{value.name}}
</span>
<span *ngIf="!f">
, {{value.name}}
</span>
</div>
</mat-grid-tile>
</mat-grid-list>
Maybe try to format your data before putting it in the template
component.ts
formattedDate: string = '';
data: any = [
{
"name": "AAA",
"desc": "XXXXXXXX"
},
{
"name": "BBB",
"desc": "YYYYYYY"
}
];
formatDate() {
this.formattedDate = this.data.map(el => el.name).join(', '); // return 'AAA, BBB'
}

How to show a table on hovering a button in react?

I tried
class MyColorPicker extends React.Component {
render() {
const darkColors = ['#B80000', '#DB3E00', '#FCCB00', '#008B02', '#006B76', '#1273DE', '#004DCF', '#5300EB'];
const lightColors = ['#EB9694', '#FAD0C3', '#FEF3BD', '#C1E1C5', '#BEDADC', '#C4DEF6', '#BED3F3', '#D4C4FB'];
return (<div>
<button id="color">Select color</button>
<div className="picker">Text</div>
<div className="picker">
<table>
<tbody>
<tr>
{darkColors.map(color =>
<td style={{backgroundColor: color, width: 20, height: 20}} key={color}>{" "}</td>)}
</tr>
<tr>
{lightColors.map(color =>
<td style={{backgroundColor: color, width: 20, height: 20}} key={color}>{" "}</td>)}
</tr>
</tbody>
</table>
</div>
</div>);
}
}
css
.picker{
display: none;
}
#color:hover + .picker{
display: block !important;
}
The table does not show. I also tried adding a className to GithubPicker from react-color. Didn't work.
I don't want to set a state on hover because I have many components where I want to attach a button and a color picker. In this case, on hovering one button, all the other color pickers will show. I just want one color picker to show. How can I achieve this?
#color:hover + .picker work for element with class .picker just after #color:hover. In your case you have two .picker element. So first .picker will shown but as css second element will not show.
Please wrap both .picker div into a one div and put .picker class on wrapped div element.
Still you will get one more issue. As css you are trying to put hover css for button. When your mouse will move to picker, button hover will clear and again .picker will disappear.
So we have to wrap all element in one div and put hover css for wrapped div.
JSX
<div>
<div className="picker">
<button id="color">Select color</button>
<div className="picker-colors">
<div>Text</div>
<div>
<table>
<tbody>
<tr>
{darkColors.map(color => (
<td
style={{
backgroundColor: color,
width: 20,
height: 20
}}
key={color}
>
{" "}
</td>
))}
</tr>
<tr>
{lightColors.map(color => (
<td
style={{
backgroundColor: color,
width: 20,
height: 20
}}
key={color}
>
{" "}
</td>
))}
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
CSS
.picker-colors {
display: none;
}
.picker:hover .picker-colors {
display: block;
}
Here's a working example https://codesandbox.io/s/9yrr9wx60r

Hide table row when cell is empty

I'm having an issue where I can't seem to find an answer to, but I can't imagine it's not possible.
I have a table with two columns: the left column contains a label, the right side contains a value. However, the value can be empty. The label is fixed text.
What I want is to hide the entire row if the right cell of the row (the value) is empty.
For example:
<table>
<tr>
<td class="label">number of users:</td>
<td class="value">8</td>
</tr>
<tr>
<td class="label">total number of people:</td>
<td class="value"></td>
</tr>
</table>
Since the last row does not contain a value, I want the entire row to be hidden.
I can hide the cell using td:empty, but that's not enough. I tried to work around this by setting the height of the row to 0px and make it expand when the 'value'-cell is shown, but I can't get that to work either since the label cell already expands the row.
Anyone knows how I can tackle this problem using just HTML/CSS?
There's no parent selector in css, so you can't do this with css.
You may use jQuery:
$('td').each(function(){
if($(this).is(:empty)){
$(this).closest('tr').hide();
}
});
Or in shorter form,
$('tr:has("td:empty")').hide();
See the docs: :empty, :has,closest and each
While JavaScript is necessary to solve this problem, jQuery is, by no means, a requirement. Using the DOM, one can achieve this with the following:
function hideParentsOf(cssSelector) {
var elems = document.querySelectorAll(cssSelector);
if (elems.length) {
Array.prototype.forEach.call(elems, function (el) {
el.parentNode.style.display = 'none';
});
}
}
hideParentsOf('td:empty');
function hideParentsOf(cssSelector) {
// cssSelector: String,
// a string representing a CSS selector,
// such as 'td:empty' in this case.
// retrieving a NodeList of elements matching the supplied selector:
var elems = document.querySelectorAll(cssSelector);
// if any elements were found:
if (elems.length) {
// iterating over the array-like NodeList with Array.forEach():
Array.prototype.forEach.call(elems, function(el) {
// el is the current array-element (or NodeList-element in
// this instance).
// here we find the parentNode, and set its 'display' to 'none':
el.parentNode.style.display = 'none';
});
}
}
hideParentsOf('td:empty');
<table>
<tr>
<td class="label">number of users:</td>
<td class="value">8</td>
</tr>
<tr>
<td class="label">total number of people:</td>
<td class="value"></td>
</tr>
</table>
References:
CSS:
:empty pseudo-class.
JavaScript:
Array.prototype.forEach().
document.querySelectorAll().
Function.prototype.call().
Node.parentNode.
HTMLElement.style.
An HTML/CSS solution exists if you don't mind throwing out <table> <tr> and <td>. You can get the same end result with CSS - including still rendering like a table:
CSS:
/* hide if empty */
.hideIfEmpty:empty { display: none; }
/* label style */
.hideIfEmpty::before { font-weight:bold; }
/* labels */
.l_numberofusers::before { content:"Number of users: "; }
.l_numberofpeople::before { content: "Number of people:"; }
.l_numberofdogs::before { content: "Number of dogs:" }
/* table like rows/cells */
.table { display: table; }
.row { display: table-row; }
.cell { display: table-cell; }
HTML
<!-- if the div.hideIfEmpty is empty, it'll be hidden;
labels come from CSS -->
<div class="table">
<div class="row hideIfEmpty l_numberofusers"><span class="cell">8</span></div>
<div class="row hideIfEmpty l_numberofpeople"><span class="cell">12</span></div>
<div class="row hideIfEmpty l_numberofdogs"></div>
</div>
The caveat is that your <div> has to be empty to hide the row, and values in the <div> must have a class .cell applied to them.
Result:
Number of users: 8
Number of people: 12
This will make your CSS very long if you have many labels/rows since you have to have one rule for every row to populate the label.

Resources