Using ReactJS and normal javascript code together - reactjs

I'm trying to work with React. I got a HTML page with some contents and a corresponding JS script holding the logic. Now I decided to use React to dynamically create some reusable view components within a button click event.
Simple example:
HTML
<script src="/ReactTester/react/react.js"></script>
<script src="/ReactTester/react/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
<script src="/ReactTester/myreact.js" type="text/babel"></script>
<button onclick="button_click()">Button</button>
<div id="example"></div>
React + JS
var HelloWorld = React.createClass(
{
render: function()
{
return(<h1>Hello, world!</h1>);
}
});
var button_click = function ()
{
ReactDOM.render(<HelloWorld/>, document.getElementById('example'));
}
When I click the button I get the error
ReferenceError: 'button_click' is not defined
If I try to render the element without using the button event everything works fine. It seems to be a problem with mixing React and normal JS code. Am I missing something? What's wrong?
Any hint would be great. Thanks in advance!

Try this
var HelloWorld = React.createClass({
render: function() {
return (
<h1>Hello, world!</h1>
);
}
});
var button_click = function () {
ReactDOM.render(<HelloWorld />, document.getElementById('example'));
}
document.getElementByTagName('button')[0].addEventListener('click', button_click);

The button_click function defined in your file is not available in the global scope. If you attach to the global scope via the window object for example : window.button_click = ... that will work. But explicitly attaching the event handler as stated in the other answer is a better option since there is no global scope pollution.

Related

angularjs ng-click "How can I use javascript's setAttribute() to create ng-click attribute with some function"

