React js and bootstrap modal from examples on github - javascript

I'm trying to set up an app with react and everything is going well except for my modal. I've used this code from the following link, untouched and I get errors. https://github.com/facebook/react/blob/master/examples/jquery-bootstrap/js/app.js
Try this fiddle http://jsbin.com/eGocaZa/1/edit?html,css,output
The callback functions don't seem to have access to "this". If you log "this" in the console, it logs the window object.
openModal: function() {
this.refs.modal.open();
},
I did pass in this and return a new function which seemed to work but that didn't seem right and not playing nice with jsfiddle. I got the modal firing locally but then I run into the same issue with the close function. Any help would be appreciated. Thanks!
var Example = React.createClass({
handleCancel: function() {
if (confirm('Are you sure you want to cancel?')) {
this.refs.modal.close();
}
},
render: function() {
var modal = null;
modal = (
<BootstrapModal
ref="modal"
confirm="OK"
cancel="Cancel"
onCancel={this.handleCancel}
onConfirm={this.closeModal}
title="Hello, Bootstrap!">
This is a React component powered by jQuery and Bootstrap!
</BootstrapModal>
);
return (
<div className="example">
{modal}
<BootstrapButton onClick={this.openModal(this)}>Open modal</BootstrapButton>
</div>
);
},
openModal: function(obj) {
return function(){obj.refs.modal.open();}
},
closeModal: function() {
this.refs.modal.close();
}
});

I found a few problems with your code:
You were loading the Bootstrap JS before jQuery but it needs to be loaded after.
You were using React 0.3.0, which had different scoping rules for component methods -- since React 0.4, methods are bound to the component automatically. You could have written openModal: React.autoBind(function() { this.refs.modal.open(); }) or onClick={this.openModal.bind(this)} in React 0.3 but upgrading to 0.4 removes the necessity to bind manually.
Your modal had the hide class which seemed to make it invisible; I removed it and now the modal seems to appear. I'm not sure at the moment why this behaves differently between your code and the example.
Here's my working example jsbin . The modal appears to have some strange CSS applied to it but I don't think it's React-related so I'll leave you here. Let me know if anything's unclear.

Of course I worked on this all night and then figure the answer out after I ask a question here but here's the solution.
The functions needed to be wrapped in the autoBind to access "this". Here are the functions affected...
close: React.autoBind(function() {console.log(this);
$(this.getDOMNode()).modal('hide');
}),
open: React.autoBind(function() {
$(this.getDOMNode()).modal('show');
}),
...
handleCancel: React.autoBind(function() {
if (this.props.onCancel) {
this.props.onCancel();
}
}),
handleConfirm:React.autoBind(function() {
if (this.props.onConfirm) {
this.props.onConfirm();
}
})
...
openModal: React.autoBind(function() {
this.refs.modal.open();
}),
closeModal: React.autoBind(function() {
this.refs.modal.close();
})

Related

Submit a form from a Modal in Vue js

I need to send a form from a modal. Not using a full Vue app, but inserting Vue.js in my HTML page.
I tried a lot of unsuccesful things with my current modal, so I reduced it to the basic modal example I used for the first time https://v2.vuejs.org/v2/examples/modal.html
For the form, I used also the most basic form validation example at https://v2.vuejs.org/v2/cookbook/form-validation.html (I have it working in other places).
And I have created this unsuccessful fiddle:
https://jsfiddle.net/JIBRVI/03qnok9m/53/
Vue.component('modal', {
template: '#modal-template'
})
// start app
// eslint-disable-next-line no-new
new Vue({
el: '#app',
data: {
showModal: false,
errors: [],
name: ''
},
methods: {
checkForm: function (e) {
if (this.name) {
return true
}
this.errors = []
if (!this.name) {
this.errors.push('Name required.')
}
e.preventDefault()
}
}
})
In the basic modal example I added the form with a field, a submit button and a placeholder to show errors. Also the input field «name» and the array «errors» to the data section in the app. I also added the «checkForm» method.
The main error says:
Property or method "checkForm" is not defined on the
instance but referenced during render. Make sure that this property is
reactive, either in the data option, or for class-based components, by
initializing the property
Maybe the main page can communicate with the modal, so data and methods from the main page can’t be used.
I also tried to make a component with the form, but it didn’t work either. I can’t communicate with the modal.
Any help will be aprreciated.
You need to move the name and checkform methods to the modal component. You have currently defined those two in the app component and are trying to access it from the modal which is in the modal component.
Vue.component('modal', {
template: '#modal-template',
data(){
return {
name: '',
errors: [],
}
},
methods: {
checkForm: function (e) {
if (this.name) {
return true
}
this.errors = []
if (!this.name) {
this.errors.push('Name required.')
}
e.preventDefault()
}
}
})

