Elegant way to write when inside a for loop? - raku

I can write something like this (elem here is an XML::Element but it doesn't really matter):
for $elem.nodes {
when XML::Element { ... }
when XML::Text { ... }
...
default { note qq{Ignoring unknown XML node "$_".} }
}
which looks nice, but doesn't give me a readable name for $_ inside the code using it, which is why I'd prefer to write this:
for $elem.nodes -> $child {
when XML::Element { ... }
when XML::Text { ... }
...
default { note qq{Ignoring unknown XML node "$child".} }
}
but this doesn't work because now $_ isn't set, and so I actually need to write
for $elem.nodes -> $child {
given $child {
when XML::Element { ... }
when XML::Text { ... }
...
default { note qq{Ignoring unknown XML node "$child".} }
}
}
which is a bit redundant and adds an extra level of indentation.
It's definitely not the end of the world, but am I missing some simple way to have both a readable variable name and avoid given?

You can bind the variable above the when statements, it's a little uglier but it does the job.
for $elem.nodes {
my $child = $_;
when XML::Element { say 'I am XML!' }
when XML::Text { say 'I am text!' }
default { say "I am default: $child" }
}
Edit: In Raku I think it is perfectly reasonable to stick to using $_ seeing as the idea of $_ has been around for quite some time.

for #a -> $x { {
when 2 { say "$x ... a" }
when 4 { say "$x ... b" }
} given $x }
naughty double curlies and a post-given?

Related

Knex filter on pre filtered request

I would like to achieve a request in which I can filter my results with where clauses and then apply others where but on the result of the first where
return await UserModel.query()
.withGraphFetched('roles')
.modify(function(queryBuilder) {
if (accessControlFilters.agency_id) {
queryBuilder.orWhere('agency_id', accessControlFilters.agency_id)
}
if (accessControlFilters.third_party.length) {
queryBuilder.orWhereIn('third_party_id', accessControlFilters.third_party)
}
if (accessControlFilters.corporation.length) {
queryBuilder.orWhereIn('corporation_id', accessControlFilters.corporation)
}
})
.modify(function(queryBuilder) {
if (Array.isArray(userFilters.corporation) && userFilters.corporation.length) {
queryBuilder.orWhereIn('corporation_id', userFilters.corporation)
}
if (Array.isArray(userFilters.third_party) && userFilters.third_party.length) {
queryBuilder.orWhereIn('third_party_id', userFilters.third_party)
}
})
.limit(limit)
.offset(offset)
.debug()
But there it just concatenates my first modifier with the second :(
DO you have any clue how i can achieve this ?

VueJS: Adding a class to one element if a class exists on another element

I'm working in VueJS. I'm trying to bind a class to one element based on an existence of a class on another element. The below is in a :for loop to print out the list.
The '#accordion-'+(index+1)) is the id of the div I want to check to see if a class exists on it.
I wrote a method and it works UNTIL I check the element's classList. Right now, I'm only doing a console log, but eventually this will return true and hopefully the class will apply.
methods: {
existingTopic: function(lcDivID) {
const element = document.querySelector(lcDivID);
console.log(element); //It gives me the element.
/* The below is where it crashes */
console.log(element.classList.contains("collapsePanelExistingTopic"));
}
}
I find it so frustrating. I've spent a day on this without any results. Any help you can provide it would be great.
Here it is, you can also use this.$el as document
...
methods: {
hasClass() {
const element = this.$el.querySelector('h1')
if (element.classList.contains('your-class-here')) {
console.log(true)
this.$el.querySelector('anotherelement').classList.add("your-another-class");
} else {
console.log(false)
}
}
},
mounted() {
this.hasClass()
}
...
Alternative
<h1 class="mb-5 fs-lg" ref="myElement">Sign up</h1>
...
methods: {
hasClass() {
const element = this.$refs.myElement
if (element.classList.contains('mb-5')) {
console.log(true)
this.$el.querySelector('anotherelement').classList.add("your-another-class");
} else {
console.log(false)
}
}
},
mounted() {
this.hasClass()
}
...
So you can define ref as :ref="index+1" in your loop

Access an element's Binding

