inject content from one react component to another onClick - javascript

OK,so i am starting to get my head around ReactJs but keep getting stumped by one simple concept that is a doddle with plain old jQuery.
I want to be able to add to the content of one element on the screen when an on click event happens upon another. Following the react tutorial i completely understand the way they have achieved the adding to the comments list, the comment list is a child of the parent which is setting the state.. but surely this cannot be the only way as it feels very rigid and inflexible.
Here is a simple mockup of what I am trying to explain. On click of the button, i want to inject new content into the div with id "newComments"..
JSBin: http://jsbin.com/vokufujupu/1/edit
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/JSXTransformer.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx">
var InputBox = React.createClass({
clickHandler: function(){
//Append a new string to the end of the existing copy in the copy box
alert('after this alert the value of the button should be appended to the content of the div#newComments');
},
render: function() {
return (
<div classNmae="copyBox">
<input type="button" name="inputButton" value="click me button" className="bbbc"
onClick={this.clickHandler} />
</div>
);
}
});
var CopyBox = React.createClass({
render: function() {
return (
<div classNmae="copyBox">
<p>div#newComments:</p>
<div id="newComments"></div>
</div>
);
}
});
var Page = React.createClass({
render: function() {
return (
<div>
<CopyBox/>
<InputBox/>
</div>
);
}
});
React.render(
<Page/>,
document.getElementById('content')
);
</script>
<!-- The equiv in plain old js -->
<script type="text/javascript">
function newContent(obj){
document.getElementById('vanBox').innerHTML = document.getElementById('vanBox').innerHTML + obj.value;
}
</script>
<div id="vanilaJsEquiv">
<div id="vanBox"></div>
<input type="button" value="ClickyClik" onclick="newContent(this)"/>
</div>
</body>
</html>
I've been hunting around google and the docs for yonks and cannot find the answer..

In react there is no concept of manipulating HTML / DOM. React is responsible just for rendering based on component state. Every component renders whatever it's current state is.
So you need to manipulate the state of other component. For that Facebook is using Flux. Which is a bit more complex workflow, but once you get it, it is actually pretty simple concept.
On one component click you dispatch an action. That action will trigger event, stores that are subscribed to that event will react and update internal state. After update, store emits change event, all components listening for changes in that store will update.
Yes you will need to write a lot more code. It gets much simpler if component is manipulating it's own state, then it would be enough to just call this.setState({ ... }) inside the component. And yes there, are other ways to do this.

Related

load react script inside text/template script

I'm trying to contribute to a project that uses <script type="text/template"></script> for rendering the elements of a page.
My contribution is to change the elements of the page into react components. However when I order the react to render in a specific div with ReactDOM.render() I get an error saying
Uncaught Error: _registerComponent(...): Target container is not a DOM element.
I know that means that react doesn't find the div where to render so propably I will need to load the react script after the div, I've tried that but the error is there again.
I've tried loading the react script otherwise like this
<script type="text/template">
<ul class="new-component-template" id="xblocklist" >
</ul>
<script type="text/babel" src="path to react file"/>
</script>
but when I load the page the error is gone an the script is not loaded at all.
What I need is to load the script when the outer script is called, I.E can I write a function inside <script type="text/template"></script> that actually loads the react script inside.
UPDATE
var ListXblocks = React.createClass({
componentWillMount: function(){
this.setState({Problemstyle: this.props.Problemstyle})
fetch('http://192.168.33.10:8002/XBlocks/')
.then(result=>result.json())
.then(result=> {
this.setState({items:result})
})
},
render:function(){
return(
<div>
{
this.state.items.map((item)=>{return(
<li className="editor-manual" key={item.title}>
<button type="button" className="button-component" data-category="problem" data-boilerplate="">
<span className="name">{item.description}</span>
</button>
</li>)})
}
</div>
);
}
});
ReactDOM.render(<ListXblocks/>, document.getElementById('xblocklist'));
Script tag with type="text/template" doesn't do anything particularly and it just let browser to ignore what inside it. This approach usually uses by templating systems like handlebarjs and React doesn't support it. You can read more about this here. So if you put your React scripts also inside that, the browser is just going to ignore that as well.
Beacuse your ul tag is not a html element, document.getElementById('xblocklist') is going to return null. That's why you get "Target container is not a DOM element." error. So you have to get the html out of the script tag either manually or using JavaScript.