Electron global shortcut to toggle show/hide of menubar

I am trying to add a global shortcut to my Electron app that will toggle showing/hiding it. My app is a menubar app built using maxogden/menubar and React.
I have the following code. I've left a couple of bits out just for brevity but this is how I have setup the global shortcuts.
I think it's important to note one of the tips on the maxogden/menubar Readme too:
Use mb.on('after-create-window', callback) to run things after your
app has loaded
const { globalShortcut } = require('electron');
const keyboardShortcuts = {
open: 'CommandOrControl+Shift+g',
close: 'CommandOrControl+Shift+g'
}
menu.on('after-create-window', () => {
globalShortcut.register(keyboardShortcuts.open, () => {
menu.window.show();
});
});
menu.on('after-show', () => {
globalShortcut.unregister(keyboardShortcuts.open);
globalShortcut.register(keyboardShortcuts.close, () => {
menu.window.hide();
});
});
menu.on('focus-lost', () => {
globalShortcut.unregister(keyboardShortcuts.close);
globalShortcut.register(keyboardShortcuts.open, () => {
menu.window.show();
});
});
Once the menubar has first been opened, my shortcut is registered and will work to show the app. However, the code I've implemented to unregister the shortcut, and re-register it to hide the app (when showing), doesn't seem to work.
I'm not sure if my code to reregister the shortcut is setup within the right event handler i.e after-show and focus-lost. I have a feeling that these event handlers I'm working within are related directly to my menu rather than menu.window. This would explain why the reregistration of the shortcut isn't happening, but I'm not sure.
Does anyone have any idea how I would sensibly set up a global shortcut toggle to open/close my menubar app?
From the menubar docs (https://github.com/maxogden/menubar) the menubar instance exposes the following methods:
{
app: the electron require('app') instance,
window: the electron require('browser-window') instance,
tray: the electron require('tray') instance,
positioner: the electron-positioner instance,
setOption(option, value): change an option after menubar is created,
getOption(option): get an menubar option,
showWindow(): show the menubar window,
hideWindow(): hide the menubar window
}
Using menu.showWindow() & menu.hideWindow() instead of menu.window.show() & menu.window.hide() will work.
I would further suggest that you use the built in events to manage your state, simplifying your code and implementation:
const { globalShortcut } = require('electron');
let isShown = false;
menu
.on('after-show', () => { isShown = true })
.on('after-hide', () => { isShown = false })
.on('focus-lost', () => { isShown = false });
globalShortcut.register('CommandOrControl+Shift+g', () => {
isShown ? menu.hideWindow() : menu.showWindow()
});

ReactJS window.addEventListener("hashchange") not working

I have a React component called Home. Within Home, I have a function "urlListener" in which I have added an event listener to alert whenever the URL is being changed.
var Home = React.createClass({
getInitialState: function () {
return {
openedFile: this.props.location.query.file || '',
apps: [],
showNav: this.props.location.query.file ? false : true,
layout: 'row',
cloneAppName: 'New Application',
appName: 'Application Name',
showSave: false
}
},
urlListener: function(){
window.addEventListener("hashchange", function(){
saveUnsaved();
});
},
saveUnsaved: function(){
}
The listener works fine and is being called whenever there is a change in the URL. However, the console says that the function I'm trying to call is not a function. I am new to React and any help on this would be appreciated.
Someone had commented the answer which seemed to work for me, but the comment was removed before I could accept the answer. The correct way of doing it is
{() => this.saveUnsaved();}
The arrow function apparently switches the context from "window" to "this"(the current component) internally. Without the arrow function, "this" would be referring to the "window" component.
Works perfectly for me.
// ES6:
componentDidMount() {
window.addEventListener('popstate', this.handleOnUrlChange, false)
}
componentWillUnmount() {
window.removeEventListener('popstate', this.handleOnUrlChange, false)
}
handleOnUrlChange = () => {
// your code
}

vue is not defined on the instance but referenced during render

I'm trying to build a simple app in vue and I'm getting an error. My onScroll function behaves as expected, but my sayHello function returns an error when I click my button component
Property or method "sayHello" is not defined on the instance but
referenced during render. Make sure to declare reactive data
properties in the data option. (found in component )
Vue.component('test-item', {
template: '<div><button v-on:click="sayHello()">Hello</button></div>'
});
var app = new Vue({
el: '#app',
data: {
header: {
brightness: 100
}
},
methods: {
sayHello: function() {
console.log('Hello');
},
onScroll: function () {
this.header.brightness = (100 - this.$el.scrollTop / 8);
}
}
});
I feel like the answer is really obvious but I've tried searching and haven't come up with anything. Any help would be appreciated.
Thanks.
But for a few specific circumstances (mainly props) each component is completely isolated from each other. Separate data, variables, functions, etc. This includes their methods.
Thus, test-item has no sayHello method.
You can get rid of the warning by using .mount('#app') after the Vue instance rather than the el attribute.
Check the snippet below;
var app = new Vue({
data: {
header: {
brightness: 100
}
},
methods: {
sayHello: function() {
console.log('Hello');
},
onScroll: function () {
this.header.brightness = (100 - this.$el.scrollTop / 8);
}
}
}).mount('#app');
Please note; the following might not be necessary but did it along the way trying to solve the same issue: Laravel Elixir Vue 2 project.

Reactjs - what is the workflow to force a refresh?

I'm just trying to create a react component wrapping the CodeMirror (4.1) editor.
I came across this problem for which there is a workround via forcing a refresh once the component has loaded, but I'm not quite sure of the workflow I need to achieve this when react is added into the picture.
The suggestion is that to overcome the error I would need to
"Call .refresh() after resizing the wrapping container."
My code is currently as follows in the Editor component:
function ($, React, CodeMirror) {
return React.createClass({
render: function () {
console.log("render-editarea");
return (
<textarea id="editarea">
-- Comment here
USE [All Data Items];
SELECT ID FROM [Test Event]
</textarea>
)
},
componentDidMount: function () {
var onExecute = this.props.onExecute;
var editorNode = document.getElementById("editarea");
console.log("componentDidUpdate-editarea:" + editorNode);
var editor = CodeMirror.fromTextArea(editorNode, {
lineNumbers: true,
matchBrackets: true,
indentUnit: 4,
mode: "text/x-mssql",
extraKeys: {"Ctrl-E": function(cm) {
console.log(editor.getValue());
onExecute(editor.getValue());
}}
});
},
and it is loaded via the Render function of the parent component
I have tried
hooking the window resize event (as shown in the React manual) in
the editor component.
forcing a refresh in the parent component's componentDidMount
function using $("#editarea").refresh();
but neither of these appeared to work
So I'd be grateful if someone could show me the right way to do it.
Many thx
Use the ref attribute to reference rendered nodes rather than IDs or DOM selectors:
function ($, React, CodeMirror) {
return React.createClass({
render: function () {
console.log("render-editarea");
return (
<textarea ref="editarea">
-- Comment here
USE [All Data Items];
SELECT ID FROM [Test Event]
</textarea>
)
},
componentDidMount: function () {
var onExecute = this.props.onExecute;
var editorNode = this.refs.editarea;
console.log("componentDidUpdate-editarea:" + editorNode);
var editor = CodeMirror.fromTextArea(editorNode, {
lineNumbers: true,
matchBrackets: true,
indentUnit: 4,
mode: "text/x-mssql",
extraKeys: {"Ctrl-E": function(cm) {
console.log(editor.getValue());
onExecute(editor.getValue());
}}
});
},
So this post helped me. The .refresh() was a function on CodeMirror which I hadn't fully understood. I used the method as suggested in that post in the parents componentDidLoad event.
componentDidMount: function () {
$('.CodeMirror').each(function(i, el){
el.CodeMirror.refresh();
});
},

Categories

Resources