How do I conditionally apply CSS styles in AngularJS? - css

Q1. Suppose I want to alter the look of each "item" that a user marks for deletion before the main "delete" button is pressed. (This immediate visual feedback should eliminate the need for the proverbial "are you sure?" dialog box.) The user will check checkboxes to indicate which items should be deleted. If a checkbox is unchecked, that item should revert back to its normal look.
What's the best way to apply or remove the CSS styling?
Q2. Suppose I want to allow each user to personalize how my site is presented. E.g., select from a fixed set of font sizes, allow user-definable foreground and background colors, etc.
What's the best way to apply the CSS styling the user selects/inputs?

Angular provides a number of built-in directives for manipulating CSS styling conditionally/dynamically:
ng-class - use when the set of CSS styles is static/known ahead of time
ng-style - use when you can't define a CSS class because the style values may change dynamically. Think programmable control of the style values.
ng-show and ng-hide - use if you only need to show or hide something (modifies CSS)
ng-if - new in version 1.1.5, use instead of the more verbose ng-switch if you only need to check for a single condition (modifies DOM)
ng-switch - use instead of using several mutually exclusive ng-shows (modifies DOM)
ng-disabled and ng-readonly - use to restrict form element behavior
ng-animate - new in version 1.1.4, use to add CSS3 transitions/animations
The normal "Angular way" involves tying a model/scope property to a UI element that will accept user input/manipulation (i.e., use ng-model), and then associating that model property to one of the built-in directives mentioned above.
When the user changes the UI, Angular will automatically update the associated elements on the page.
Q1 sounds like a good case for ng-class -- the CSS styling can be captured in a class.
ng-class accepts an "expression" that must evaluate to one of the following:
a string of space-delimited class names
an array of class names
a map/object of class names to boolean values
Assuming your items are displayed using ng-repeat over some array model, and that when the checkbox for an item is checked you want to apply the pending-delete class:
<div ng-repeat="item in items" ng-class="{'pending-delete': item.checked}">
... HTML to display the item ...
<input type="checkbox" ng-model="item.checked">
</div>
Above, we used ng-class expression type #3 - a map/object of class names to boolean values.
Q2 sounds like a good case for ng-style -- the CSS styling is dynamic, so we can't define a class for this.
ng-style accepts an "expression" that must evaluate to:
an map/object of CSS style names to CSS values
For a contrived example, suppose the user can type in a color name into a texbox for the background color (a jQuery color picker would be much nicer):
<div class="main-body" ng-style="{color: myColor}">
...
<input type="text" ng-model="myColor" placeholder="enter a color name">
Fiddle for both of the above.
The fiddle also contains an example of ng-show and ng-hide. If a checkbox is checked, in addition to the background-color turning pink, some text is shown. If 'red' is entered in the textbox, a div becomes hidden.

I have found problems when applying classes inside table elements when I had one class already applied to the whole table (for example, a color applied to the odd rows <myClass tbody tr:nth-child(even) td>). It seems that when you inspect the element with Developer Tools, the element.style has no style assigned. So instead of using ng-class, I have tried using ng-style, and in this case, the new CSS attribute does appear inside element.style. This code works great for me:
<tr ng-repeat="element in collection">
[...amazing code...]
<td ng-style="myvar === 0 && {'background-color': 'red'} ||
myvar === 1 && {'background-color': 'green'} ||
myvar === 2 && {'background-color': 'yellow'}">{{ myvar }}</td>
[...more amazing code...]
</tr>
Myvar is what I am evaluating, and in each case I apply a style to each <td> depending on myvar value, that overwrites the current style applied by the CSS class for the whole table.
UPDATE
If you want to apply a class to the table for example, when visiting a page or in other cases, you can use this structure:
<li ng-class="{ active: isActive('/route_a') || isActive('/route_b')}">
Basically, what we need to activate a ng-class is the class to apply and a true or false statement. True applies the class and false doesn't. So here we have two checks of the route of the page and an OR between them, so if we are in /route_a OR we are in route_b, the active class will be applied.
This works just having a logic function on the right that returns true or false.
So in the first example, ng-style is conditioned by three statements. If all of them are false, no style is applied, but following our logic, at least one is going to be applied, so, the logic expression will check which variable comparison is true and because a non empty array is always true, that will left an array as return and with only one true, considering we are using OR for the whole response, the style remaining will be applied.
By the way, I forgot to give you the function isActive():
$rootScope.isActive = function(viewLocation) {
return viewLocation === $location.path();
};
NEW UPDATE
Here you have something I find really useful. When you need to apply a class depending on the value of a variable, for example, an icon depending on the contents of the div, you can use the following code (very useful in ng-repeat):
<i class="fa" ng-class="{ 'fa-github' : type === 0,
'fa-linkedin' : type === 1,
'fa-skype' : type === 2,
'fa-google' : type === 3 }"></i>
Icons from Font Awesome

