DOM classList.remove only works after two clicks instead of one - dom

I am working on an app where I want to remove a classList from one element and add a classList to a second. What I have happening though is the second element gets its classList added on the first click, but the first element does not get its classList removed until the second click.
Here is the event handler:
const resetCount = () => {
setCount(initialCount);
setGrid(initialState);
const active = document.querySelector(".active");
active.classList.remove("active");
const five = document.querySelector(".five");
five.classList.add("active");
}
I have this connected to my button via onClick.
As I mentioned, when I click the button the const five gets the class name "active" added, but the const active does not get the class name "active" removed. When I click a second time, the cont active then gets the class name "active" removed.
Since the two event are next to each other and in the same function, I'm confused as to why the remove("active") event only works after the second click? Any clarification would be appreciated.

Related

Unreal Engine 4.27.2 C++ | SetText not updating UserWidget’s TextBlock from a WidgetComponent in a BP

I have an Actor BP, inside this BP I have a UWidgetComponent with a Widget Class already selected in the Details, and that Widget Class has a TextBlock. Now, inside this BP I also have a C++ USceneComponent, this component is in charge of showing a random text, in the TextBlock mentioned above, each time the user presses a button.
This is in my USceneComponent's header file (.h)
class UWidgetComponent* QuestionWidget;
class UQuestionProjectionText* QuestionText;
class UTextBlock* QuestionTextBlock;
Then in the ".cpp" file, in the constructor
QuestionWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("CodeQuestionWidget"));
QuestionTextBlock = CreateDefaultSubobject<UTextBlock>(TEXT("CodeTextBlock"));
Then in the BeginPlay()
//Gets the WidgetComponent from the BP
QuestionWidget = Cast<UWidgetComponent>(GetOwner()->GetComponentByClass(UWidgetComponent::StaticClass()));
if (QuestionWidget)
{
QuestionWidget->InitWidget();
//Gets an instance of the UMyUserWidget class, from the UWidgetComponent in the BP
QuestionText = Cast<UMyUserWidget>(QuestionWidget->GetUserWidgetObject());
this->QuestionTextBlock = QuestionText->QuestionTextBlock;
//Sets the text to an empty String
QuestionTextBlock->SetText(FText::FromString(""));
}
else
{
UE_LOG(LogTemp, Error, TEXT("QuestionWidget was not found"));
return;
}
Then in the TickComponent(), when the user presses the button, I use a something that looks like this
QuestionTextBlock->SetText(FText::FromString(QuestionStringsArray[9]));
The problem is that when I press the button, the text does not change in the Widget, but if I print the text that I'm passing, it does print the string, so I'm not passing an empty value to the "SetText()".
Another weird thing, is that the line in the BeginPlay that sets the text, that one works, I have changed it to a random string, instead of an empty one, and it does display it.
I don't know if when I do the "QuestionWidget->InitWidget();" I'm creating a new one separate from the one in the BP, or if I'm just missing something. If I eliminate the "QuestionWidget->InitWidget()" the widget gets initialized on time sometimes, and sometimes it doesn't.
I have some error handling in my code, but eliminated it here so that it didn't look too messy. But also, none of the Errors popup, everything goes on smoothly, only that the Widget doesn't show the updated Text.

How to access control from the popup fragment by ID