I would like to be able to select a button using querySelector and set an attribute of "ng-click=doSomething()"
I have tried selecting the button and then setAttribute("ng-click", "doSomething()") but its not working
my DOM:
<body>
<div ng-app="myApp" ng-controller="mainCtrl">
<button id="myBtn">click Me</button>
</div>
<script src="./js/app2.js"></script>
</body>
my javascript:
(function() {
"use strict";
angular.module("myApp", []).controller("mainCtrl", mainCtrl);
/** #ngInject */
function mainCtrl($scope) {
init();
function init() {
$scope.doSomething = () => {
console.log("doing something");
}
let btn = document.querySelector('#myBtn');
btn.setAttribute("ng-click", "doSomething()");
}
}
})();
when I click the button it should console log something.
Generally speaking, if you dynamically add "AngularJS-ified" stuff to a document after it's created - such as dynamically creating <button> elements and then adding ng-click attributes to them - those elements will neither be tracked by watchers, nor be part of the normal digest cycle. So, for example, consider the following simple example:
const myApp = angular.module('stuff', [])
.controller('stuff-cont', function($scope) {
const targ = document.querySelector('#target');
for (let i = 0; i < 10; i++) {
let newBtn = document.createElement('button');
newBtn.setAttribute('ng-click', 'sayRandNum()');
newBtn.innerText = `Button ${i}`
targ.append(newBtn);
}
$scope.sayRandNum = () =>{
alert('Your random number is '+Math.ceil(Math.random()*100));
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app='stuff' ng-controller='stuff-cont'>
<div id='target'>
</div>
The buttons above are clickable, they have an appropriately "structured" ng-click, but they <i>don't trigger</i>!
</div>
Here, we're (for some reason...) creating 10 nearly-identical buttons. However, because of when we built these ng-click'ed buttons (namely, after the initial compilation phase), and specifically when we added the ng-click attributes (also after the initial compilation phase), the buttons are effectively not "known" to the AngularJS cycle".
Looked at another way, when AngularJS is first "loaded" on a page, it first walks through the HTML on that page, and looks for any databinds ({{likeThis}}; we'll ignore these for now) or directives. ng-click, ng-repeat, and other Babbys First AngularJS stuff are just standardized directives, so they're part of that "looking for directives" procedure. When AngularJS finds said directives, it says "Okay, you've got an ng-click on this element; I'll keep an eye on that".
To actually add new AngularJS-ified elements - or add AngularJS behavior to existing elements, as I believe is more the case with you - you'll need to use the $compile function, which basically says "hey, AngularJS! I made a new thing and want you to watch it!"
This SO answer -- Working with $compile in angularjs has a pretty decent explanation of how to use the $compile function.
(function() {
"use strict";
var btn = document.querySelector('#myBtn');
btn.setAttribute("ng-click", "doSomething()");
angular.module("myApp", []).controller("mainCtrl", mainCtrl);
function mainCtrl($scope){
$scope.doSomething = function(){
alert('abc');
}
}
angular.bootstrap(document, ['myApp']);
})();
Please check the JSFiddle , the difference is you have to modified the html before angular bootstrapped so your modified html and js code can be compiled properly. Here is a AngularJS Developer Guide - Bootstrap with more infomation of angularjs bootstrap

Angular - binding a dynamically created object

I'm using Angular and I can't edit a dynamically created object, even though it is presented correctly (meaning the binding works).
I have the following view:
<html>
<head>
</head>
<body ng-app='TestApp'>
<div ng-controller='TestCtrl'>
<input ng-model="newModel().name"/>
</div>
</body>
</html>
And the following controller implementation:
var TestApp = angular.module("TestApp", []);
function TestCtrl($scope) {
$scope.newModel = function(){
return { name: 'Fresh' }
}
}
I'm using a method to return the correct object for binding because I need to execute some logic to decide which object should be binded.
The input field displays the correct, dynamically created, value. But I cant seem to edit it.
What am I doing wrong? Is this the wrong way to achieve such a functionality?
Thanks.
Instead of returning an object and by attaching a function to the scope, you can update a scope variable in the function.
Controller code:
var TestApp = angular.module("TestApp", []);
function TestCtrl($scope) {
$scope.newModel = {}; // initialize the scope variable
function updateScope () {
// do some logic
$scope.newModel = { name: 'Fresh' }; // update it with the required object
}
updateScope(); // run the function
}
HTML code:
<body ng-app='TestApp'>
<div ng-controller='TestCtrl'>
<!-- remove `()` since `newModel` is no longer a function -->
<input ng-model="newModel.name"/>
</div>
</body>
Hope this solves the issue.

updating $scope value on $rootScope.$on doesnt update DOM (UI) [duplicate]

I'm trying to making some custom elements with AngularJS's and bind some events to it, then I notice $scope.var won't update UI when used in a binding function.
Here is a simplified example that describing the probelm:
HTML:
<!doctype html>
<html ng-app="test">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="Ctrl2">
<span>{{result}}</span>
<br />
<button ng-click="a()">A</button>
<button my-button>B</button>
</div>
</body>
</html>
JS:
function Ctrl2($scope) {
$scope.result = 'Click Button to change this string';
$scope.a = function (e) {
$scope.result = 'A';
}
$scope.b = function (e) {
$scope.result = 'B';
}
}
var mod = angular.module('test', []);
mod.directive('myButton', function () {
return function (scope, element, attrs) {
//change scope.result from here works
//But not in bind functions
//scope.result = 'B';
element.bind('click', scope.b);
}
});
DEMO : http://plnkr.co/edit/g3S56xez6Q90mjbFogkL?p=preview
Basicly, I bind click event to my-button and want to change $scope.result when user clicked button B (similar to ng-click:a() on button A). But the view won't update to the new $scope.result if I do this way.
What did I do wrong? Thanks.
Event handlers are called "outside" Angular, so although your $scope properties will be updated, the view will not update because Angular doesn't know about these changes.
Call $scope.$apply() at the bottom of your event handler. This will cause a digest cycle to run, and Angular will notice the changes you made to the $scope (because of the $watches that Angular set up due to using {{ ... }} in your HTML) and update the view.
This might be also a result of different problem but with the same symptoms.
If you destroy a parent scope of the one that is assigned to the view, its changes will not affect the view in any way even after $apply() call. See the example - you can change the view value through the text input, but when you click Destroy parent scope!, model is not updated anymore.
I do not consider this as a bug. It is rather result of too hacky code in application :-)
I faced this problem when using Angular Bootstrap's modal. I tried to open second modal with scope of the first one. Then, I immediately closed the first modal which caused the parent scope to be destroyed.
use timeout
$timeout(function () {
code....
},
0);

Finished Loading of ng-include in angular js

What is the best way to detect the end of html loading by ng–include? I want to write some code that runs when it has finished loading.
There are two ways to detect when ng-include finished loading, depending on your need:
1) via onload attribute - for inline expressions. Ex:
<div ng-include="'template.html'" onload="loaded = true"></div>
2) via an event $includeContentLoaded that ng-include emits - for app-wide handling. Ex:
app.run(function($rootScope){
$rootScope.$on("$includeContentLoaded", function(event, templateName){
//...
});
});
when it finishes loading the content
you can use onload for it as below
<div ng-include=".." onload="finishLoading()"></div>
in controller,
$scope.finishLoading = function() {
}
after loading the ng-include finishLoading scope function will call.
here is the working Demo Plunker
You can use this code:
$scope.$on('$includeContentLoaded', function () {
// it has loaded!
});
Here's a complete example that will catch all the ng-include load events emitted by the application:
<html ng-app="myApp">
<head>
<script src="angular.min.js"></script>
</head>
<body ng-controller="myController">
<div ng-include="'snippet.html'"></div>
<script>
var myApp = angular.module("myApp",[]);
myApp.controller('myController', function($scope) {
$scope.$on('$includeContentLoaded', function(event, target){
console.log(event); //this $includeContentLoaded event object
console.log(target); //path to the included resource, 'snippet.html' in this case
});
});
</script>
</body>
</html>
A problem I had, and the reason I take the time to post this, is I failed to include both the ng-app and ng-controller in my HTML markup.
If I have to wait for the element to be present, I wrap a $timeout with $includeContentLoaded:
var selector = '#foo';
$rootScope.$on('$includeContentLoaded', function(event, templateName){
$timeout(() => {
const $el = angular.element(selector);
if ($el.length) {
// Do stuff with element.
}
});
});
The timeout gives it time load properly, specially if the ng-include contains a directive that takes a few milliseconds to render.
There is an alternative for that only using JS call stack tricks. put ng-init="eventName" on the desired element. After that, declare the event on the angular controller:
$scope.eventName = () => {
setTimeout(() => { alert('loaded!'); }, 0);
}
That makes the alert only pop up when everything about angular is loaded, that occur because of the call-stack of JavaScript that considers some codes as Microtasks and some others as Macrotasks and they have priorities like the Microtasks run always first and just after all Microtasks run, the Macrotasks can take the place on the call-stack.
And, setTimeout() is considered a Macrotask for JavaScript, so it will only run as the latest tasks.

Using ReactJS with AngularJS

I am trying to use ReactJS with AngularJS but it is not working out. Could anyone please direct me on how to gel them together? Or please point out what am missing here?
My index.html is as follows:
<!DOCTYPE html>
<html data-ng-app="MyApp">
<head>
<title>My Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body data-ng-controller="Ctrl1">
<div id="myDiv">
<button id="btn1" data-ng-click="clickMe()">Click Me</button>
</div>
<script src="http://fb.me/react-0.8.0.js"></script>
<script src="http://fb.me/JSXTransformer-0.8.0.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.js"></script>
<script type="text/jsx" src="reactExample.js"></script>
<script src="angularExample.js"></script>
</body>
</html>
Here is how my reactExample.js has been written:
/**
* #jsx React.DOM
*/
var testMy = React.createClass({
render: function(){
return ( <p>Hello</p>)
}
});
And my angularExample.js is as follows:
var myapp = angular.module('MyApp',[]);
myapp.controller('Ctrl1',['$scope',function($scope){
$scope.clickMe = function(){
alert("Clicked!");
React.renderComponent(testMy, elem[0]);
}
}]);
It does not display anything (other than the alert). Am expecting to see 'Hello' printed there but it throws the following error in the console:
Error: Invariant Violation: prepareEnvironmentForDOM(...): Target container is not a DOM element
Any help would be much appreciated.
Already #Simon Smith mentioned why the error occour React.renderComponent expect second argument but the way you play DOM manipulation inside controller is not appropriate. In AngularJs DOM manipulation should be in directive. Lets see how it could be
From Facebook's React vs AngularJS: A Closer Look blog
React components are far more powerful than Angular templates; they should be compared with Angular's directives instead.
Bottom of this blog Using React and AngularJS together section you can see how angular and react can play together.
From react website
React components implement a render() method that takes input data and returns what to display.
In angularjs components are rendered by directive
See this plunker where I integrate angularjs and react.
In react-example.js I have created virtual dom element
var Hello = React.createClass({
render: function() {
return React.DOM.div({}, 'Hello ' + this.props.name);
}
});
And myMessage directive render the virtual dom
React.renderComponent(Hello({name: scope.myModel.message}), document.getElementById('example'));
Where virtual dom's name property will bind with scope.myModel.message
To use React in my controller, i do this
myApp.controller(function($scope){
$scope.myComponent = {};
$scope.myComponent.setState({data: "something"});
});
and in my React component:
window.myComponent = React.createClass({
getInitialState: function(){
return {
data:''
}
},
componentWillMount: function(){
var scope = this.props.scope;
scope.myComponent = this;
},
render:func .....
});
I'm using the ngReact directive from David Chang, which passes the $scope as a property into a component. So now you can call setState from your controller, forcing the react component to re-render :)
I have a bigger example of above in my React-Sandbox
I would consider doing the integration via a directive as that is often the recommended approach for integrating non angular code with angular.
Here is an example:
angular.module('app').directive('greeting',[function(){
return {
restrict:'AE',
scope:{name:'#'},
link: function (scope, elem, attrs){
var GreetingSection = React.createClass({displayName: 'Greeting',
render: function() {
var message = "Good morning " + this.props.data.name + ", how are you?";
return React.createElement('div', {className: "greeting"},message);
}
});
React.renderComponent(GreetingSection({data:scope}), elem[0]);
}
};
}]);
More info here (link died; points to archive.org copy):
http://www.syntaxsuccess.com/viewarticle/547bbd04fa0710440b45e41c
Instead try using ngReact angular module rather than using ReactJs directly. This provides seamless integration with angularJs as well. Check the samples/examples # https://github.com/davidchang/ngReact
renderComponent expects a DOM element as a second argument to inject the component. It seems that is what the error is complaining about.

Resources