How can I render MathJax when some element is clicked?

I have an input field in which I type LaTeX commands which must be rendered as MathJax when I click a button. I use the textContent property to set the content of the div element to whatever I type in the input field. However, the browser doesn't render MathJax and shows the content of div as LaTeX commands. I think this is probably because MathJax is rendered when the document is loaded. Is there some way by which I can render MathJax when the button is clicked? I have no knowledge of jquery or any other JavaScript library. Is there some way of doing this without using them or is it necessary to use them?
<head>
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML">
<!-- The source is used to render mathjax -->
function dispMJ() {
var a = document.getElementById("mj").value;
document.getElementById("disp").textContent = a;
}
</script>
</head>
<body>
<input type="text" id="mj">
<input type="button" value="render MJ" onclick="dispMJ();">
<div id="disp"></div>
</body>
you should use the QUEUE to tell MathJax to rerender the div, Look a full example here http://codepen.io/damianfabian/pen/EgWEpv?editors=1000
UpdateMath = function () {
var TeX = document.getElementById("MathInput").value;
QUEUE.Push(["Text",math,"\\displaystyle{"+TeX+"}"]);
}

Travel to outer component until reaching the one that has a specific attribute

Example html code (Looks weird but my intention is to show that the outer components can be anything)
<div>
<div/a/p/.. myAttribute="123">
<p>
<a>
<script type="text/javascript">
jQuery.ajax({url: "local.host?attribute= "});
</script>
</a>
</p>
</div/a/p/..>
</div>
What I want is that, from the inner script, before the ajax call is fired, I want to travel to outer parent components, till I can reach the component that has myAttribute. Then I will take the value of myAttribute and fill it into my url as "local.host?attribute= 123"
I found the closest of JQuery, but it requires to know the component type in advanced.
Any help is appreciated.
Thank you very much.
I think '*[myAttribute]' will do it fine.
$(el).parents('*[myAttribute]');
To target the current script. you can use
document.currentScript;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<div>
<div/a/p/.. myAttribute="123">
<p>
<a>
<script type="text/javascript">
var self = document.currentScript,
e = $(self).closest('*[myAttribute]'),
attrVal = (e.attr('myAttribute') );
console.log( attrVal );
// commented
// jQuery.ajax({url: "local.host?attribute="+attrVal });
</script>
</a>
</p>
</div/a/p/..>
</div>

Knockout multiple bindings on nested dom elements

I've managed to get myself into a bit of trouble with a project I'm working on.
Originally the site has one page on it that uses Knockout, with the other pages using jQuery. Due to some problems with the Foundation modal placing itself in the root of the body element, I ended up applying the bindings for the viewmodel for this page to the body element.
Fast forward 4 months, and without foreseeing the trouble I'm in now, I went and rebuilt our shopping basket in Knockout. The shopping basket is visible on every page and is included using a ZF2 partial.
Going back to the page I worked on 4 months ago, it is completely broken with the error message in console saying:
Uncaught Error: You cannot apply bindings multiple times to the same element.
Here's some code to show my layout:
<html>
<head>
<title>My Website</title>
</head>
<body> // 4 month old SPA bound here
<nav>
<div id='shopping-basket'> // Shopping basket bound here
...
</div>
</nav>
<div id='my-app'>
...
</div>
</body>
</html>
JavaScript:
var MyAppViewModel = function() {
// logic
};
var ShoppingBasketViewModel = function() {
//logic
};
ko.applyBindings(new MyAppViewModel(), document.body);
ko.applyBindings(new ShoppingBasketViewModel(), document.getElementById('shopping-basket');
If I had the time I could go back and rework the original application to sit within it's own div container that would site side by side with the basket, but unfortunately this isn't an option.
The other option is to discard the last bit of work I did on the shopping basket and replace it with jQuery, but this would mean losing a weeks worth of work.
Is there anyway when I'm applying the bindings that I could have both viewmodels working side by side, while being nested in the Dom, and remaining independent of each other?
I had some similar problem. I needed to applybinding to specific nested elements and start with a bind on the document first. Same problem. My solution was to add some ignore element part and than bind the specific element manually.
1) Add a custom binding so you can skip binding on the specific shopping basket:
ko.bindingHandlers.stopBinding = {
init: function() {
return { controlsDescendantBindings: true };
}
};
ko.virtualElements.allowedBindings.stopBinding = true;
2) Add the custom binding in your html (surround your shopping basket):
<html>
<head>
<title>My Website</title>
</head>
<body> // 4 month old SPA bound here
<nav>
<!-- ko stopBinding: true -->
<div id='shopping-basket'> // Shopping basket bound here
...
</div>
<!-- /ko -->
</nav>
<div id='my-app'>
...
</div>
</body>
3) Apply your bindings as you already do:
ko.applyBindings(new MyAppViewModel(), document.body);
ko.applyBindings(new ShoppingBasketViewModel(), document.getElementById('shopping-basket');
4) The first bind will skip the shopping-basket because of your custom binding handler and your second bind will explicitly bind the shopping-basket.
I haven't tested the code above on your specific example, but it should point you into the correct direction.