I want my text area to be empty after I press OK button.
I have try this line this.byId("id").setValue("")
onWorkInProgress: function (oEvent) {
if (!this._oOnWorkInProgressDialog) {
this._oOnWorkInProgressDialog = sap.ui.xmlfragment("WIPworklist", "com.sap.FinalAssestments.view.WorkInProgress", this);
//this.byId("WIP").value = "";
//this.byId("WIP").setValue();
this.getView().addDependent(this._oOnWorkInProgressDialog);
}
var bindingPath = oEvent.getSource().getBindingContext().getPath();
this._oOnWorkInProgressDialog.bindElement(bindingPath);
this._oOnWorkInProgressDialog.open();
},
//function when cancel button inside the fragments is triggered
onCancelApproval: function() {
this._oOnWorkInProgressDialog.close();
},
//function when approval button inside the fragments is triggered
onWIPApproval: function() {
this._oOnWorkInProgressDialog.close();
var message = this.getView().getModel("i18n").getResourceBundle().getText("wipSuccess");
MessageToast.show(message);
},
The text area will be in popup in the fragment. I am expecting the text area to be empty.
If you instantiate your fragment like this:
sap.ui.xmlfragment("WIPworklist", "com.sap.FinalAssestments.view.WorkInProgress", this);
You can access its controls like this:
Fragment.byId("WIPworklist", "WIP").setValue(""); // Fragment required from "sap/ui/core/Fragment"
Source: How to Access Elements from XML Fragment by ID
The better approach would be to use a view model. The model should have a property textAreaValue or something like that.
Then bind that property to your TextArea (<TextArea value="{view>/textAreaValue}" />). If you change the value using code (e.g. this.getView().getModel("view").setProperty("/textAreaValue", "")), it will automatically show the new value in your popup.
And it works both ways: if a user changes the text, it will be automatically updated in the view model, so you can access the new value using this.getView().getModel("view").getProperty("/textAreaValue");.
You almost have it, I think. Just put the
this.byId("WIP").setValue("") line after the if() block. Since you are adding the fragment as a dependent of your view, this.byId("WIP") will find the control with id "WIP" every time you open the WIP fragment and set its value to blank.
You are likely not achieving it now because A. it is not yet a dependent of your view and B. it is only getting fired on the first go-around.

Polymer 2 - Perform action every time element is shown via iron-pages/iron-selector

I'm attempting to create a logout page that will work even after that element has been attached once to the DOM. This occurs when you get a login, then logout, then login again, and attempt to log back out.
For instance, the shell has
<iron-selector selected="[[page]]" attr-for-selected="name">
<a name="logout" href="[[rootPath]]logout">
<paper-icon-button icon="my-icons:sign-out" title="Logout" hidden$="[[!loggedIn]]"></paper-icon-button>
</a>
<a name="login" href="[[rootPath]]login">
<paper-icon-button icon="my-icons:sign-in" title="Login" hidden$="[[loggedIn]]"></paper-icon-button>
</a>
</iron-selector>
<<SNIP>>
<iron-pages selected="[[page]]" attr-for-selected="name" fallback-selection="view404" role="main">
<my-search name="search"></my-search>
<my-login name="login"></my-login>
<my-logout name="logout"></my-logout>
<my-view404 name="view404"></my-view404>
</iron-pages>
I also have an observer for page changes in the shell:
static get observers() {
return [
'_routePageChanged(routeData.page)',
];
}
_routePageChanged(page) {
this.loggedIn = MyApp._computeLogin();
if (this.loggedIn) {
this.page = page || 'search';
} else {
window.history.pushState({}, 'Login', '/login');
window.dispatchEvent(new CustomEvent('location-changed'));
sessionStorage.clear();
this.page = 'login';
}
}
This works well as when I click on the icon to logout, it attaches the my-logout element just fine and performs what in ready() or connectedCallback() just fine.
my-logout has
ready() {
super.ready();
this._performLogout();
}
The issue comes when, without refreshing the browser and causing a DOM refresh, you log back in and attempt to log out a second time. Since the DOM never cleared, my-logout is still attached, so neither ready() nor connectedCallback() fire.
I've figured out a way of working around this, but it feels very kludgy. Basically, I can add an event listener to the element that will perform this._performLogout(); when the icon is selected:
ready() {
super.ready();
this._performLogout();
document.addEventListener('iron-select', (event) => {
if (event.detail.item === this) {
this._performLogout();
}
});
}
Like I said, it works, but I dislike having a global event listener, plus I have to call the logout function the first time the element attaches and I have to listen as the listener isn't active till after the first time the element is attached.
There does not appear to be a "one size fits all" solution to this. The central question is, "Do you want the parent to tell the child, or for the child to listen on the parent?". The "answer" I came up with in the question works if you want to listen to the parent, but because I don't like the idea of a global event listener, the below is how to use <iron-pages> to tell a child element that it has been selected for viewing.
We add the selected-attribute property to <iron-pages>:
<iron-pages selected="[[page]]" attr-for-selected="name" selected-attribute="selected" fallback-selection="view404" role="main">
<my-search name="search"></my-search>
<my-login name="login"></my-login>
<my-logout name="logout"></my-logout>
<my-view404 name="view404"></my-view404>
</iron-pages>
Yes, this looks a little confusing considering the attr-for-selected property. attr-for-selected says, "What attribute should I match on these child elements with the value of my selected property?" So when I click on
<iron-selector selected="[[page]]" attr-for-selected="name">
<a name="logout" href="[[rootPath]]logout"><paper-icon-button icon="my-icons:sign-out" title="Logout" hidden$="[[!loggedIn]]"></paper-icon-button></a>
</iron-selector>
it will set the <my-logout> internally as the selected element and display it. What selected-attribute="selected" does is to set an attribute on the child element. If you look in the browser JS console, you will see that the element now looks like
<my-login name="login"></my-logout>
<my-logout name="login" class="iron-selected" selected></my-logout>
We can define an observer in that in the <my-logout> element that checks for changes
static get properties() {
return {
// Other properties
selected: {
type: Boolean,
value: false,
observer: '_selectedChanged',
},
};
}
_selectedChanged(selected) {
if (selected) {
this._performLogout();
}
}
The if statement is so that we only fire the logic when we are displayed, not when we leave. One advantage of this is that we don't care if the element has already been attached to the DOM or not. When <iron-selector>/<iron-pages> selects the <my-logout> the first time, the attribute is set, the element attaches, the observer fires, the observer sees that selected is now true (as opposed to the defined false) and runs the logic.

