Background:
I have a component inside a template. This component is a pop up box which opens on a button click. This pop up box has check boxes (all by default set to false) inside of it. When I close this pop up box I want to completely reset all of the variables to the default settings, i.e. all checkboxes are now turned off. Currently, when I re-open this popup box the previous checkboxes are still checked. How do I do this without manually toggling every checkbox:
this.set("checkbox1", false);
this.set("checkbox2", false);
so on...
Is there a function that will automatically reset the component and uncheck all the checkboxes and set the variables back to false?
Relevant Code:
Template: app/template/home.hbs
{{popup-modal isOpen=showModal}}
Component: app/template/components/popup-modal.hbs
{{#bs-modal body=false footer=false open=isOpen title="popup box" closeAction=(action "cancel") submitAction=(action "submit")}}
{{#bs-modal-body}}
<label><input type="checkbox" {{action "toggleCheckbox" "checkbox1" on="click" preventDefault=false}}/> Checkbox 1</label>
<label><input type="checkbox" {{action "toggleCheckbox" "checkbox2" on="click" preventDefault=false}}/> Checkbox 2</label>
{{/bs-modal-body}}
{{bs-modal-footer closeTitle="Cancel" submitTitle="Ok"}}
{{/bs-modal}}
Component JS: app/components/popup-modal.js
import Ember from 'ember';
export default Ember.Component.extend({
checkbox1: false,
checkbox2: false,
actions: {
submit(){
// close box
this.set('isOpen', false);
},
cancel(){
// how do I reset component here?
// in other words, make all checkbox set to false
// without manually doing it like below:
this.set("checkbox1", false);
this.set("checkbox2", false);
},
toggleCheckbox(checkbox){
this.toggleProperty(checkbox);
}
}
});
Many times i've also had similar situation. And sometimes the values were not just boolean but strings or numeric (non-zero) values, so I also needed to be able to reset some properties of the component to initial state.
In my opinion resetting all properties of the component is not so good (i don't know if it's even possible), because somebody would like to add some property which should keeps it's state even after user clicks "Cancel" button.
I think good idea is to make some function which will set every properties to initial state. For example your code can looks like:
import Ember from 'ember';
export default Ember.Component.extend({
// EmberJS Component hook fired after component have been initialized
init() {
this._super(...arguments);
this.setInitialState();
},
setInitialState(){
this.set("checkbox1", false);
this.set("checkbox2", false);
},
actions: {
submit(){
// close box
this.set('isOpen', false);
this.setInitialState();
},
cancel(){
this.setInitialState();
},
toggleCheckbox(checkbox){
this.toggleProperty(checkbox);
}
}
});
PS. This is good that components lives and changes it's state (for example properties) without need to be initialized again if it weren't destroyed. Because when some properties are changed not the whole component is rerendered, but only things which were changed (in this case just checkboxes).
As others have said, it would be useful to have a reproduction of your code.
However - the answer is likely going to be setting the defaults you want (false in your case) in one of the component lifecycle hooks.
init is probably fine, assuming that the component gets destroyed when you close it from the template.
Related
I am using the TabView component from PrimeVue (Vue 3), and I want to stop the tab change if any changes are made by the user, the problem is that I don't know how. I've already tried passing the event and using preventDefault and stopPropagation but seems that it doesn't work and click event is still happening.
The procedure should be:
If any changes are made, user press the tab and a dialog appears.
If user clicks 'No', I should prevent the tab change and stop the click event
Here is the demo of what I'm trying to archive, should be simple https://codesandbox.io/s/aged-wave-yzl1k?file=/src/App.vue:0-1753
If a flag is true I want to show a confirm dialog and prevent the tab change if user dismiss it.
The component that I'm using for the TabView: https://primefaces.org/primevue/showcase/#/tabview
Thanks in advance,
From the docs it looks like that internally the component will first switch tabs and then emit "tab-click", which explains the issue you're seeing. The exception is if the tab is disabled, in which case it won't change tabs but will emit "tab-click".
It took a bit to figure out, but there is a way to get the functionality you need with only a small adjustment. It requires a change in your main.js as well as in your App.vue file.
// main.js
/*
* Put this after you import TabView.
* This will prevent automatic tab switching but still emits
* the event to your application.
*/
TabView.methods.onTabClick = function(event, i) {
this.$emit('tab-click', {
originalEvent: event,
index: i
});
}
// App.vue
const onTabClick = (event) => {
if (changes.value) {
confirm.require({
message:
"Are you sure that you want to leave this tab? You'll lose your changes",
icon: "fal fa-exclamation-triangle",
acceptLabel: "Yes",
rejectIcon: "No",
accept: () => {
alert("here we should allow tab change");
activeIndex.value = event.index; // manually set activeIndex
},
reject: () => {
alert("stop tab change");
},
});
}
};
These changes modify what the onTabClick library method to only emit the event, without automatically switching. Then in your app you can check the index property of the event to determine what should be set to active.
I'm struggling with getting a modal to appear onClick(). I have a function within a component that adds players to an existing list when clicking on Add Player. The button is rendered separately in a renderAddButton() function, which passes onAddButtonClick() as a prop.
I would like for the user to be able to input the player's name in a form within a modal before it is added to the list, right now the code outputs a Player + index as the name of the player.
function onAddButtonClick() {
setItems((prev) => {
const newItems = [...prev];
newItems.push({
name: `Player ${newItems.length + 1}`,
teamId: currentTeam[0].teamId
});
playersStore.push({
name: `Player ${newItems.length + 1}`,
teamId: currentTeam[0].teamId
});
return newItems;
});
}
I have this form which I want to represent in the modal:
export const PlayerForm = () => {
return (
<div>
<form>
<input type='string' id='playerId' name='playerName' defaultValue='0' />
<input
type='number'
id='playerGoals'
name='totalGoals'
defaultValue='0'
min='1'
max='5'
/>
<input
type='number'
id='playerGoals'
name='playerGoalPercentage'
defaultValue='0'
min='1'
max='5'
/>
</form>
</div>
);
};
How do I trigger the modal from inside onAddButtonClick()?
I implement modals using the react-bootstrap framework.
From the component that I want to display the modal from, I will create a handler that will govern the component's ability to show the modal based on the bool I set in state. Typically from the parent component this show handler would look like this:
setShow = () => {
this.setState({ show: !this.state.show });
};
As seen in the example this handles a state attribute called show which is what dictates whether or not the modal gets to display in app.
Below is the implementation of the modal I would use as a child component to the parent component where it would reside and where I would pass the state attribute which I called show that dictates with true or false whether or not to display the modal:
<ExampleModal
show={this.state.show}
setShow={this.setShow}
activeRecord={this.state.activeRecord}
activePrimaryAccountId={this.state.activePrimaryAccountId}
userAccessRole={this.props.userAccessRole}
/>
I pass the necessary details that the modal needs to display as props that I get from the the parent component's state attributes. The most important being the show attributes to include the setShow function which I use in the child component (the modal itself) to update state in the parent component to close the modal when the time comes also.
In the ExampleModal component I start off with declaring state with the following attributes already loaded from props:
this.state = {
show: this.props.show,
...
}
I then use a handler that takes advantage of the setShow function passed down to the child component in props as shown:
handleClose = () => this.props.setShow(false);
In the modal component there is a button that uses this handler in its onClick() synthetic event to trigger the closing of the modal after it has rendered to the browser.
Conversely in the parent component, your button will use the onClick() synthetic event to trigger a call that would be implemented something like this in the button to open the modal:
onClick={this.setShow(true)}
I reuse that process in all of my modals in React.js, hope that helps. The trick here is using componentDidUpdate() or useEffect() (if you're using React Hooks) effectively to make sure you have the right data loaded in state in the parent component so that you can pass it into the props of the child component at the right time. The <ExampleModal /> I gave you should give you enough of a clue.
I have a parent component which contains a child, which in turn contains a md-dialog component. I open the dialog from the component with this:
ParentComponent.vue
<template>
<div>
<ConfirmDialog
:dialogState="isDialogShown"
#closeDialog="value => isDialogShown = false"
/>
<button #click="handleOpenModal()">open modal</button>
</div>
</template>
<script>
export default {
name: 'ParentComponent',
data() {
return { isDialogShown: false }
},
methods: {
handleOpenModal() {
this.isDialogShown = true;
}
}
}
</script>
Button click opens this dialog:
ConfirmDialog.vue
<template>
<md-dialog :md-active.sync="localDialogState">
markup
</md-dialog>
</template>
<script>
export default {
name: "ConfirmDialog",
props: ["dialogState"],
computed: {
localDialogState: {
get() {
return this.$props.dialogState;
},
set() {
this.handleCloseDialog();
}
}
},
methods: {
handleCloseDialog() {
this.$emit("closeDialog");
}
}
};
</script>
I don't really like this because it uses this localDialogState's setter to perform this side effect which only eventually funnels back down to set localDialogState after setting the prop on the parent, however so far I've had to use the setter so I can capture events like backdrop click or ESC keypress that are meant to close the modal.
NB, I have had to use this "local" version of dialog state because apparently props and computed props are all just kept on this object's instance, so the names collide otherwise. There might also be a better way to do this which I am open to hear suggestions on since the above are my first lines of Vue I have ever written.
What is the canonical way of updating these (.sync'd) props in order to catch close events triggered by md-mialog?
Obviously a dialog cannot be used by only one boolean prop. That way you can't distinguish when a dialog was cancelled and when it was confirmed. In more complex cases you may have several confirm buttons with different actions.
I recommend to use a method like open to open a dialog by using $refs in a parent component and close a dialog inside itself with emitting confirmation events that can be catched in a parent component.
So if a user wants to check a checkbox, it will prompt a dialog if he/she have rights to tick-untick. So when he don't have rights, the state will stay the same.
I am using angularjs with kendo treeview.
$scope.options = {
checkboxes: {
checkChildren: true,
},
loadOnDemand: false,
check: onCheck,
dataSource: $scope.treeData,
template: '{{ dataItem.text}}',
schema: {
model: {
children: "List"
}
}
function onCheck(e) {
var currentItem = e.sender.dataItem(e.node);
console.log(currentItem.rights);
//if currentItem.rights= '0001'
//dataItem.checked = true else false (something like this)
}
Kendo UI's events sometime are very poorly designed. Example here: The check event is prevented but it still perform the change, what doesn't happens in the default JS behaviour. What you can do is a rollback on changes:
if (!window.confirm("Do you have the rights to change this?")) {
window.setTimeout(function (node) {
this.expand(node); // expand() creates the child list if not created yet
$(node).find('input[type="checkbox"]').prop("checked", null);
}.bind(this, e.node));
}
Demo
IKR it is ugly, but I can't figure a better way for doing that. The nice thing is that the checkbox user's clicks, is not a real checkbox, it is a span that somehow internally changes the state of a real hidden checkbox and you can't prevent it's default click behaviour! Maybe somebody in their forums could help with a better solution but I'm not sure about that.
UPDATE
In fact there was a logic error in the snippet. The check event should be:
check: function(e) {
let dataItem = this.dataItem(e.node);
if (!window.confirm("Do you have the rights to change this?")) {
window.setTimeout(function (node, currentState) {
this.expand(node); // Expand created the child list if not created yet
$(node).find('input[type="checkbox"]').prop("checked", (currentState ? "checked" : null));
this.dataItem(node).set("checked", currentState);
}.bind(this, e.node, !dataItem.checked));
}
}
Updated demo.
The bug you've reported was happening because it was changing only the element state and not the DataItem's checked property, so internally the widget was acting wierd. Also, there was another bug that, the cancel action was only changing the checkbox to unchecked state and that was wrong, since the user could be cenceling a checked checkbox, so it state has to be kept checked.
We have a backbone program which will listen on a checkbox. When user clicks on the checkbox, it will trigger a function, but the problem is that the event.currentTarget.checked is not correct.
Say, there is a template contains a checkbox:
<input type="checkbox" id="show_user" />
It binds to a backbone view, and have such events:
events: {
"click #show_user": "toggle"
},
toggle: function(event) {
this.model.set("show_user", event.currentTarget.checked);
}
So if I clicked the checkbox to select it, I hope event.currentTarget.checked is true. But when we run karma test on Chrome(with debugger), we clearly see that it is false!
If we keep running the debugger line by line, it will be true after a while.
Why it has such behavior? Is there any other stable way to get the checked status of a checkbox?
Update:
I think I'm missing some important information. When I say click on the checkbox, I mean in the jasmine test, I write:
$("#show_user").click()
In this case, the event parameter in the method toggle is too early received that the state of the checkbox itself has not been changed yet. If I put it in a setTimeout with very short time, say:
toggle: function(event) {
setTimeout(function() {
this.model.set("show_user", event.currentTarget.checked);
}, 1);
}
The event.currentTarget.checked will be correct.
Now I changed it to change:
events: {
"change #show_user": "toggle"
}
And in my test, I won't use .click(), instead, I write:
var checkbox = $("#show_user");
checkbox.prop("checked", !checkbox.prop("checked"));
checkbox.change();
to change the state of the checkbox and trigger a change event. Now it's working well!