React Motion: simple chaining - react-motion

I'm using react, mobx and I need to animate things.
I want to do a simple animation chaining: when the first animation ends, the second starts.
As use case think of a section templates: every time the user change the section, the current section fade out and then the next section fade in.
Any hints?

Regardless react-motion or any other library, you can always use onAnimationEnd animation event listener .
With this method you are able to detect when css animation ends on related html element and execute method function.
Then you can choose what to do , for example using setState():
animation1 = () => {
this.setState({
animation1: true
})
}
animation2 = () => {
this.setState({
animation2: true
})
}
animation3 = () => {
this.setState({
animation3: true
})
}
render() {
return (
<div>
<div onAnimationEnd={this.animation1}>
First animation
</div>
<div onAnimationEnd={this.animation2}>
Second animation
</div>
<div onAnimationEnd={this.animation3}>
Third animation
</div>
</div>
)
}

Related

How do I smoothly reduce a divs height to 0 in VueJS?

I've tried and failed for hours now. The way I've done it is in onMounted() I first div = document.GetElementByID. Then, div.style.height = '0';. However, since this is being run from inside onMounted(), it doesn't animate or transition it, even though I have all the necessary Tailwind classes for it to do so (note that it successfully resizes the div to 0 height, but there's no animation or transition). I even tried to put the div.style.height = '0' into a seperate function outside onMounted() which didn't work. The only thing that animates or transitions is opacity. And only that. Why? Why not height or anything else? What do I have to do to make it work?
<template>
<div v-if="props.state == 2" class="w-full bg-[#E7685D] text-black text-2xl overflow-hidden duration-500 ease-in-out" id="alert">
<p class="text-center p-2">text</p>
</div>
</template>
<script>
import { onMounted } from '#vue/runtime-core'
onMounted(() => {
const alert = document.getElementById('alert');
setTimeout(() => { alert.style.height = '0%', 5000 })
})
const props = defineProps({
state: String
})
</script>
Maybe that's not animating because of your wrong syntax for setTimeout function. setTimeout(() => { alert.style.height = '0%', 5000 }). You've to define milliseconds out of the function brackets {...} in case you want a 5 seconds delay before execution of the setTimeout function. Given below is the right syntax for the setTimeout function.
Syntax: setTimeout(function, milliseconds, param1, param2, ...)
setTimeout(() => {
alert.style.height = '0%'
}, 5000)
Edited:
I'm not sure! Maybe adding the transition/animation classes in the setTimeout will do the trick.
setTimeout(() => {
alert.classList.add("duration-500", "ease-in-out")
alert.style.height = '0%'
}, 5000)
If it didn't work, try moving lines up/down in the setTimeout function.

Vuejs conditional rendering v-if