This works well when ng-class can't be used (for example when styling SVG):
ng-attr-class="{{someBoolean && 'class-when-true' || 'class-when-false' }}"
(I think you need to be on latest unstable Angular to use ng-attr-, I'm currently on 1.1.4)
I have published an article on working with AngularJS+SVG. It talks about this issue and numerous others. http://www.codeproject.com/Articles/709340/Implementing-a-Flowchart-with-SVG-and-AngularJS

span class="circle circle-{{selectcss(document.Extension)}}">
and code
$scope.selectcss = function (data) {
if (data == '.pdf')
return 'circle circle-pdf';
else
return 'circle circle-small';
};
css
.circle-pdf {
width: 24px;
height: 24px;
font-size: 16px;
font-weight: 700;
padding-top: 3px;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
background-image: url(images/pdf_icon32.png);
}

This solution did the trick for me
<a ng-style="{true: {paddingLeft: '25px'}, false: {}}[deleteTriggered]">...</a>

You can use ternary expression. There are two ways to do this:
<div ng-style="myVariable > 100 ? {'color': 'red'} : {'color': 'blue'}"></div>
or...
<div ng-style="{'color': (myVariable > 100) ? 'red' : 'blue' }"></div>

Another option when you need a simple css style of one or two properties:
View:
<tr ng-repeat="element in collection">
[...amazing code...]
<td ng-style="{'background-color': getTrColor(element.myvar)}">
{{ element.myvar }}
</td>
[...more amazing code...]
</tr>
Controller:
$scope.getTrColor = function (colorIndex) {
switch(colorIndex){
case 0: return 'red';
case 1: return 'green';
default: return 'yellow';
}
};

See the following example
<!DOCTYPE html>
<html ng-app>
<head>
<title>Demo Changing CSS Classes Conditionally with Angular</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="res/js/controllers.js"></script>
<style>
.checkboxList {
border:1px solid #000;
background-color:#fff;
color:#000;
width:300px;
height: 100px;
overflow-y: scroll;
}
.uncheckedClass {
background-color:#eeeeee;
color:black;
}
.checkedClass {
background-color:#3ab44a;
color:white;
}
</style>
</head>
<body ng-controller="TeamListCtrl">
<b>Teams</b>
<div id="teamCheckboxList" class="checkboxList">
<div class="uncheckedClass" ng-repeat="team in teams" ng-class="{'checkedClass': team.isChecked, 'uncheckedClass': !team.isChecked}">
<label>
<input type="checkbox" ng-model="team.isChecked" />
<span>{{team.name}}</span>
</label>
</div>
</div>
</body>
</html>

As of AngularJS v1.2.0rc, ng-class and even ng-attr-class fail with SVG elements (They did work earlier, even with normal binding inside the class attribute)
Specifically, none of these work now:
ng-class="current==this_element?'active':' ' "
ng-attr-class="{{current==this_element?'active':' '}}"
class="class1 class2 .... {{current==this_element?'active':''}}"
As a workaround, I've to use
ng-attr-otherAttr="{{current==this_element?'active':''}}"
and then style using
[otherAttr='active'] {
... styles ...
}

One more (in the future) way to conditionally apply style is by conditionally creating scoped style
<style scoped type="text/css" ng-if="...">
</style>
But nowadays only FireFox supports scoped styles.

There is one more option that I recently discovered that some people may find useful because it allows you to change a CSS rule within a style element - thus avoiding the need for repeated use of an angular directive such as ng-style, ng-class, ng-show, ng-hide, ng-animate, and others.
This option makes use of a service with service variables which are set by a controller and watched by an attribute-directive I call "custom-style". This strategy could be used in many different ways, and I attempted to provide some general guidance with this fiddle.
var app = angular.module('myApp', ['ui.bootstrap']);
app.service('MainService', function(){
var vm = this;
});
app.controller('MainCtrl', function(MainService){
var vm = this;
vm.ms = MainService;
});
app.directive('customStyle', function(MainService){
return {
restrict : 'A',
link : function(scope, element, attr){
var style = angular.element('<style></style>');
element.append(style);
scope.$watch(function(){ return MainService.theme; },
function(){
var css = '';
angular.forEach(MainService.theme, function(selector, key){
angular.forEach(MainService.theme[key], function(val, k){
css += key + ' { '+k+' : '+val+'} ';
});
});
style.html(css);
}, true);
}
};
});

well i would suggest you to check condition in your controller with a function returning true or false .
<div class="week-wrap" ng-class="{today: getTodayForHighLight(todayDate, day.date)}">{{day.date}}</div>
and in your controller check the condition
$scope.getTodayForHighLight = function(today, date){
return (today == date);
}

One thing to watch is - if the CSS style has dashes - you must remove them. So if you want to set background-color, the correct way is:
ng-style="{backgroundColor:myColor}"

Here's how i conditionally applied gray text style on a disabled button
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
styleUrls: [ './app.component.css' ],
template: `
<button
(click)='buttonClick1()'
[disabled] = "btnDisabled"
[ngStyle]="{'color': (btnDisabled)? 'gray': 'black'}">
{{btnText}}
</button>`
})
export class AppComponent {
name = 'Angular';
btnText = 'Click me';
btnDisabled = false;
buttonClick1() {
this.btnDisabled = true;
this.btnText = 'you clicked me';
setTimeout(() => {
this.btnText = 'click me again';
this.btnDisabled = false
}, 5000);
}
}
Here's a working example:
https://stackblitz.com/edit/example-conditional-disable-button?file=src%2Fapp%2Fapp.component.html

