Recursive iteration over an object in Jade template? - recursion

I have an object of mixed type properties - some strings, some arrays of strings, some objects containing arrays of strings - that can potentially go many levels deep.
I would like to iterate over all properties so that an object creates a div, an array creates a div, and a string property creates a span to contain the text.
{ "string" : "some text", "object" : { "array" : [ "text" ] } }
The above object would render as:
<span>some text</span>
<div>
<div>
<span>text</span>
</div>
</div>
But usually much more complex structures. How should I go about accomplishing this is Jade?

It's been a while since you asked, but mixin is your friend, I think. I haven't tried it out, but if mixins support recursion, this should work:
mixin parseObject(obj)
div
- each val, key in obj
- if (typeof val === 'string')
span #{val}
- else if (typeof val === 'object')
mixin parseObject(val)
Then in the body of your .jade file, call mixin parseObject(rootObject).

Recursion seems to be suppported now. I have successfully used the function with a minor tweak; you need to use the mixin keyword when calling the function.
mixin parseObject(obj)
div
each val, key in obj
if typeof val === 'string'
span #{val}
else if typeof val === 'object'
mixin parseObject(val)

In the modern version of Jade it's look like
mixin parseObject( obj )
div
each val in obj
if typeof val === 'string'
span= val
else if typeof val === 'object'
+parseObject( val )
Then in the body of your .jade file, call
+parseObject( rootObject )

Related

Template literal not working correctly with Tailwind CSS

I am passing in a Hex Colour into a prop and attempting to set the background of an element with it. Here is my code:
let cardColourRGB: string;
if (cardColour) {
cardColourRGB = "[" + cardColour + "]";
console.log(cardColourRGB);
} else {
cardColourRGB = "white/50"
}
In the return function:
<div className={`bg-${cardColourRGB}`}></div>
Passing in some colours work, but others don't. For example, passing in #AAA32E as the prop does not set the colour, but setting the colour directly works:
<div className={`bg-[#AAA32E]`}></div>
Why could this be?
According to the official documentation of the tailwind.css it is not preferred to have such classNames
As document says:
The most important implication of how Tailwind extracts class names is that it will only find classes that exist as complete unbroken strings in your source files.
If you use string interpolation or concatenate partial class names together, Tailwind will not find them and therefore will not generate the corresponding CSS:
Don't construct class names dynamically
<div class="text-{{ error ? 'red' : 'green' }}-600"></div>
Instead, make sure any class names you’re using exist in full
<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>
So , for your case, use the following code:
<div class="{{ cardColour ? 'bg-[#AAA32E]' : 'bg-white-50' }}"></div>
Hope it helps!
TailwindCSS doesn't allow you to generate classes dynamically. So when you use the following to generate the class…
`bg-${cardColourRGB}`
…TailwindCSS will not pick that up as a valid TailwindCSS class and therefore will not produce the necessary CSS.
Instead, you must include the full name of the class in your source code. You can return the full value like this
let cardColourRGB: string;
if (cardColour) {
cardColourRGB = "bg-[" + cardColour + "]";
console.log(cardColourRGB);
} else {
cardColourRGB = "bg-white/50"
}
where cardColourRGB is your value you are passing .
By doing it this way, the entire string for every class is in your source code, so TailwindCSS will know to generate the applicable CSS.
Read more: https://tailwindcss.com/docs/content-configuration#class-detection-in-depth

Cypress testing pseudo CSS class :before