How to highlight the selected tab in sap.m.IconTabBar?

Dear SAPUI5 developers,
I have a sap.m.IconTabBar and I set the active tab by the code when the user switch between pages. I used the following code:
sap.ui.getCore().byId(this.createId("iconTabBar")).setSelectedKey("1");
The problem is that it switch the selected tab correctly to the first tab. But it does not show the blue line under the tab that shows it is selected.
Please look at the following images:
What shows when I select the first tab by code:
But what it shows when I press the tab by the mouse it shows a blue line under the icon like the following:
As #Ash said in the comments you need to call fireSelect but this works just if user the second tab at first. If the user is on the first tab and switches between the pages then fireSelect does not act properly. Thus you need to select the second tab at first then it will works almost all times.
sap.ui.getCore().byId(this.createId("iconTabBar")).setSelectedKey("2");
sap.ui.getCore().byId(this.createId("iconTabBar")).setSelectedKey("1");
sap.ui.getCore().byId(this.createId("iconTabBar")).fireSelect();
Ok I had a look into the IconTabBar source Code and theres something I dont really get why but here is how it proceeds :
when you call IconTabBar.setSelectedKey(key), it calls IconTabHeader.setSelectedKey(key)
then internally the IconTabBarHeader calls setSelectedItem(item, true)
the 'true' here is important, the parameter is named 'bAPIchange' in the setSelectedItem function, and it is used as condition for the fireSelect() :
if (!bAPIchange) {
// fire event on iconTabBar
if (bIsParentIconTabBar) {
oParent.fireSelect({
selectedItem: this.oSelectedItem,
selectedKey: sSelectedKey,
item: this.oSelectedItem,
key: sSelectedKey
});
} else {
// fire event on header
this.fireSelect({
selectedItem: this.oSelectedItem,
selectedKey: sSelectedKey,
item: this.oSelectedItem,
key: sSelectedKey
});
}
}
Which explains why the event is not fired in your case

How to get the name of previous page in ZK?

I have a list page where it has button menu. While i select a record and click on that button menu a new page will be open with the corresponding code from the selected record.
From the opened page's doAfterCompose, how can i get the name of the previous page from where we clicked the button and opened the new page?
Please anyone help me with this...
What you should do (although I haven't seen your code) is to generate an event that passes as a value the previous page number when 'that button menu' is clicked. The event will then be picked up by the event queue together with the passed value.
So inside the method that handles the event generated by the click of the button menu you should add the following:
EventQueues.lookup("myqueue", EventQueues.DESKTOP, true)
.publish(new Event("buttonClicked", null, previousPage));
where previousPage is the value passed (an integer for the sake of the example).
Then inside the doAfterCompose() method you extrapolate the data passed:
EventQueues.lookup("myqueue", EventQueues.DESKTOP, true).subscribe(
new EventListener() {
public void onEvent(Event evt) {
if (evt.getName().equalsIgnoreCase("buttonClicked")) {
int thePreviousPage = (int) evt.getData();
.......