MVC-ReactJS button onclick event not get fired - asp.net-mvc-4

I am creating simple stuff seeking for capturing button click event to some text or get some alert. ReactJS JSX code is pasted below:
var SearchBar = React.createClass({
getInitialState: function() {
return {message: "test"};
},
test1: function(e) {
alert('hi');
this.setState({message: "New Message"});
},
render: function() {
var self = this;
return (
<div>
<button onClick={self.test1}>Change Message</button>
{this.state.message}
</div>
);
},
});
I use above SearchBar component in MVC cshtml as:
#Html.React("SearchBar", new{ })
Button get rendered on html page, but unable to change this.state.message value on click event. Where am I doing mistake?

There are two things that need to care about this issue
Add all jsx file uwins Script tag or using bundle in cshtml or in Views\Shared_Layout.cshtml file. e.g.
#System.Web.Optimization.Scripts.Render("~/bundles/main")
Call #Html.ReactInitJavaScript() method after that.
Now Click event surely get work.

Maybe you desire so:
render: function() {
return <div>
<button onClick={this.test1}>Change Message</button>
{this.state.message}
</div>
}
Use this instead self

Related

Call method when modal closes in Vue

I have a Vue app (and I'm relatively new to Vue), anyway I have a generic error modal which is displayed when any of my axios calls fail.
On the modal, I want to be able to retry the failed process when the 'Retry' button is clicked but I'm struggling a bit on how to achieve this. I don't think props will help me as the modal is triggered by
VueEvent.$emit('show-error-modal')
I have managed in my catch to pass the function which has failed by using
VueEvent.$emit('show-error-modal', (this.test));
Then in my modal, I have access to it using
created() {
VueEvent.$on('show-error-modal', (processFailed) => {
console.log('processFailed', processFailed)
this.processFailed = processFailed;
$('#errorModal').modal('show').on('shown.bs.modal', this.focus);
});
}
Using 'F12' it gives
test: function test() {
var _this2 = this;
var page = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
alert('boo');
this.loading = true;
var order_by = {
'ho_manufacturers.name': 4,
'services.servicename': 4
};
axios.post('/test/url', {
id: this.selectedManufacturers,
page: page,
order_by: order_by
}).then(function (response) {
var paginator = response.data.paginator;
_this2.$store.dispatch('paginateProducts', paginator);
_this2.$store.dispatch('setProductPaginator', paginator);
_this2.loading = false;
})["catch"](function (error) {
var processFailed = 'fetchProducts';
_this2.loading = false;
VueEvent.$emit('show-error-modal', _this2.test);
console.log(error);
});
},
I don't think this is the right way of doing it though as all this. are replaced with _this2 as shown above. I need the modal to be generic so I can reuse it but I can't figure it out how to retry the process when clicking the button.
The modals are registered in my app.vue file as
<template>
<div>
<!-- Other code here -->
<app-error-modal />
</div>
</template>
<script>
// Other code here
import ErrorModal from './components/ErrorModal';
export default {
name: 'App',
components: {
// Other code here
appErrorModal: ErrorModal
}
// Other code here
</script>
Modal button HTML
<button ref="retryButton" class="btn btn-success col-lg-2" type="submit" data-dismiss="modal" #click="retryProcess">Retry</button>
Modal script code
<script>
export default {
name: "ErrorModal",
created() {
VueEvent.$on('show-error-modal', (processFailed) => {
console.log('processFailed', processFailed)
this.processFailed = processFailed;
$('#errorModal').modal('show').on('shown.bs.modal', this.focus);
});
},
methods: {
focus() {
this.$refs.retryButton.focus();
},
retryProcess() {
//this.$parent.test(); TRIED THIS BUT DIDN'T WORK
}
}
}
</script>
I'd rather not have to the store.
Use custom event on your component
<div id="app">
<error-modal v-if="isError" v-on:retry-clicked="retry"></error-modal>
<button #click="isError = true">Make Error</button>
</div>
const errorModal = {
template : "<button #click=\"$emit('retry-clicked')\">Retry</button>"
}
new Vue({
el : "#app",
data : {
isError : false
},
components : {
errorModal
},
methods : {
retry : function(){
this.isError = false;
console.log("child component has called parent when retry clicked")
}
}
})
Custom event on component - VUEJS DOC
Everything you have there looks correct. You are passing the function to retry ("test") as "processFailed" - I would call that something different such as retryFn.
Then in your error modal component you just need:
<button #click="processFailed">Retry</button>
Don't worry about what the browser shows you in F12, that is the transpiled Javascript, it will work fine.

Can a vue component know if a listener is listening?

Say I have a modal dialogue as a Vue component. Sometimes I want OK and Cancel. Sometimes, I just want OK. The cleanest way I can think to do this would be for my component to only display Cancel when it's caller is listening for cancel events. Can this be done?
jsfiddle
markup
<div id="vue-root">
<confirm-modal v-on:ok="handleOk"></confirm-modal>
<confirm-modal v-on:ok="handleOk" v-on:cancel="handleCancel"></confirm-modal>
</div>
code
Vue.component('confirm-modal',{
template : `
<div class="confirm-modal">
Are you sure<br>
<button v-on:click="$emit('ok',{})">OK</button>
<button v-if="'HOW DO I TEST IF CANCEL WILL BE CAPTURED???'" v-on:click="$emit('cancel',{})">Cancel</button
</div>
`,
})
vm = new Vue({
el : '#vue-root',
methods : {
handleOk : function(){
alert('OK already');
},
handleCancel : function(){
}
}
})
First you can emit an event without value and the parent will catch it. You dont need this empty Object.
What i understood from your question is this:
If you to track in confirm-modal component if the cancel button is clicked ?
Then do this.
Vue.component('confirm-modal',{
template : `
<div class="confirm-modal">
Are you sure<br>
<button v-on:click="$emit('ok',{})">OK</button>
<button v-if="isCaptured" v-on:click="cancelClick">Cancel</button
</div>
`,
data: function () {
return {
isCaptured: false,
};
},
methods: {
cancelClick: function() {
this.isCaptured = true;
// or this.$emit('cancel'); then pass prop from parent
},
}
})
vm = new Vue({
el : '#vue-root',
methods : {
handleOk : function(){
alert('OK already');
},
handleCancel : function(){
}
}
})
The easy way to do this, you can pass props from parent to child component when you want to show ok and cancel button.
Here you have some more about props
https://v2.vuejs.org/v2/guide/components.html#Passing-Data-with-Props

Show string in component

I am trying to learn Vue.js, and am playing around with the modal component example. I'm trying to change it up a bit so that the button clicked can provide data to the component (I'm very new to this so my terminology may be off).
I've updated the app to be:
// start app
var app = new Vue({
el: '#app',
data: {
showModal: false,
title: 'Default Title'
},
methods: {
modalInit: function(title) {
//this.title = title;
this.showModal = true;
}
}
})
The updates were mainly so that I can change the title within the modal based on the button clicked, here is the update to the button:
<button id="show-modal"#click="modalInit('A title')">Show Modal</button>
The relevant portion of the x-template:
<div class="modal-header">
<h2>{{ title }}</h2>
</div>
Not sure if it matters, but the component is:
Vue.component('modal', {
template: '#modal-template',
})
In this state, the modal will open fine but the title won't be there and I get the console error: [Vue warn]: Property or method "title" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
I can't figure out to to properly "declare reactive data properties in the data option".
Thanks!
You've specified a title property for the root component. But, the modal component does not have a title property. Add it like this:
Vue.component('modal', {
template: '#modal-template',
data() {
return {
title: "Default Title"
}
}
})
If you want to pass in a dynamic value for title, make it a property instead:
Vue.component('modal', {
template: '#modal-template',
props: ['title']
})
Then, you can pass the value for the title in the component tag:
<modal :title="dynamicTitle"></modal>

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>

Vue.js bind to DOM custom event with dots in name (like bootstrap events)

Using Vue 2.1.10
I can bind to DOM events with v-on directive. For example:
v-on:click
To bind to DOM click.
But I can't figure how to bind to an event that has dots in the name. such as "show.bs.modal" from bootstrap.
Currently, I use a workaround binding in the created hook with Regular DOM Methods, but I really would like to use the declarative syntax for that. How can this be achieved? thanks
EDIT:
The question is about allowed syntax: how can I do something like:
Vue.component('comp',{
template:'<div v-on:show.bs.modal="sunrise"></div',
methods:{
sunrise:function(e){
}
}
})
I was facing the very same problem when working on old projects.
Luckily I found the answer here: vue2 doc
<!-- object syntax (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
This works on Bootstrap 5.1.1 with Vue 2.16.14:
<div class="modal" v-on="{ 'hide.bs.modal': handleModalClose }">
...
</div>
I think dots are not supported in v-on but you could create a custom directive to create an event listener for that event.
Not sure if there is something easier but something like in the demo below or this fiddle should work.
The demo creates a new event with dots in name but that should also work with bootstrap events (not tested). Please let me know if it's not working with bootstrap and I'll have a look.
Unbinding only works if you're using v-if. If you're removing that element with Javascript directly. The event will still be there.
var helloEvent = new Event('demo.event.hello');
document.addEventListener('demo.event.hello', function(e) {
// this is just for testing event dispatching!
console.log('main event listener');
}, false);
const bindCustomEvent = {
getName: function(binding) {
return binding.arg + '.' +
Object.keys(binding.modifiers).map(key => key).join('.');
},
bind: function(el, binding, vnode) {
const eventName = bindCustomEvent.getName(binding);
console.log(el, eventName);
document.addEventListener(eventName, binding.value);
},
unbind: function(el, binding) {
const eventName = bindCustomEvent.getName(binding);
console.log('unbinding', eventName);
document.removeEventListener(eventName, binding.value);
}
};
Vue.directive('bindCustomEvent', bindCustomEvent);
new Vue({
el: '#app',
data() {
return {
enabled: true,
eventMsg: ''
};
},
methods: {
sunrise: function(e) {
console.log('received event');
this.eventMsg = 'received event';
},
testEvent: function() {
document.dispatchEvent(helloEvent);
},
toggle: function() {
console.log('toggle', this.enabled);
this.enabled = !this.enabled;
if (!this.enabled) {
this.eventMsg = '';
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
<div v-bind-custom-event:demo.event.hello="sunrise" v-if="enabled">
Hello, {{eventMsg}}
</div>
<!--
The following markup is not working
<div v-on="demo.event.hello:sunrise" v-if="enabled">
Hello, {{eventMsg}}
</div>-->
<button #click="testEvent()">
Change
</button>
<button #click="toggle">
<span v-if="enabled">disable custom event</span>
<span v-else>enable custom event</span>
</button>
</div>