How to create a React component for a bootstrap alert? - javascript

I'm trying to create a React component where I can call a function like showMessage(message, bgColor) and an alert box will pop up that closes automatically after 5 seconds.
I've created many React components in my application but this one is causing me trouble because of having to call alert(), it not always being present, and having a required timer functionality. I just can't think of a "React"ive way of designing the component.
Here is the current jQuery code that I'm using which works but I'm trying to get away from:
$("#alertBox").css('background-color', color);
$("#alertBox").html(message);
$("#alertBox").alert();
$("#alertBox").fadeIn(500, "linear").fadeOut(5000, "linear", function() {
$("#alertBox").alert('close');
});
The problem is React is fundamentally declarative and this code just seems so imperative. In certain event handlers in my codebase I want to be able to just call a function to momentarily display this alert box. If it wasn't for the call to the alert function and fading in/out it wouldn't be so bad (could just do conditional redner). This is the last place in my code I'm still using jQuery which I'm trying to completely neuter from my application.
I'd also rather not use react-bootstrap and react-motion because I just found out about them a couple days ago and this is the last React component I need for my application and rather not rewrite everything now to use those libraries.

How about something like;
<showMessage message={message} bgcolor={bgcolor} hide={hideMessage}/>
then in showMessage render;
render() {
if (this.props.hide) return null;
if (this.state.timerDone) return null;
return (<div id="messageDive" style={whatever}></div>);
}
And you probably need something in componentReceivedProps to reset timerDone and set your timer. Then of course timerDone method to setState({timerDone: true}).

Related

Handling onClick of a <a> tag inside of dangerouslySetInnerHTML/regular html

I am currently writing a reddit client inside of ReactJS plus electron. I am simply doing this to understand apis better and to understand programming a large scale program. However I am becoming stuck when trying to render markdown. I have imported markdown to jsx libraries but none of them (unless I am using them wrong) have allowed me to properly convert the reddit markdown into a component I can work with.
For example from the reddit api, I receive a comment like this:
Hi mi name is John Doe, and I want you guys to check out [Reddit](http://www.reddit.com) Also don't be afraid to check out [Google](http://www.google.com)
Now I have tried using npm modules that convert markdown into jsx, this works and will render the markdown, but I need to be able to interact with the onClick methods. Simple because this is running in an electron window, and navigating the whole browser window will navigate away from my program. Current I have it working with the following code, but this is very slow and calls the webview over 100 times.
...
// On click of an a, this gets called a couple hundred times. :(
componentDidMount() {
var self = this;
$('.linkHandler a').click(function (e) {
e.preventDefault();
// Opens my own custom electron webview
self.props.loadURL(e.target.href)
})}
...
render(){
return(
<div className="linkHandler">
{<ReactMarkdown source={comment.body} />}
</div>
)
}
}
I just want to be able to add an onClick handler to all of the tags in my markdown. Reddit also supplies the html of the markdown already, but alas I am still in the same spot. With the html version I also tried doing the following:
onClick(){
console.log('onClick Called');
}
...
getHTMl(){
// Quickly tpyed this from memory, just keep in mind the original replaced every <a with <a onClick=""
return comment.body_html.replace(/<a/g,'<a onClick="' + this.onClick + '"');
}
...
render(){
return(
<div setInnerHTMLDangerously={{"__html":this.getHTML()}}
)
}
}
Tried adding () to the onClick but it gets called (100s of times) before clicking the button. The way it is now, it returns error: null reference (or something like that) in the console on click.

Add class to dynamically generated HTML

I'm working to use custom checkbox styles with a checkbox which is dynamically generated by javascript for the Google Identity Toolkit. For example, we add this div:
<div id="gitkitWidgetDiv"></div>
And the Google Identity Toolkit script generates new html for that div.
I need to add a class to the HTML which is added by the javascript without any action by the user and I'm struggling to make it work. For example, here is my code:
$("#gitkitWidgetDiv").on('ready', ".gitkit-sign-in-options label", function() {
$(this).addClass('checkbox');
});
I've tried switching 'ready' for a few other options and also using the livequery plugin, but nothing is working for me. It works if I use an active event like 'click,' but I can't figure out how to do this when the page loads. Could someone please help? Thanks!
Modern browsers (including IE11) support mutation obervers. You can use one to monitor the parent node of the div that will be added. When the div has been added, just add the class.
Here's something I made which comes in handy in annoying cases like this where it's difficult to tell when the element you need has finished loading in: https://gist.github.com/DanWebb/8b688b31492632b38aea
so after including the function it'd be something like:
var interval = 500,
stopTime = 5000,
loaded = false;
setIntervalTimeout(function() {
if($('.dynanicElementClass').length && !loaded) {
$('.dynanicElementClass').addClass('checkbox');
loaded = true;
}
}, interval, stopTime);
It's not perfect and I'm sure there are better solutions out there but in most cases like this it does the job.

