Bind classes with Alpine but based on value of variable, not boolean - alpine.js

OK, so, I'm probably barking up the wrong tree, but I'm lost in the forest anyway.
I'm trying to sort out class binding so I can toggle classes in a checkout's navigation bar to where the user is in the checkout.
There are three checkout steps (billing, payment method and payment) and each of the three can be 'inactive', 'active', or 'done'. Assume, when the page loads, that all the billing information is already complete, so billing would be done, the payment method step would be active, and the payment step would be inactive. The container therefore looks like :
<main x-data="{b:'done', pm:'active', p:'inactive' }">
Within an individual step I am trying to use the value of each variable in class binding like so :
<i class="text-4xl" :class="pm='inactive' ? 'fal fa-circle-xmark text-gray-500' : ''" :class="pm='active' ? 'fal fa-pen-circle text-indigo-500' : ''" :class="pm='done' ? 'fal fa-circle-check text-green-500' : ''"></i>
But it's not working - if it were, for the payment method step I'd be seeing an indigo pen icon, but I'm seeing the 'inactive' grey circle-xmark icon.
Where am I going wrong? And can I simplify the tag rather than use three ternary operators?

OK, so, I was being a bit of a muppet. Needed "==" rather than just "=" in the comparator section of the ternary operator. And yes, you can nest the ternary clause :
<i class="text-4xl" :class="pm=='inactive' ? 'fal fa-circle-xmark text-gray-500' : ''" :class="pm=='active' ? 'fal fa-pen-circle text-indigo-500' : ''" :class="pm=='done' ? 'fal fa-circle-check text-green-500' : ''"></i>

Related

Binding classes issue with AlpineJS - ternary operator and curly brackets

EDIT: Due to the general syntax of AlpineJS, writing a ternary operator inside curly brackets is a mistake you may easily run into.
This is just a "grammar" issue but I really want to figure it out... Let's jump into it.
Everything is ok if I write:
:class="{ 'some_class': activeSlide == slide }"
On the contrary it doesn't work (i.e. 'some_class' is not added as a class) if I write:
:class="{ activeSlide == slide ? 'some_class' : '' }"
What's wrong with it?
(I don't think it's relevant, but you can have a look at the entire code here, inside 'template' tags: link)
The ternary operator works only with the non-object class syntax:
:class="activeSlide == slide ? 'some_class' : ''"
You can use the shorthand conditionals as well:
:class="activeSlide == slide && 'some_class'"

:class not adding class

So I have this button, and x-data for a parent div had this: days: {SUN: false}. It finds the variable so no problem. Here is the button:
<button type="button" class="date-btn" #click="days[$el.textContent] = !days[$el.textContent]; alert(days[$el.textContent])" :class="{'bg-blue-800' ? days[$el.textContent]: ''}">SUN</button>
The alert shows that the value switches from true and false on click, but the color of the button never changes.
Here is my .date-btn styles, but even stripping it dry except of m and px gives the same result:
#layer components {
.date-btn {
#apply m-2 px-7 py-3 text-white font-medium text-sm leading-snug
uppercase rounded shadow-md hover:shadow-lg
focus:shadow-lg focus:outline-none focus:ring-0
active:shadow-lg transition duration-150 ease-in-out drop-shadow-lg;
}
}
I also did :class="'bg-blue-800' ? days[$el.textContent]: ''" but no banana.
What is wrong with my :class call?
The input of the ternary operator should be a condition:
:class="days[$el.textContent] ? 'bg-blue-800' : ''"
You can also use a shorthand version:
:class="days[$el.textContent] && 'bg-blue-800'"
I'd have a read in the docs. x-bind, what is what you're using when using the shorthand :class, uses booleans to define what class to bind. As per example you've given, you've mixed up the ternary statement and object syntax. This won't ever result in anything. However, say you hadn't wrapped it in an object, then it would still fail, since 'bg-blue-800' is always truthy, which will always bind '' as class.
If you want to use a ternary statement, instead you'd have to write it as such: :class="days[$el.textContent] ? 'bg-blue-800' : ''".
Because AlpineJS aims at making life easy, you can also use shorthand syntax to replace the ternary operator. In this case, you could rewrite above example to :class="days[$el.textContent] && 'bg-blue-800'"
The last method you already mention in your own answer. The object class: boolean syntax, which (in my opinion) is the most logical way to write the x-bind is the last possible method to bind elements with. By passing a class as key, and a boolean as value, AlpineJS knows which class(es) to bind.
Ok for some reason this worked :class="{'bg-blue-800' : days[$el.textContent]}"
Shoutout to https://daily.dev/blog/alpine-js-the-ultimate-guide

