Using ReactJS with AngularJS - 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.

Related

Angularjs TypeError: is not a function issue

I have a select that calls a controller in a directive, that in turn calls a function from a service when the user selects a value in the dropdown list. For some reason I'm getting this error:
TypeError: myService.getMessage is not a function
So I created a plunker to pull out the basic functionality and I was able to duplicate the error using a controller to call a basic service, but I'm still not solving it yet.
Here is the code:
HTML Code:
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.2.x" src="https://code.angularjs.org/1.2.22/angular.js" data-semver="1.2.22"></script>
<script src="script.js"></script>
</head>
<body ng-controller="MainCtrl">
<select ng-options="option for option in listOfOptions"
ng-model="selectedItem"
ng-change="selectedItemChanged()">
</select>
<p>This is the selected Item from the model: <b>{{selectedItem}}</b></p>
<p>This is the result of the ng-change function: <b>{{calculatedValue}}</b></p>
</body>
Script Code:
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope, myService) {
$scope.listOfOptions = ['One', 'Two', 'Three'];
$scope.selectedItemChanged = function(){
$scope.calculatedValue = 'You selected number ' + $scope.selectedItem;
//Call the service.
myService.getMessage();
}
});
app.service('myService', function(){
function getMessage(){
alert("You are in myService!");
}
});
I've seen lots of different, much more complicated code for this type of error, but I'm not understanding what is causing this? Any ideas as to the proper way to do this?
What I'm trying to do is to use a function like myService.mySearch(param1) from a controller or directive.
wrong service code, should be:
app.service('myService', function() {
this.getMessage = function() {
alert("You are in myService!");
}
});
.service() is a function which takes a name and a function that defines the service.It acts as a constructor function.We can inject and use that particular service in other components : controllers, directives and filters.
Correct Syntax :
app.service('myService', function(){
this.getMessage = function(){
alert("You are in myService!");
}
});
Main thing is that service is a constructor function. Hence, we can work with this keyword. In background, this code calls Object.create() with the service constructor function, when it gets instantiated.
In addition to Fetra R 's answer,
Factory is mostly preferable in all cases. It can be used when you have constructor function which needs to be instantiated in different controllers. Service is a kind of Singleton Object. The Object return from Service will be same for all controller.
So as it is like a singleton object, you have to declare it as The Fetra R's Answer, or You may write a factory.
app.factory('myService', function() {
return {
getMessage: function() {
alert("You are in myService!");
}
}
});
and use it in the same manner.

Using ReactJS and normal javascript code together

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.

block-ui-pattern has no effect