Is there a way in which I can test the content of the pseudo CSS class for :before on my element with Cypress?
I have seen links documenting:
Accessing nth-child pseudo element
Accessing the actual content pseudo class of a normal CSS class
But I have not found anything for CSS classes using the ::before pseudo class.
Imagine the code:
.myClass:before {
content: "foo-";
}
<div>
<span class="myClass">Bar</span>
</div>
How could one test that 'foo-' is present?
There's a way to assert on the CSS properties of pseudo-elements, although it's not as simple as just using a Cypress command.
Use cy.get() to get a reference to the element.
Read the Window object off of the element, and then invoke Window.getComputedStyle(), which can read the computed CSS of pseudo selectors.
Use getPropertyValue on the returned CSS declaration to read the value of the content property.
Assert on it.
Here's an example that works on the code posted in the OP:
cy.get('.myClass')
.then($els => {
// get Window reference from element
const win = $els[0].ownerDocument.defaultView
// use getComputedStyle to read the pseudo selector
const before = win.getComputedStyle($els[0], 'before')
// read the value of the `content` CSS property
const contentValue = before.getPropertyValue('content')
// the returned value will have double quotes around it, but this is correct
expect(contentValue).to.eq('"foo-"')
})
Based on Zach's answer I created a command that returns the pseudo-element property (without single quotes around).
function unquote(str) {
return str.replace(/(^")|("$)/g, '');
}
Cypress.Commands.add(
'before',
{
prevSubject: 'element',
},
(el, property) => {
const win = el[0].ownerDocument.defaultView;
const before = win.getComputedStyle(el[0], 'before');
return unquote(before.getPropertyValue(property));
},
);
You will use it like this
it('color is black', () => {
cy.get('button')
.before('color')
.should('eq', 'rgb(0,0,0)'); // Or .then()
});
Try asserting on the text of the parent:
cy.get('.myClass').parent().should('have.text', 'foo-bar')
If that doesn't work, you may have to use the textContent property:
cy.get('.myClass').parent(). should($el => expect ($el).to.contain('foo-bar')
)
This was my solution to get, convert and compare a hexadecimal's background-color with a rgb returned.
const RGBToHex = (rgbColor) => {
// it parse rgb(255, 13, 200) to #fa92D4
const [red, green, blue] = rgbColor.replace(/[a-z]|\(|\)|\s/g, '').split(',');
let r = parseInt(red, 10).toString(16);
let g = parseInt(green, 10).toString(16);
let b = parseInt(blue, 10).toString(16);
if (r.length === 1) r = `0${r}`;
if (g.length === 1) g = `0${g}`;
if (b.length === 1) b = `0${b}`;
return `#${r}${g}${b}`;
};
cy.get('.element').then(($el) => {
const win = $el[0].ownerDocument.defaultView;
const before = win.getComputedStyle($el[0], 'before');
const bgColor = before.getPropertyValue('background-color');
expect(RGBToHex(bgColor)).to.eq('#HEXA');
});

How to use urls in React CSS inline styling without `$`?