Can I use a tenary operator when passing blade attribute to child component

pretty straigth forward. I would like to know if I can use tenary operators or null coalesce operators when I pass an attribute to a child component in blade.
For example
<x-filter :categories="{{ isset($categories) ? $categories : false }}" />
or
<x-filter :categories="{{ $categories ?? false }}" />
I have not been successful with this in laravel 8.
Am I having an error in syntax or is this just not possible?
Best Simon
TL;DR:
Use {{ }} OR : but not both of them
When you place a : before the attribute this means you are writing php within your double quotes, so as #lagbox mentioned in his comment just remove it. OR remove the colon and you are good to go, nothing fancy here.

How to write xpath to select item that doesnot have aria-hidden="true"?

I need to click "Cancel" button. So I tried writing an x-path that looks something like this :
//button[text()='Cancel']
But two elements are identified since two elements match the conditions as below, though one of the element is hidden :
<button type="button" class="gwt-Button button_action_id_9135994370013188418_9135995360013188733_compositepopup_3 TableCtrl-button cancel-button">Cancel</button>
<button type="button" class="gwt-Button button_action_id_9149469526113774095_9149539697213772969 TableCtrl-button cancel-button" aria-hidden="true" style="display: none;">Cancel</button>
Is there a way I could identify the element that does not have a tag like 'aria-hidden' ?
Try to use below XPath to match required (not hidden button):
//button[text()='Cancel' and not(#aria-hidden='true')]

Capybara - Finding a disabled button located in table row

I have a button
<button type="button" id="saveListing" class="button small save-button" data-bind="enable: saveEnabled, click: save"><i class="fa fa-save"></i> Save</button>
located in the tr of a table.
I wrote a function for testing the button status, simply using:
And(/^...the button "(.*?)" on "(.*?)" page is disabled$/) do |button_name, page|
button_id = ConfigHelper.get_button_info(button_name, page)['id']
button_class = ConfigHelper.get_button_info(button_name, page)['class']
if !button_id.nil?
find_button(button_id)[:disabled].should eq 'true'
elsif !button_class.nil?
find(button_class)[:disabled].should eq 'true'
else
button_text = ConfigHelper.get_button_info(button_name, page)['text']
find('button', text: button_text)[:disabled].should eq "true"
end
end
However, this block does not work for a button in a table row. I also tried add checking by button id, and it also did not work. How can I implement it without taking table id as a parameter? (As I don't want to write table id inside the feature)
When using id, the error is:
Capybara::ElementNotFound: Unable to find css ".saveListing"
or using text:
Ambiguous match, found 4 elements matching css "button" (Capybara::Ambiguous)
Thanks.
Capybaras find_button doesn't search css classes at all so unless you have overwritten find_button I'm not sure why you would be getting that error from using it with an id. find_button will search by the id, value, text content, or title attribute of the button, and also supports a disabled filter for searching. More stable (if the status of the button is changing due to JS) versions of those checks would be
find_button('saveListing', disabled: true).should be #Note: no # in front of the id here since its not a css selector
find_button('button text', disabled: true).should be
These would be more stable because they will utilize Capybaras waiting behavior to find the disabled button, whereas the way they were written previously would immediately find the button and error if they weren't yet disabled.
saveListing is the id of your button, not a class. In css selectors, dot is used for classes and hash sign is used for ids.
Therefore, you should either be using #saveListing or .save-button, but not .saveListing. This is why your first matching fails.
As to why the second one does - I guess there are 4 rows, each with one button and Capybara doesn't know which one you are referring to. If you want to check this condition for all of them, you could use all instead of find like this:
all('button', text: button_text).each do |button|
button[:disabled].should eq "true"
end

Resources