Related

What CSS should I write in html template to generate a pdf of a particular height & width

I am generating a PDF using nodejs with pdf-creator-node and I got success.
My requirement is I need to generate a PDF with Height X Width = 926px X 1296px.
I don' know what css I should write to generate this dimension pdf.
right now if I set div or body height and widht with above mentioned dimension I am getting 3 pages
this is what I tried
#page {
width: 1296px;
height: 926px;
}
<div
class="parent-div"
style="
width: 1296px;
height: 926px;
background-color: #faf0e6;
border: 1px solid red;
"
></div>
jsPDF is able to use plugins. In order to enable it to print HTML, you have to include certain plugins and therefore have to do the following:
Go to https://github.com/MrRio/jsPDF and download the latest
Version.
Include the following Scripts in your project:
jspdf.js
jspdf.plugin.from_html.js
jspdf.plugin.split_text_to_size.js
jspdf.plugin.standard_fonts_metrics.js
If you want to ignore certain elements, you have to mark them with an ID, which you can then ignore in a special element handler of jsPDF. Therefore your HTML should look like this:
<!DOCTYPE html>
<html>
<body>
<p id="ignorePDF">don't print this to pdf</p>
<div>
<p><font size="3" color="red">print this to pdf</font></p>
</div>
</body>
</html>
Then you use the following JavaScript code to open the created PDF in a PopUp:
var doc = new jsPDF();
var elementHandler = {
#ignorePDF': function (element, renderer) {
return true;
}
};
var source = window.document.getElementsByTagName("body")[0];
doc.fromHTML(
source,
15,
15,
{
'width': 180,'elementHandlers': elementHandler
});
doc.output("dataurlnewwindow");
**For me this created a nice and tidy PDF that only included the line 'print this to pdf'.
Please note that the special element handlers only deal with IDs in the current version, which is also stated in a GitHub Issue. It states:**
Because the matching is done against every element in the node tree, my desire was to make it as fast as possible. In that case, it meant "Only element IDs are matched" The element IDs are still done in jQuery style "#id", but it does not mean that all jQuery selectors are supported.
Therefore replacing '#ignorePDF' with class selectors like '.ignorePDF' did not work for me. Instead you will have to add the same handler for each and every element, which you want to ignore like:
var elementHandler = {
#ignoreElement': function (element, renderer) {
return true;
},
#anotherIdToBeIgnored': function (element, renderer) {
return true;
}
};
From the examples it is also stated that it is possible to select tags like 'a' or 'li'. That might be a little bit too unrestrictive for the most use cases though:
We support special element handlers. Register them with a jQuery-style ID selector for either ID or node name. ("#iAmID", "div", "span" etc.) There is no support for any other type of selectors (class, of the compound) at this time.
One very important thing to add is that you lose all your style information (CSS). Luckily jsPDF is able to nicely format h1, h2, h3, etc., which was enough for my purposes. Additionally, it will only print text within text nodes, which means that it will not print the values of textareas and the like. Example:
<body>
<ul>
<!-- This is printed as the element contains a textnode -->
<li>Print me!</li>
</ul>
<div>
<!-- This is not printed because jsPDF doesn't deal with the value attribute -->
<input type="textarea" value="Please print me, too!">
</div>
</body>