i am trying to implement block-ui into our angular application on an element by element basis. (everything is included, referenced and injected correctly)
We have been trying to implement
block-ui-pattern
with no success.
our $http request is :-
$http.get('/user/123/GetUserAddress/').then(function (data) {
and our block-ui-pattern is :-
< div block-ui block-ui-pattern="/^user\/123\/GetUserAddress\$/">
{{address}}
</div>
This seems to match the documentation, but is failing to work. Am i missing something fundamental?
Our application exposes an isloading flag. initially set to true, and when the $http promise returns, sets this to false.. I realize that it is not in the documentation, however, Is there a way to set
< div block-ui="isloading"></div>
Post by Parash Gami pointed me in the right direction.
I actually ended up writing a custom directive that wraps block-ui
var myBlocker = angular.module('app.Angular.Directive.myBlocker ', []);
myBlocker.directive('myBlocker ', ['$compile', function ($compile) {
return {
restrict: 'A',
scope :{
blockId: '#id',
block: '=',
},
controller: ['$scope', 'blockUI', function ($scope, blockUI) {
var myBlock = blockUI.instances.get($scope.blockId);
$scope.$watch('block', function (newValue, oldValue) {
if ($scope.block === true)
{
myBlock.start()
}
else {
myBlock.stop()
}
});
}],
link: function link(scope, element, attrs) {
element.attr('block-ui', scope.blockId);
element.attr('style', 'min-height:200px;');
element.removeAttr("my-blocker");
element.removeAttr("data-my-blocker");
$compile(element)(scope);
}
};
}]);
This allows me to now simply add the directive to an element
< div id="myId" my-blocker block="loading">
Please check sample code. Just include one CSS and one JS of blockUI and add dependency blockUI, use blockUI.start() method to show loader and use blockUI.stop() method to hide loader. Following example hide loader after 2 seconds. Use it as per your requirement.
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="http://angular-block-ui.nullest.com/angular-block-ui/angular-block-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
<script type="text/javascript" src="http://angular-block-ui.nullest.com/angular-block-ui/angular-block-ui.js"></script>
</head>
<body ng-app="app.user">
<div ng-controller="tempController">
</div>
</body>
</html>
<script type="text/javascript">
var app = angular.module('app.user',['blockUI']);
app.controller('tempController', function(blockUI,$timeout)
{
blockUI.start();
$timeout(function()
{
blockUI.stop();
},2000)
});
</script>

Angularjs Binding Appears to be Broken for Dojo DateTextBox

I’m new to Angularjs and the Dojo-Toolkit so please forgive my newbieness.
I have an element in my page that I’m able to bind to my model without any problem: <input type="text" ng-model="startDateRange"></input> This works as expected.
When I update the element so it uses the Dojo-Toolkit the binding appears to be broken: <input type="text" ng-model="startDateRange" data-dojo-type="dijit/form/DateTextBox"></input> The binding to the model no longer works.
I’m not sure what I’m doing wrong. Any help would be appreciated. Thanks.
AngularJS binding works upon DOM nodes, if you move, delete or replace a DOM node that AngularJS is watching, then the code will no longer work.
As Thomas Kagan said, Dojo widgets will replace the DOM node with the data-dojo-type on it by the DOM nodes provided in the template of those widgets. This simply erases your binding as if it didn't exist.
A proper AngularJS solution would be to wrap the Dojo DateTextBox inside a directive, so AngularJS knows that this is encapsulated and AngularJS should only access the directive through an API (the scope of a directive).
For example:
myApp.directive("dateTextBox", function($timeout) {
var link = function(scope, elem, attr) {
require(["dijit/form/DateTextBox"], function(DateTextBox) {
var dateTxtBox = new DateTextBox({});
dateTxtBox.set('value', scope.date);
dateTxtBox.on("change", function(date) {
$timeout(function() {
scope.date = date;
});
});
elem.append(dateTxtBox.domNode);
});
};
return {
restrict: 'E',
scope: {
date: "="
},
link: link
};
});
This is just a basic example, I also made a demo, which you can view by running the snippet below.
angular.module("myApp", []).controller("TestCtrl", function($scope) {
$scope.date = new Date();
})
.directive("dateTextBox", function($timeout) {
var link = function(scope, elem, attr) {
require(["dijit/form/DateTextBox"], function(DateTextBox) {
var dateTxtBox = new DateTextBox({});
dateTxtBox.set('value', scope.date);
dateTxtBox.on("change", function(date) {
$timeout(function() {
scope.date = date;
});
});
elem.append(dateTxtBox.domNode);
});
};
return {
restrict: 'E',
scope: {
date: "="
},
link: link
};
});
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/dojo/1.10.1/dijit/themes/claro/claro.css" />
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.1/dojo/dojo.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="myApp" class="claro">
<div ng-controller="TestCtrl">
<date-text-box date="date"></date-text-box><br />
{{date | date}}
</div>
</body>
</html>
When the dojo parser runs it will identify the input element as a dijit widget and destroy your input element replacing it with a dojo widget so the ng-model property will no longer be present. I recommend using Dojo's observable module for data-binding instead of trying to mix in angular

AngularJS: Binding JavaScript Received in XHR to the View

Working on a reporting application where reports are generated (in HTML) from a BIRT Report Engine object. The report data comes as a JSON string is recieved from XHR. The JSON string contains a combination of HTML and javascript (a function call, specifically). Once received, the report data is stuffed into a for display in the view. The view is put together using AngularJS.
I did some research and found that binding the HTML/javascript to the view in Angular requires the use of $compile. Using that as a basis, i put together some code that will include data and execute code bound from a string defined explicitly in the $scope. But - unless i'm going overlooking something after staring at the same stuff for a couple hours, the approach i'm using does not work with $scope data defined by XHR. Here's a plunkr to show the general idea implemented. Any suggestions would be greatly appreciated.
The HTML
<!DOCTYPE html>
<html data-ng-app="example">
<head lang="en">
<meta charset="UTF-8">
<title></title>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.11.0/ui-bootstrap-tpls.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular-sanitize.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="controller" >
<h1>Static Content</h1>
<p><button href="javascript: void(null)" role="link" ng-click="loadSubreport('recv_po_detail.rptdesign', 'static')">PO000007</button></p>
<h1>HTML from static string</h1>
<div compile="snippet"></div>
<h1>HTML from server string</h1>
<div compile="html.body"></div>
<hr />
<button ng-click="alert()">Show XHR Data</button>
</body>
</html>
The Javascript
var app = angular.module('example', []);
app.controller('controller', ['$scope', '$compile', '$http', function ($scope, $compile, $http){
$scope.snippet="<p><button href=\"javascript: void(null)\" ng-click=\"loadSubreport('recv_po_detail.rptdesign', 'compiled')\">PO000007</button></p>";
$http.get('data.json')
.success(function (data) {
$scope.html = data;
});
$scope.loadSubreport = function (filename, source) {
alert("Called from " + source);
};
$scope.alert = function () {{
alert($scope.html.body);
}}
}]);
app.directive('compile', ['$compile', function ($compile) {
"use strict";
return function (scope, element, attrs) {
var ensureCompileRunsOnce = scope.$watch(
function (scope) {
return scope.$eval(attrs.compile);
},
function (value) {
element.html(value);
$compile(element.contents())(scope);
ensureCompileRunsOnce();
}
);
};
}]);
Your watch goes off right at the start, when html.body still is undefined.
Then you run ensureCompileRunsOnce() and unwatch the scope. So the proper report, once loaded, never gets compiled.
I uncommented the line ensureCompileRunsOnce() and get a nice view of the report.
DEMO

Resources