JQuery Mobile Custom Flipbox set initialization value

I have a custom flipbox which is described in the accepted answer here: JQuery Mobile Custom Flipbox get selected value.
I'm struggling to set the initial value after the page is rendered my 'change-page' function is something like this:
changePage:function (page) {
page.$el.attr('data-role', 'page');
page.render();
$('body').append(page.$el).trigger('create');
$.mobile.changePage(page.$el, {changeHash:false});
}
As I use Backbone.js to manage the views and then I delegate the navigation to jQM.
I basically need to set the initial value of this input text field ( maybe there's a workaround)
Ok I figured this out myself and I'm willing to share the solution:
first of all the change page function is slightly different:
changePage:function (page) {
page.$el.attr('data-role', 'page');
//get rid of everything in the old page.
page.remove();
//render the page again
page.render();
$('body').append(page.$el).trigger('create');
$.mobile.changePage(page.$el, {changeHash:false});
}
the remove call is necessary to get rid of every event listener you had in the old rendered HTML.
In every page that needs init parameters you can specify in the render function this:
render: function(){
....
this.$el.on('pageshow',this.initFields);
....
}
initFields: function(){
// put your jQuery code here (e.g. this.$el.find('foo').val('initValue');
}
please note that this solution as of the jQM documentation is valid up to the 1.5.0 version of jQM, after that the pageshow event will not be triggered anymore.

Programmatically activating a custom TinyMCE button

I've got a lot of custom buttons on my TinyMCE toolbar, most of which open a dialog box with some further options in when you click them. This all works fine.
Here is an example of something in my tinyMCE_setup() function:
ed.addButton('link2', {
title: '{!link!}',
image: '../style/common/images/link_20x20.png',
onclick: function() {
replyBoxDialog('link', ed);
}
});
However, I want to be able to call these programatically, and faking a .click() on the button with jQuery won't cut it.
I've tried calling the function directly
replyBoxDialog('link',tinyMCE);
But no matter what I try as the second argument, I can't get the right object (so it fails when it's time to insert something into the editor, as it doesn't know what the editor is).
I've also had a try with various execCommand() calls, but I've no idea what to put in there.
Any clues?
All you have to do is to use a real editor object as paramter
var editor_instance = tinymce.activeEditor; // in case you just use one editor
var editor_instance = tinymce.get('my_special_editor_id'); // in case you have more than one editor
replyBoxDialog('link', editor_instance);
I've managed to make it work by creating a variable 'globalEd' at the top of the script and adding globalEd = ed; to tinyMCE_setup(), then I can call replyBoxDialog('dragndrop', globalEd);. This seems like a properly hacky way of doing things though, so I'd welcome any further advice.

TinyMCE's isDirty method

I have a card-making web project. I have a tinyMCE textfield and a visual of the card. Upon changing the content of the tinyMCE editor I want to update the visual of the card to reflect the new text/changes.
TinyMCE comes with an IsDirty method:
if (tinyMCE.activeEditor.isDirty())
alert("You must save your contents.");
What I don't understand is WHERE I would place this if statement to regularly check for it. I understand that JS is event driven and so it needs to be "called", do I call it every keypress?
You could add a global timeout function that every second or so (the interval is up to you) checks:
function updateCardIfDirty() {
if (tinyMCE.isDirty()) {
// rerender the card
}
}
setInterval(updateCardIfDirty, 2000); // check every 2 seconds.
A cleaner solution might be to check every time they make a change in the tinyMCE editor. This can be made possible by the onChange() event tinyMCE provides, as follows:
tinyMCE.init({
...
setup : function(ed) {
ed.onChange.add(function(ed, l) {
// rerender the card
});
}
});
The downside of the first approach is that it will execute every 2 seconds, even if they dont edit the card for an hour. The downside of the second approach is that if they perform 10 edits in 1 second, it will rerender the card 10 times in that second.
So finally, let's try a third approach which gets the best of both worlds, and loses both disadvantages we mentioned:
tinyMCE.init({
...
setup : function(ed) {
ed.onChange.add(function(ed, l) {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(function(){ timeout=null; rerenderCard(); }, 1000);
});
}
});
I am currently trying to see how to fix TinyMCE in this regard. Outside of TinyMCE, the current cross browser solution is to use the OnTextChange DOM event that is supported by most of the current browsers. I test it today and it works really nice for non-keyboard right click cut/paste/delete and also keyboard changes. No Keyboard events necessary with the textchange event. (Note, it needs to be prepared in DOM, google the jquery.textchange.js plugin).
However, it doesn't work when TinyMCE is wrapping the textarea control. I am currently looking at the source and the plugins to see how to implement this textchange event.

Categories