Vuejs: How to bind a variable to global css class?

I would like to bind a variable as it:
<style lang='sass'>
.picker-item.picker-item-selected
background-color: {{ MY_BG_COLOR }}
</style>
this obviously does not work.
For the context I'm using a picker from framework7 which I want to override the background-color depending of user actions.
Then I cannot really use usual ways to bind values for styling
Is there a way to bind a value for global css class?
You can't do that you want directly, but you can Use "inline style":
You must to bind an inline style with a variable
<div id="app">
<div :style="divInline">Hello</div>
</div>
You must create the variable in the data section
data: {
divInline: 'background-color:red;'
}
after that, you can change the inline style through methods.
methods: {
changeColor( color ) {
this.divInline = 'background-color:' + color + ';'
}
}
Example complete

User Inputted live Color Scheme Change

I designed a site so that changing two user inputted colors should change the color scheme of the entire site.
What is the best way to accomplish this. I know that I would have to save the items in the database and pull every time the user logged in in order to implement the color scheme with every login.
But at the moment I am more worried about a live change as soon as the user changes the html color value.
I know of an option to where I add a CSS class to every component that would change such as ... .primaryColor and .secondaryColor. And then alter all of the elements with that class. Is there a better way with React or another CSS/Javascript solution?
Also another complication is that it would have to be in a way that when the user loads other components that have not rendered yet, the change is still in affect.
One possible solution is to use the <style> element coupled with dangerouslySetInnerHTML, like this. (Notice the backticks ` around the CSS - it's an interpolated string literal.)
const Theme = props => {
<style dangerouslySetInnerHTML={{__html: `
.styled { color: ${props.userColor} }
`}}
/>
}
Then a component that used the theme would simply be <div className="styled" />
I got the idea for this solution here.
If you use this method, be very careful you're using sanitized variables to create your CSS theme. Otherwise, there's potential problems with injection attacks.
I would use an event listener on the input, read the value, and if it matches whatever you want to trigger the color scheme change, apply the theme value to a data attribute on a root element and use CSS to control the color schemes.
var input = document.getElementById('input'),
body = document.getElementsByTagName('body')[0];
input.addEventListener('keyup',function() {
var val = this.value;
if (this.value == 'foo') {
body.setAttribute('data-theme','secondary');
} else if (this.value == 'bar') {
body.setAttribute('data-theme','primary');
} else {
body.setAttribute('data-theme','');
}
// ajax request to save theme pref in db
})
/* defaults */
body {
color: #333;
}
/* primary theme */
[data-theme="primary"] {
color: red;
}
[data-theme="primary"] p {
background: yellow;
}
/* secondary theme */
[data-theme="secondary"] {
color: blue;
}
[data-theme="secondary"] ul {
background: grey;
}
<input id="input" placeholder="enter 'foo' or 'bar'">
<p>
paragraph
</p>
<ul>
<li>list</li>
</ul>
you can easily do this using js.
just add your class .primaryColor, .secondaryColor with jQuery addClass() Method.
select the element
example :
$(selector).addClass(classname,function(index,currentclass))
more example :https://www.w3schools.com/jquery/tryit.asp?filename=tryjquery_html_addclass

dynamic stylesheet with angularjs

I have and angularjs application that fetches data via api, and builds a webpage with it.
Usually I use ng-style to create dynamic styling, but now I have to use the nth-of-type attribute that can only be used in a css stylesheet (I cannot use individual styling since the number and order of elements always change).
I have tried this naive code (in the html page):
<style ng-if="styles.sc && styles.sc.length==3">
a.mosection:nth-of-type(3n) > div {
background-color: {{styles.sc[0]}} !important;
}
a.mosection:nth-of-type(3n+1) > div {
background-color: {{styles.sc[1]}} !important;
}
a.mosection:nth-of-type(3n+2) > div {
background-color: {{styles.sc[2]}} !important;
}
</style>
But it didn't work... Apparently angular doesn't bind the data inside the style tag (the ng-if attribute does get digested properly)
Does anyone have any idea how this can be done?
Thanks!
You should checkout those three ng-*
https://docs.angularjs.org/api/ng/directive/ngClass
https://docs.angularjs.org/api/ng/directive/ngClassOdd
https://docs.angularjs.org/api/ng/directive/ngClassEven
all of them can accept functions as attributes, you can also checkout
https://docs.angularjs.org/api/ng/directive/ngStyle
which might be actually the best in your case
Thanks!
I indeed solved it by using ng-style with a function
The HTML
<div class="widget widget-people" ng-style="{backgroundColor: staggerBgColors('widget', 'widget-people', '#333333')}"></div>
<div class="widget widget-property" ng-style="{backgroundColor: staggerBgColors('widget', 'widget-property', '#24d10f')}"></div>
The scope function
$scope.staggerBgColors = function(elesClass, eleClass, defaultColor){
if (!$scope.styles || !$scope.styles.sc || $scope.styles.sc.length!=3){
return defaultColor;
}else{
var listItem = $('.'+eleClass);
var n = $('.'+elesClass).index( listItem ) % 3;
return '#' + $scope.preview.moment.sc[n];
}
}
I had to implement the same functionality of the css property "nth-of-type" using jQuery, but it works prefectly!

Toggling css in knockout.js for changing select list width

I am using Knockout.js with jQuery tmpl plugin. The template I have defined has a few select lists. I need to expand the width of the select items (on IE 8) when a select list is clicked (to accomodate the longest element in the list). I was thinking of toggling the css class when a user clicks on the select list to achieve this but am not sure how to go about it. Here is what I have so far:
//CSS classes
<style>
.bigclass
{
width: 200px;
}
.normalclass
{
width: auto;
}
</style>
// Call makeBig javascript method on mouseover.
<script id='criteriaRowTemplate' type='text/html'>
<tr>
<td style="width: 23%"><select style="width: 100%" data-bind='event: { mouseover: makeBig, mouseout: makeNormal}, options: criteriaData, optionsText: "Text", optionsCaption: "--Select--", value: SearchCriterion' /></td>
</tr>
</script>
var CriteriaLine = function() {
this.SearchCriterion = ko.observable();
//Control comes to this method. Not sure though if the element captured is correct.
makeBig = function(element) {
$(element).addClass("bigclass")
};
makeNormal = function(element) {
$(element).addClass("normalclass")
};
};
So my questions are:
How do we pass the select list to the makeBig javascript function? I believe I need to change the following syntax in my code:
data-bind='event: { mouseover: makeBig, mouseout: makeNormal
How do we add the class to the passed select list. I have added the code but it's not working, maybe because element doesn't have a value.
Alternatively, is there any other approach to ensure that the user sees the full text of the dropdown in IE 8?
Here is a custom binding to add a CSS class to the element on hovering:
http://jsfiddle.net/BL9zt/
Note that it subscribes to the IE specific mouseenter and mouseleave event, so you also have to reference jQuery which simulates those events in the other browsers.
Another, knockout-independent approach is described here:
http://www.getharvest.com/blog/wp-content/uploads/2009/12/select_box_demo.html

Resources