jQuery Add object to dynamic parent

New to jQuery, having an issue with dynamically changing the selector.
<html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script>
function addFirst(){
var fc = document.getElementById("fc").value;
$("#parent").append("<div id='firstChild" + fc + "'>My boring text.</div><a href='#' onClick='addSecond(\"firstChild" + fc +"\")'>Add Second Child</a>");
fc = (fc - 1) + 2;
document.getElementById("fc").value = fc;
}
function addSecond(){
$(***THE PARENT SELECTOR***).append("<div>Some more boring text.</div>");
}
</script>
<body>
<input type="hidden" id="fc" value="1"
<div id="parent"></div>
Add First Child
</body>
</html>
I need a selector that gets me the direct parent of an object when that parent object has been created dynamically, as indicated by my text "THE PARENT SELECTOR".
EDIT
My solution to this, in case anyone else comes across this question, was to use .call inside the onClick event.
Located in the string of the addFirst() function:
<a href='#' onClick='addSecond.call(this,\"firstChild" + fc +"\"); return false;'>
Then, changed the addSecond() function:
$(this.parentNode).prepend("<div>BlahBlah");
It shouldn't matter how the object was created, the selector will work the same.
$(this).parent().append("<div>Some more boring text.</div>");
As noted, this will not work unless you pass a reference to your object. You could leverage jQuery more and all of this will be much easier:
<html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script>
$(function() {
$("body").on('click', '.addsecond', function() {
$(this).parent().append("<div>Some more boring text.</div>");
});
$("body").on('click', '.addfirst', function() {
var fc = $('.fc').length + 1;
$("#parent").append("<div id='firstChild" + fc + "' class='fc'>My boring text.</div><a href='#' class='addsecond'>Add Second Child</a>");
});
});
</script>
</head>
<body>
<div id="parent">
Add First Child
</div>
</body>
</html>
Note, that I added a class addfirst and addsecond. These are used to add handlers to the links automatically (or more accurately, attach handlers to the body, which trigger when divs with these classes are clicked). When they are clicked, this is passed to the function, allowing you to find that object's parent, etc.
You could clean this code up even further, but I can't guess how you are using it. I am guessing you want the children added somewhere different than where they are actually ending up.
Example: http://jsfiddle.net/2LjYH/
If I were to guess, I would make #parent wrap the "Add First Child" link, like this:
<input type="hidden" id="fc" value="1"
<div id="parent">
Add First Child
</div>
Example: http://jsfiddle.net/XjUzA/
This seems to work more intuitively. And if you want the second child added directly after the link, you don't want .parent().append() at all, you simply want to use .after():
Example: http://jsfiddle.net/XjUzA/1/
Assuming that the this keyword refers to the target element you can select the parent using
$(this).parent();

Categories