I have a custom attribute that processes authentication data and does some fun stuff based on the instructions.
<div auth="disabled: abc; show: xyz; highlight: 123">
There's a lot of complicated, delicate stuff happening in here and it makes sense to keep it separate from semantic bindings like disabled.bind. However, some elements will have application-logic level bindings as well.
<div auth="disabled.bind: canEdit" disabled.bind="!editing">
Under the covers, my auth attribute looks at the logged in user, determines if the user has the correct permissions, and takes the correct action based on the result.
disabledChanged(value) {
const isDisabled = this.checkPermissions(value);
if (isDisabled) {
this.element.disabled = true;
}
}
This result needs to override other bindings, which may or may not exist. Ideally, I'd like to look for an existing Binding and override it ala binding behaviors.
constructor(element) {
const bindings = this.getBindings(element); // What is the getBindings() function?
const method = bindings['disabled']
if (method) {
bindings['disabled'] = () => this.checkPermission(this.value) && method();
}
}
The question is what is this getBindings(element) function? How can I access arbitrary bindings on an element?
Edit: Gist here: https://gist.run/?id=4f2879410506c7da3b9354af3bcf2fa1
The disabled attribute is just an element attribute, so you can simply use the built in APIs to do this. Check out a runnable example here: https://gist.run/?id=b7fef34ea5871dcf1a23bae4afaa9dde
Using setAttribute and removeAttribute (since the disabled attribute does not really have a value, its mere existence causes the element to be disabled), is all that needs to happen:
import {inject} from 'aurelia-framework';
#inject(Element)
export class AuthCustomAttribute {
constructor(element) {
this.el = element;
}
attached() {
let val = false;
setInterval(() => {
if(this.val) {
this.el.setAttribute('disabled', 'disabled');
} else {
this.el.removeAttribute('disabled');
}
this.val = !this.val;
}, 1000);
}
}
NEW RESPONSE BELOW
You need to work directly with the binding engine. A runnable gist is located here: https://gist.run/?id=b7fef34ea5871dcf1a23bae4afaa9dde
Basically, you need to get the original binding expression, cache it, and then replace it (if auth === false) with a binding expression of true. Then you need to unbind and rebind the binding expression:
import {inject} from 'aurelia-framework';
import {Parser} from 'aurelia-binding';
#inject(Element, Parser)
export class AuthCustomAttribute {
constructor(element, parser) {
this.el = element;
this.parser = parser;
}
created(owningView) {
this.disabledBinding = owningView.bindings.find( b => b.target === this.el && b.targetProperty === 'disabled');
if( this.disabledBinding ) {
this.disabledBinding.originalSourceExpression = this.disabledBinding.sourceExpression;
// this expression will always evaluate to true
this.expression = this.parser.parse('true');
}
}
bind() {
// for some reason if I don't do this, then valueChanged is getting called before created
this.valueChanged();
}
unbind() {
if(this.disabledBinding) {
this.disabledBinding.sourceExpression = this.disabledBinding.originalSourceExpression;
this.disabledBinding.originalSourceExpression = null;
this.rebind();
this.disabledBinding = null;
}
}
valueChanged() {
if(this.disabledBinding ) {
if( this.value === true ) {
this.disabledBinding.sourceExpression = this.disabledBinding.originalSourceExpression;
} else {
this.disabledBinding.sourceExpression = this.expression;
}
this.rebind();
} else {
if( this.value === true ) {
this.el.removeAttribute('disabled');
} else {
this.el.setAttribute('disabled', 'disabled');
}
}
}
rebind() {
const source = this.disabledBinding.source;
this.disabledBinding.unbind();
this.disabledBinding.bind(source);
}
}
It is important that the attribute clean up after itself, as I do in the unbind callback. I'll be honest that I'm not sure that the call to rebind is actually necessary in the unbind, but it's there for completeness.

Looking for an element to not be present

I'm trying to look for an absence of an element in a conditional which would then take two different paths if the element is not there. However what I am getting is 'element not found' which is what I need but I need to go around this. Here is what I've tried:
if (HomeScreen.tabs.propertiesTab.isPresent()) {
HomeScreen.tabs.propertiesTab.click();
} else {
HomeScreen.tabs.allTabsTab.click().then(function() {
HomeScreen.allTabs.properties.click();
})
}
and
HomeScreen.tabs.propertiesTab.isPresent().toBeFalsy().then(function(isVisible) {
if (isVisible) {
HomeScreen.tabs.propertiesTab.click();
} else {
HomeScreen.tabs.allTabsTab.click().then(function() {
HomeScreen.allTabs.properties.click();
});
}
});
Any suggestions?
Try to explicitly resolve the promise with then():
browser.isElementPresent(HomeScreen.tabs.propertiesTab).then(function (isPresent) {
if (isPresent) {
// ...
} else {
// ...
}
});
Using browser.isElementPresent() here, but it should work with .isPresent() as well:
In protractor, browser.isElementPresent vs element.isPresent vs element.isElementPresent

SCSS nth-child in for loop? [duplicate]

This question already has an answer here:
Using SASS Variables within nth-child?
(1 answer)
Closed 7 years ago.
I was trying to create a loop to create a number of nth-child selectors with matching content:
$show-numbers: true;
#if $show-numbers {
#for $i from 1 through 5 {
&:nth-child(1) {
&:before {
content: '#{$i}';
}
}
}
}
This, of course, makes 5 copies of
ul.checkout-bar li:nth-child(1):before {
content: "1";
}
with the "content" correctly incremented. But I cannot get the nth-child value to increment. Is this not possible in Sass?
NOTE a static variable can be interpolated:
$foo: 1;
&:nth-child(#{$foo}) {
&:before {
content: '1';
}
}
This works fine. It's the first thing I tried.
However, when using the $i in the for loop, it does not work.
You need to wrap the $i as an integer in the :nth-child() like this:
$show-numbers: true;
#if $show-numbers {
#for $i from 1 through 5 {
&:nth-child(#{$i}) {
&:before {
content: '#{$i}';
}
}
}
}
Renders:
:nth-child(1):before {
content:'1';
}
:nth-child(2):before {
content:'2';
}
:nth-child(3):before {
content:'3';
}
:nth-child(4):before {
content:'4';
}
:nth-child(5):before {
content:'5';
}