Learning React and trying to cheat off this codepen. I do not understand 2 things.
What is the ... before largebox, flex, and other css classes?
return <div style={{...largebox, ...flex}} key={props.id}
What does the $ do in the css url param? Is it jQuery?
`url(${props.photo})
const FormCard = (props) => (
<div>
{
DATA.map((props) => {
return <div style={{...largebox, ...flex}} key={props.id}>
<div style={{...Photo,backgroundImage: `url(${props.photo})`}}></div>
<div>
<Author author={props.author}/>
<Something bio={props.bio}/>
<AdBox adpic={props.adpic} />
<IconBox />
</div>
</div>
})
}
</div>
)
The three dots '...' are called spread operator, see here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
The $ sign is no Jquery but is actually referencing template literals: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
Hopefully, the docs are enough as I currently can't find [the link] to [that tutorial] I remember...
Spread operator:
const styleA = { color: 'red' };
const styleB = { background: 'blue' };
const styleC = { ...styleA, ...styleB };
// styleC = {color: "red", background: "blue"}
String Template:
const user = 'Bob';
const greetings = `Hello ${user}`;
// greetings = 'Hello Bob'
for your first question we call it Spread Operator in a simple description for below line :
style={{...largebox, ...flex}}
this is mean copy all property of largebox and flex object into a new object and assing it to style.or this line means :
style={{...Photo,backgroundImage:"myurl}"}
create a new object for me with all property of Photo object and also add a property with name backgroundImage to it. so if Photo is equal to {name:'1.jpg'} the new object is equal to
{name:'1.jpg',backgroundImage:"myUrl"}
Now for your second question, this is template literal and allow you to write variables or call functions inside a string. think we have not this so we must write it like :
backgroundImage: "url(" + props.photo +")"
so as you see it is something like concating props.photo and other strings.but with template literals we can wrap string with backticks and then write variable or function of javascript between ${} like below
backgroundImage: `url(${props.photo})`
then ${props.photo} replace with its value.

react-virtualized Grid.cellRenderer - issues adding style prop

I am using the Grid component and have a cellRenderer. In it I attempt to add a backgroundColor style to the outer div.
customColumnRenderer(props: GridCellProps): React.ReactNode {
...
props.style.backgroundColor = "hotpink";
...
return <div style={props.style}
... </div>;
}
All is good at first, but then I scroll vertically a bit and I get this exception:
Uncaught TypeError: Cannot assign to read only property 'backgroundColor' of object '#<Object>'
When I look in the debugger. props.style looks like a simple Object to me. The doc says
"You can add additional class names or style properties as you would like."
Any thoughts on what I might be doing wrong?
Best workaround I could come up with is to merge in style props from a different object using the spread operator. Something like this:
customColumnRenderer(props: GridCellProps): React.ReactNode {
...
let myStyles = {backgroundColor: "hotpink"};
let styles = {...props.style, ...myStyles};
...
return <div style={styles}
... </div>;
}

What is the best way to conditionally apply a class?

Lets say you have an array that is rendered in a ul with an li for each element and a property on the controller called selectedIndex. What would be the best way to add a class to the li with the index selectedIndex in AngularJS?
I am currently duplicating (by hand) the li code and adding the class to one of the li tags and using ng-show and ng-hide to show only one li per index.
If you don't want to put CSS class names into Controller like I do, here is an old trick that I use since pre-v1 days. We can write an expression that evaluates directly to a class name selected, no custom directives are necessary:
ng:class="{true:'selected', false:''}[$index==selectedIndex]"
Please note the old syntax with colon.
There is also a new better way of applying classes conditionally, like:
ng-class="{selected: $index==selectedIndex}"
Angular now supports expressions that return an object. Each property (name) of this object is now considered as a class name and is applied depending on its value.
However these ways are not functionally equal. Here is an example:
ng-class="{admin:'enabled', moderator:'disabled', '':'hidden'}[user.role]"
We could therefore reuse existing CSS classes by basically mapping a model property to a class name and at the same time keep CSS classes out of Controller code.
ng-class supports an expression that must evaluate to either
A string of space-delimited class names, or
An array of class names, or
A map/object of class names to boolean values.
So, using form 3) we can simply write
ng-class="{'selected': $index==selectedIndex}"
See also How do I conditionally apply CSS styles in AngularJS? for a broader answer.
Update: Angular 1.1.5 has added support for a ternary operator, so if that construct is more familiar to you:
ng-class="($index==selectedIndex) ? 'selected' : ''"
My favorite method is using the ternary expression.
ng-class="condition ? 'trueClass' : 'falseClass'"
Note: Incase you're using a older version of Angular you should use this instead,
ng-class="condition && 'trueClass' || 'falseClass'"
I'll add to this, because some of these answers seem out of date. Here's how I do it:
<class="ng-class:isSelected">
Where 'isSelected' is a javascript variable defined within the scoped angular controller.
To more specifically address your question, here's how you might generate a list with that:
HTML
<div ng-controller="ListCtrl">
<li class="ng-class:item.isSelected" ng-repeat="item in list">
{{item.name}}
</li>
</div>
JS
function ListCtrl($scope) {
$scope.list = [
{"name": "Item 1", "isSelected": "active"},
{"name": "Item 2", "isSelected": ""}
]
}
See: http://jsfiddle.net/tTfWM/
See: http://docs.angularjs.org/api/ng.directive:ngClass
Here is a much simpler solution:
function MyControl($scope){
$scope.values = ["a","b","c","d","e","f"];
$scope.selectedIndex = -1;
$scope.toggleSelect = function(ind){
if( ind === $scope.selectedIndex ){
$scope.selectedIndex = -1;
} else{
$scope.selectedIndex = ind;
}
}
$scope.getClass = function(ind){
if( ind === $scope.selectedIndex ){
return "selected";
} else{
return "";
}
}
$scope.getButtonLabel = function(ind){
if( ind === $scope.selectedIndex ){
return "Deselect";
} else{
return "Select";
}
}
}
.selected {
color:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<div ng-app ng-controller="MyControl">
<ul>
<li ng-class="getClass($index)" ng-repeat="value in values" >{{value}} <button ng-click="toggleSelect($index)">{{getButtonLabel($index)}}</button></li>
</ul>
<p>Selected: {{selectedIndex}}</p>
</div>
I faced a similar problem recently and decided to just create a conditional filter:
angular.module('myFilters', []).
/**
* "if" filter
* Simple filter useful for conditionally applying CSS classes and decouple
* view from controller
*/
filter('if', function() {
return function(input, value) {
if (typeof(input) === 'string') {
input = [input, ''];
}
return value? input[0] : input[1];
};
});
It takes a single argument, which is either a 2-element array or a string, which gets turned into an array that is appended an empty string as the second element:
<li ng-repeat="item in products | filter:search | orderBy:orderProp |
page:pageNum:pageLength" ng-class="'opened'|if:isOpen(item)">
...
</li>
If you want to go beyond binary evaluation and keep your CSS out of your controller you can implement a simple filter that evaluates the input against a map object:
angular.module('myApp.filters, [])
.filter('switch', function () {
return function (input, map) {
return map[input] || '';
};
});
This allows you to write your markup like this:
<div ng-class="muppets.star|switch:{'Kermit':'green', 'Miss Piggy': 'pink', 'Animal': 'loud'}">
...
</div>
The was I recently did that was doing this:
<input type="password" placeholder="Enter your password"
ng-class="{true: 'form-control isActive', false: 'isNotActive'}[isShowing]">
The isShowing value is a value that is located on my controller that gets toggled with the click of a button and the parts between the single parenthesis are classes I created in my css file.
EDIT: I would also like to add that codeschool.com has a free course that is sponsored by google on AngularJS that goes over all of this stuff and then some. There is no need to pay for anything, just signup for an account and get going!
Best of luck to you all!
Ternary operator has just been added to angular parser in 1.1.5.
So the simplest way to do this is now :
ng:class="($index==selectedIndex)? 'selected' : ''"
We can make a function to manage return class with condition
<script>
angular.module('myapp', [])
.controller('ExampleController', ['$scope', function ($scope) {
$scope.MyColors = ['It is Red', 'It is Yellow', 'It is Blue', 'It is Green', 'It is Gray'];
$scope.getClass = function (strValue) {
switch(strValue) {
case "It is Red":return "Red";break;
case "It is Yellow":return "Yellow";break;
case "It is Blue":return "Blue";break;
case "It is Green":return "Green";break;
case "It is Gray":return "Gray";break;
}
}
}]);
</script>
And then
<body ng-app="myapp" ng-controller="ExampleController">
<h2>AngularJS ng-class if example</h2>
<ul >
<li ng-repeat="icolor in MyColors" >
<p ng-class="[getClass(icolor), 'b']">{{icolor}}</p>
</li>
</ul>
<hr/>
<p>Other way using : ng-class="{'class1' : expression1, 'class2' : expression2,'class3':expression2,...}"</p>
<ul>
<li ng-repeat="icolor in MyColors">
<p ng-class="{'Red':icolor=='It is Red','Yellow':icolor=='It is Yellow','Blue':icolor=='It is Blue','Green':icolor=='It is Green','Gray':icolor=='It is Gray'}" class="b">{{icolor}}</p>
</li>
</ul>
You can refer to full code page at ng-class if example
I am new to Angular but have found this to solve my issue:
<i class="icon-download" ng-click="showDetails = ! showDetails" ng-class="{'icon-upload': showDetails}"></i>
This will conditionally apply a class based on a var.
It starts off with a icon-download as a default, the using ng-class, I check the status of showDetails if true/false and apply class icon-upload. Its working great.
Hope it helps.
This works like a charm ;)
<ul class="nav nav-pills" ng-init="selectedType = 'return'">
<li role="presentation" ng-class="{'active':selectedType === 'return'}"
ng-click="selectedType = 'return'"><a href="#return">return
</a></li>
<li role="presentation" ng-class="{'active':selectedType === 'oneway'}"
ng-click="selectedType = 'oneway'"><a href="#oneway">oneway
</a></li>
</ul>
This will probably get downvoted to oblivion, but here is how I used 1.1.5's ternary operators to switch classes depending on whether a row in a table is the first, middle or last -- except if there is only one row in the table:
<span class="attribute-row" ng-class="(restaurant.Attributes.length === 1) || ($first ? 'attribute-first-row': false || $middle ? 'attribute-middle-row': false || $last ? 'attribute-last-row': false)">
</span>
This is in my work multiple conditionally judge:
<li ng-repeat='eOption in exam.examOptions' ng-class="exam.examTitle.ANSWER_COM==exam.examTitle.RIGHT_ANSWER?(eOption.eoSequence==exam.examTitle.ANSWER_COM?'right':''):eOption.eoSequence==exam.examTitle.ANSWER_COM?'wrong':eOption.eoSequence==exam.examTitle.RIGHT_ANSWER?'right':''">
<strong>{{eOption.eoSequence}}</strong> |
<span ng-bind-html="eOption.eoName | to_trusted">2020 元</span>
</li>
Here is another option that 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)
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);
}
partial
<div class="col-md-4 text-right">
<a ng-class="campaign_range === 'thismonth' ? 'btn btn-blue' : 'btn btn-link'" href="#" ng-click='change_range("thismonth")'>This Month</a>
<a ng-class="campaign_range === 'all' ? 'btn btn-blue' : 'btn btn-link'" href="#" ng-click='change_range("all")'>All Time</a>
</div>
controller
$scope.campaign_range = "all";
$scope.change_range = function(range) {
if (range === "all")
{
$scope.campaign_range = "all"
}
else
{
$scope.campaign_range = "thismonth"
}
};
If you are using angular pre v1.1.5 (i.e. no ternary operator) and you still want an equivalent way to set a value in both conditions you can do something like this:
ng-class="{'class1':item.isReadOnly == false, 'class2':item.isReadOnly == true}"
If you having a common class that is applied to many elements you can create a custom directive that will add that class like ng-show/ng-hide.
This directive will add the class 'active' to the button if it's clicked
module.directive('ngActive', ['$animate', function($animate) {
return function(scope, element, attr) {
scope.$watch(attr.ngActive, function ngActiveWatchAction(value){
$animate[value ? 'addClass' : 'removeClass'](element, 'active');
});
};
}]);
More info
Just adding something that worked for me today, after much searching...
<div class="form-group" ng-class="{true: 'has-error'}[ctrl.submitted && myForm.myField.$error.required]">
Hope this assists in your successful development.
=)
Undocumented Expression Syntax : Great Website Link... =)
Check this.
The infamous AngularJS if|else statement!!!
When I started using Angularjs, I was a bit surprised that I couldn’t find an if/else statement.
So I was working on a project and I noticed that when using the if/else statement, the condition shows while loading.
You can use ng-cloak to fix this.
<div class="ng-cloak">
<p ng-show="statement">Show this line</span>
<p ng-hide="statement">Show this line instead</span>
</div>
.ng-cloak { display: none }
Thanks amadou
You can use this npm package. It handles everything and has options for static and conditional classes based on a variable or a function.
// Support for string arguments
getClassNames('class1', 'class2');
// support for Object
getClassNames({class1: true, class2 : false});
// support for all type of data
getClassNames('class1', 'class2', ['class3', 'class4'], {
class5 : function() { return false; },
class6 : function() { return true; }
});
<div className={getClassNames({class1: true, class2 : false})} />
I understand this question id for angular, but if anyone is using React or a React-Based Framework (Amplify, NextJS, Serverless, etc.) The solution is significantly easier. The most performant way is with a ternary operator like so:
<div className={condition ? "classnameiftrue" : "classnameiffalse"}>
You can use this strategy to animate the tree if using useState() as each time the state changes it will reload that conditional with the new value.

Resources