I have this part of code wherein in my view theres a condition if I want to show it or not
<div v-if="toShow" ref="target"></div>
and in my javascript code I trigger toShow to true
this.toShow = true
this.$refs.target // always null
but when I use setTimeout() the value is not null
I need a solution wherein I dont want to use setTimeout() because I'm toggling toShow everytime for my transition so what happens is a have a lot of nested setTimeout() in my code.
You can use $nextTick which waits until the next DOM update cycle. It should be much better than setTimeout because it gets called quickly after the DOM updates rather than a specific time later.
I've created a fiddle below showing it working.
new Vue({
el: "#app",
data: () => {
return {
show: false
};
},
methods: {
toggleShow() {
this.show = !this.show;
console.log(this.$refs.target);
this.$nextTick(() => {
console.log(this.$refs.target);
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="toggleShow">Toggle Show</button>
<div v-if="show">
<h5>Showing</h5>
<div ref="target"></div>
</div>
</div>

Vue update variable and refresh in another component?

I have form component and in catch part I push errors tu my StatusBar component:
}).catch(error => {
StatusBar.methods.handleErrors(error.response.data)
})
In my StatusBar component I want to show this errors like:
handleErrors(newErrors) {
this.errors = [];
if (newErrors.errors) {
for (var i in newErrors.errors) {
this.errors.push(newErrors.errors[i][0]);
}
}
}
I tried in view area to add:
<div class="alert alert-danger" v-for="error in this.errors">{{ error }}</div>
The problem is in console output I see the correct result, but in the view area is nothing updated. I tried also with v-model but nothing was updated.
How can I push results from loop automatically/update to alert?

Handle Bootstrap modal hide event in Vue JS

Is there a decent way in Vue (2) to handle a Bootstrap (3) modal hide-event?
I found this as a JQuery way but I can't figure out how to capture this event in Vue:
$('#myModal').on('hidden.bs.modal', function () {
// do something…
})
Adding something like v-on:hide.bs.modal="alert('hide') doesn't seem to work.
Bootstrap uses JQuery to trigger the custom event hidden.bs.modal so it is not easily caught by Vue (which I believe uses native events under the hood).
Since you have to have JQuery on a the page to use Bootstrap's native modal, just use JQuery to catch it. Assuming you add a ref="vuemodal" to your Bootstrap modal you can do something like this.
new Vue({
el:"#app",
data:{
},
methods:{
doSomethingOnHidden(){
//do something
}
},
mounted(){
$(this.$refs.vuemodal).on("hidden.bs.modal", this.doSomethingOnHidden)
}
})
Working example.
Please see https://bootstrap-vue.js.org/docs/components/modal#overview
There you can find event "hide" or "hidden"
So you can bind this event:
<b-modal ref="someModal" #hide="doSometing">
One option is to tie it to a variable:
data: function(){
return {
showModal: false
//starts as false. Set as true when modal opens. Set as false on close, which triggers the watch function.
},
watch: {
showModal: function(){
if(this.showModal == false){
// do something
},
}
HTML
<button id="show-modal" #click="showModal = true">Show Modal</button>
//later if using a component
<modal v-if="showModal" #close="showModal = false">
// or alternatively in the bootstrap structure
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" #click="showModal = false">Close</button>
</div>
This may be late but another way if you are using a custom modal component (Modal.vue) you have created is to
create a method in mounted to catch the event of closure (doesn't have to be the same name as below)
mounted: function(){
this.triggerHidden();
}
create the method
methods: {
triggerHidden: function(){
var self = this;
if( $('#getModal').length ){
$('#getModal').on('hidden.bs.modal', function(){
//catch the native bootstrap close event and trigger yours
self.#emit('modal-close');
});
}
}
}
now call use your custom event with your custom/reusable modal component
<custom-modal #modal-close="doSomething"></custom-modal>
The method doSomething will be called when the modal closes. You can also use the approach to hijack the other jquery event so its a little more manageable.
Maybe creating a Custom Vue Directive can help:
Vue.directive('bsevent', {
bind: function bsEventCreate(el, binding, vnode) {
let method = binding.value || (() => { });
$(el).on(binding.arg.replaceAll(/_/g, "."), (event) => { method(event); });
},
unbind(el, binding) {
$(el).off(binding.arg.replace(/_/, "."));
},
});
And then just use it on the element you wish (this example is on a bootstrap collapsible, but you could use it to any other bootstrap event):
<div id="myCollapsible" class="collapse" v-bsevent:hidden_bs_collapse="methodToCall">
...
</div>
The only thing to remember is to register the event with underscores instead of dots (show.bs.modal => show_bs_modal).
If working with bootstrap-vue then below code snippet will be helpful:
export default {
mounted() {
this.$root.$on('bv::modal::hide', (bvEvent, modalId) => {
console.log('Modal is about to be shown', bvEvent, modalId)
})
}
}
for other events please refer to the official docs.
Just use native addEventListener (Vue 3, Composition API)
template:
<div ref="modalElement" class="modal">
...
</div>
script:
import { Modal } from "bootstrap"
import { onMounted, ref } from "vue";
const modalElement = ref(null)
let modal = null;
onMounted(() => {
modal = new Modal(modalElement.value)
modalElement.value.addEventListener("hidden.bs.modal", onHidden)
})
function onHidden() {
// do something…
}
We can also use this simple approach like this example
<template>
<div>
<button #click="openModal = true">Open Modal</button>
<div v-if="openModal">
<div class="modal-background"></div>
<div class="modal-content">
<button #click="openModal = false">Close Modal</button>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
openModal: false
}
}
}
</script>

REACT - defaultChecked don't render check attribute on second load

I got my component who won't check the radio when i go to the /view/:id for the second time. I started in my list component with react-router at the index of the site, i click on the view button of an element, the radio is checked, i return in my list and go to another or the same element and it's not checked anymore. When i inspect the component in the React developer tool, the radio has the defaultChecked=true property.
import React from 'react';
import { connect } from 'react-redux';
class LicenseRadios extends React.Component {
buildRadios() {
let { licenses, activeValue } = this.props;
return licenses.map(license => {
let checked = false;
if(activeValue !== undefined && activeValue === license.id){
checked = true;
}
return (
<div key={license.id} className="col l2">
<p>
<input name="license" type="radio" id={'licenseRdo_' + license.id} value={license.id} defaultChecked={checked} />
<label htmlFor={'licenseRdo_' + license.id}>{license.label}</label>
</p>
</div>
);
});
}
render() {
return (
<div className="row">
{this.buildRadios()}
</div>
);
}
}
export default LicenseRadios;
I tried to change the defaultChecked for the checked attribute, but it require an onChange event. I don't understand this problem. Can anybody help me please?
Thank you
The defaultChecked prop is only used during initial render. If you need to update the value in a subsequent render, you will need to use an onChange function to handle value changes.
Check out controlled components in the docs to better understand the problem you're having.
use "undefined" for initial value for defaultChecked and re-render by setting it to true or false
const Example = () => {
[checked,setChecked] = useState(undefined);
useEffect(()=>{
// fetch data
setChecked(true);
});
return (
<input type="checkbox" defaultChecked={checked} onClick={(e)=> changeValue(e)}/>
);
}