Reactjs do not re-render when children node changed - reactjs

I contrusted a table-form with two children node buttons and rows using Reactjs.
After change the value of each child node, the parent node receive the new state,
but no re-render happened, why?
<!DOCTYPE html>
<html>
<head>
<title> w3du </title><meta charset="utf-8">
<!-- Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
<style>
.quarcol {
width:25%
}
.vmidcol {
vertical-align:middle
}
.cyan {
color:#fff;
background-color:#63aae7
}
.blue {
color:#fff;
background-color:#60a0e0
}
</style>
</head>
<body>
<div class="container">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title text-center">Panel</h3>
</div>
<div id="canvas"></div>
</div>
</div>
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.js"></script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.js"></script>
<script src="https://cdn.bootcss.com/react/15.6.1/react.js"></script>
<script src="https://cdn.bootcss.com/react/15.6.1/react-dom.js"></script>
<script src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.js"></script>
<script src="https://cdn.bootcss.com/js-signals/1.0.0/js-signals.js"></script>
<script type="text/babel">
The 'table-row' stuff, the 3rd column is number, after modified, the associated button doesn't changed its value, vice versa
var BudgetTableRow = React.createClass({
getInitialState : function(){
return {
n : this.props.n,
price : this.props.price
};
},
handleChangeNum : function(e){
this.setState({
n : e.target.value
});
this.props.callback(this.props.idx, e.target.value);
},
render : function(){
var styfull = {
width : '100%',
height : '30%',
};
var stycenter = {
verticalAlign : 'middle',
horizontalAlign : 'middle'
};
return (
<tr className={this.props.color}>
<td className="quarcol text-center monofont" style={stycenter}>
<a href="#" data-toggle="tooltip" title={"<h1>"+this.props.label+"</h1>"}>{this.props.name}</a>
</td>
<td className="quarcol"><input name={this.props.label+'-price'}
className="form-control text-center monofont"
type="text" defaultValue={this.state.price} style={styfull}
onChange={this.handleChangePrice}
/></td>
<td className="quarcol"><input name={this.props.label+'-n'}
className="form-control text-center monofont"
type="text" defaultValue={this.state.n} style={styfull}
onChange={this.handleChangeNum} onFocus={this.handleFocus} onBlur={this.handleBlur}
/></td>
<td className="quarcol text-center monofont" style={stycenter}>
{this.state.price * this.state.n}
</td>
</tr>
);
}
});
The 'button' stuff, after modified, the associated table-row doesn't changed its value
var BudgetTableButton = React.createClass({
getInitialState : function(){
return {
n : this.props.n
};
},
handelClick : function(e){
e.preventDefault();
var n = (this.state.n=="0" || this.state.n=="") ? "1" : "0";
this.setState({
n : n
});
this.props.callback(this.props.idx, n);
},
render : function(){
var cls = (this.state.n=="0" || this.state.n=="") ? null : 'btn-info';
return (
<button className={'btn ' + cls} onClick={this.handelClick}>{this.props.label} {this.state.n}</button>
);
}
});
The parent node, the console.log() does right, but the form doesn't re-render, i used unique-key, is it something wrong with this.state.items.map ?
var BudgetTable = React.createClass({
getInitialState : function(){
return {
items : this.props.items
};
},
handleCallback : function(idx, n){
var items = this.state.items;
items[idx].n = n;
this.setState({
items : items
});
},
render : function(){
console.log(this.state.items[0]);
return (
<table className="table monofont">
<thead>
<tr className="blue">
<td className="text-center" colSpan="4">Table</td>
</tr>
</thead>
<tbody>
<tr><td colSpan="4">
{this.state.items.map((it, idx) =>
<BudgetTableButton key={it.label+'r'} label={it.label} idx={idx} callback={this.handleCallback} n={it.n} />
)}
</td></tr>
{this.state.items.map((it, idx) =>
<BudgetTableRow key={it.label+'b'} label={it.label} name={it.name} price={it.price} idx={idx} callback={this.handleCallback} n={it.n} />
)}
</tbody>
</table>
);
}
});
function init(){
return [
{
"price": 1340,
"name": "shoe",
"label": "Q41",
"n" : 1
}, {
"price": 1290,
"name": "clothes",
"label": "Q42",
"n" : 1
}
];
}
ReactDOM.render(
<BudgetTable items={init()} />,
document.getElementById("canvas")
);
</script>
</body>
</html>

The problem in your example is that you are using two states.
(I will talk here about the button component, but both cases are the same)
As you probably already know, the local state can only trigger update on the component it belongs to.
You are trying to sync the button's state with the state of the main app. However getInitialState is only called on initial render and not on re-render (info here).
In order to do what you want, you will need to get the button/row information from the props and not from the state (you already have them in props).
Furthermore, I would delete the children's state at all, because you don't need it. It is better that you keep the state in the top level component and only pass props and functions (like the callback fn) down to the children.
So in this case, if you change the button to the following code, it works flawlessly:
var BudgetTableButton = React.createClass({
handelClick : function(e){
e.preventDefault();
var n = this.props.n === "0" || this.props.n === "" ? "1" : "0";
this.props.callback(this.props.idx, n);
},
render : function(){
var cls = (this.props.n=="0" || this.props.n=="") ? null : 'btn-info';
return (
<button className={'btn ' + cls} onClick={this.handelClick}>{this.props.label} {this.props.n}</button>
);
}
});

You are not mutating the state.
handleCallback : function(idx, n){
var items = [...this.state.items];
items[idx].n = n;
this.setState({
items : items
});
}

Related

Why does the Ajax not updating the model correctly?

I am new to angularjs and is using code sample in book "pro-angularjs" to do some test run (it has an initial list of items, but then use Ajax to update list):
<!DOCTYPE html>
<html ng-app="todoApp">
<head>
<title>TO DO List</title>
<link href="bootstrap.css" rel="stylesheet" />
<link href="bootstrap-theme.css" rel="stylesheet" />
<script src="angular.js"></script>
<script>
var model = {
user: "Adam",
items: [{ action: "Buy Flowers", done: false },
{ action: "Get Shoes", done: false },
{ action: "Collect Tickets", done: true },
{ action: "Call Joe", done: false }],
};
var todoApp = angular.module("todoApp", []);
todoApp.run(function ($http) {
$http.get("todo.json").then(function successCallback(data) {
model.items = data;
});
});
todoApp.filter("checkedItems", function () {
return function (items, showComplete) {
var resultArr = [];
angular.forEach(items, function (item) {
if (item.done == false || showComplete == true) {
resultArr.push(item);
}
});
return resultArr;
}
});
todoApp.controller("ToDoCtrl", function ($scope) {
$scope.todo = model;
$scope.incompleteCount = function () {
var count = 0;
angular.forEach($scope.todo.items, function (item) {
if (!item.done) { count++ }
});
return count;
}
$scope.warningLevel = function () {
return $scope.incompleteCount() < 3 ? "label-success" : "label-warning";
}
$scope.addNewItem = function (actionText) {
$scope.todo.items.push({ action: actionText, done: false });
}
});
</script>
</head>
<body ng-controller="ToDoCtrl">
<div class="page-header">
<h1>
{{todo.user}}'s To Do List
<span class="label label-default" ng-class="warningLevel()"
ng-hide="incompleteCount() == 0">
{{incompleteCount()}}
</span>
</h1>
</div>
<div class="panel">
<div class="input-group">
<input class="form-control" ng-model="actionText" />
<span class="input-group-btn">
<button class="btn btn-default"
ng-click="addNewItem(actionText)">Add</button>
</span>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>Description</th>
<th>Done</th>
</tr>
</thead>
<tbody>
<tr ng-repeat=
"item in todo.items | checkedItems:showComplete | orderBy:'action'">
<td>{{item.action}}</td>
<td><input type="checkbox" ng-model="item.done" /></td>
</tr>
</tbody>
</table>
<div class="checkbox-inline">
<label><input type="checkbox" ng_model="showComplete"> Show Complete</label>
</div>
</div>
</body>
</html>
the only change i made was:
todoApp.run(function ($http) {
$http.get("todo.json").then(function successCallback(data) {
model.items = data;
});
});
it was initially:
$http.get("todo.json").success(function (data) {
model.items = data;
});
which does not run with the latetest version angularjs, and so i made the change.
when debugging, i found that the initial value of model.items is:
and it is correctly showing in UI (see left side of screenshot).
After the ajax, its value is updated to 'data' whose value is:
the value of data looks fine to me (same as initial value of items).
But after i let go the debugger, finally in UI all items are gone.
I do understand why? it seems 'items' is the same as 'data'. Anyone has a clue on how i can debug further to find out the root cause?
Thanks,
btw, the 'todo.json' i used is below:
[{ "action": "Buy Flowers", "done": false },
{ "action": "Get Shoes", "done": false },
{ "action": "Collect Tickets", "done": true },
{ "action": "Call Joe", "done": false }]
You are not updating your model correctly. As you can see from your screenshot, data contains an object data which should be assigned to your model.
todoApp.run(function ($http) {
$http.get("todo.json").then(function successCallback(data) {
model.items = data.data;
});
});

Binding data grid to form

I have a simple table of data on the left side of my UI and a form on the right. As a brand new user to ReactJS I was able to accomplish this without much difficulty. However, I would like the user to be able to click the Name column from each row and have it populate the form on the right. I have a similar page using jQuery and a small routing library where I simply link each row to a route like #edit/:id and use jQuery to manually bind via the selectors. The reason I am trying out React is to avoid having to do this.
Anywhere here is my code so far. Just a main App component with the form and table in separate components.
var TplList = React.createClass({
getInitialState() {
return {
data: { rows: [] }
};
},
componentDidMount() {
$.ajax({
type: "GET",
dataType: "json",
url: this.props.url,
success: function(response) {
//console.log(response);
this.setState({
data: response
});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
var tblRows = this.state.data.rows.map(function(result, index) {
return <TplRow key={index} tpl={result} />
});
return (
<div>
<h3 className="formHeader">Workcell - Master Templates</h3>
<table id="tplDataTable" width="100%" className="form" cellpadding="2" cellspacing="0" border="0">
<thead>
<tr className="titleGreen">
<th>Name</th>
<th>By</th>
<th>Date</th>
<th className="th_right">R/C</th>
<th className="th_right">Copies</th>
<th className="th_right">Vol (10mM)</th>
</tr>
</thead>
<tbody>{tblRows}</tbody>
</table>
</div>
);
}
});
var TplRow = React.createClass({
handleNameClick: function() {
},
render: function() {
var tpl = this.props.tpl;
var cls = (tpl.index % 2 > 0) ? "" : "alt";
var volume = (tpl.volume > 0) ? tpl.volume + "\u00b5" + "l" : "\u00a0";
return (
<tr className={cls}>
<td><a href="#" onClick={handleNameClick}>{tpl.name}</a></td>
<td>{tpl.username}</td>
<td>{tpl.cre_date}</td>
<td>{tpl.compound_placement}</td>
<td>{tpl.copies}</td>
<td>{volume}</td>
</tr>
);
}
});
var TplForm = React.createClass({
getInitialState() {
return {
"copies": 10
};
},
render: function() {
return (
<div>
<h3 className="formHeader">Edit Master Template</h3>
<table className="form" width="100%" cellpadding="3" cellspacing="0">
<tbody>
<tr>
<td className="label"><label htmlFor="tplCopies">Copies</label></td>
<td className="field">
<input
type="number" min="0" max="500"
name="tplCopies"
id="tplCopies"
value={this.state.copies} />
</td>
</tr>
</tbody>
</table>
</div>
);
}
});
var MasterTemplateApp = React.createClass({
render: function() {
return (
<div id="container">
<div id="wc_tpl_left">
<TplList url="/cfc/com_admin_transfer.cfc?method=getWCTemplateData" />
</div>
<div id="wc_tpl_right">
<TplForm />
</div>
<div className="tpl_clear"></div>
</div>
);
}
});
ReactDOM.render(<MasterTemplateApp />, document.getElementById('app_wc_master_tpl'));
you should write your handleNameClick method like this
handleNameClick: function() {
ReactDOM.render(<TplForm />, document.getElementById('wc_tpl_right'))
}
don't forget to remove TplForm from
<div id="wc_tpl_right">
<TplForm />
</div>
hope this helps

when I put ng-controller in div instead of body autocomplete stops working

I have a textbox with autocomplete capability and after clicking on click me button the text in autocomplete is added as in the table , here is the link that works perfectly fine,
var app = angular.module('app', []);
app.factory('Service', function() {
var typesHash = [ {
id :1,
name : 'lemon',
price : 100,
unit : 2.5
}, {
id : 2,
name : 'meat',
price : 200,
unit : 3.3
} ];
var localId = 3;
availableTags = [
"ActionScript",
"AppleScript",
"Asp",
"BASIC",
"C",
"C++",
"Clojure",
"COBOL",
"ColdFusion",
"Erlang",
"Fortran",
"Groovy",
"Haskell",
"Java",
"JavaScript",
"Lisp",
"Perl",
"PHP",
"Python",
"Ruby",
"Scala",
"Scheme"
];
var service = {
addTable : addTable,
getData : getData,
complete:complete
};
return service;
function complete($scope){
$( "#txt" ).autocomplete({
source: availableTags,
messages: {
noResults: '',
results: function() {}
}
});
$("#txt" ).on( "autocompleteselect", function( event, ui ) {
$scope.tableTools.inputData=ui.item.value;
} );
}
function addTable(name,price) {
typesHash.push({id:localId++, name:name, price:price,unit:1});
}
function getData() {
return typesHash;
}
});
app.controller('table', function(Service,$scope) {
//get the return data from getData funtion in factory
this.typesHash = Service.getData();
//get the addtable function from factory
this.addTable = Service.addTable;
this.complete=Service.complete($scope);
});
working link
but as soon as I put ng-controller="table as tableTools" in the div instead of body then autocompleting of text box start acting funny and it does not work properly
not working link
can anyone explain the reason and tell me how I can fix it in a way that even by putting ng-controller="table as tableTools" inside div it works?
Update:
here is the error:
Uncaught TypeError: Cannot set property 'inputData' of undefined
for this line:
$scope.tableTools.inputData = ui.item.value;
(remember the problem starts after you click on suggested text)
The issue is hidden in missunderstanding of the angularjs object life-cycles.
The most important here to know is:
services (factory/provider ... different creation, but at the end the same) are singletons
controllers are instantiated per each view. (There are many, multi instances of one controller through the angular app life time..)
So what happened?
There is one service:
app.factory('Service', function() {
...
and there is one controller passing its scope into that service
app.controller('table', function(Service,$scope) {
...
this.complete=Service.complete($scope);
And on the page we can see:
// first usage of controller
// its first instance is created, $scope is passed
<div class="row commonRow" ng-controller="table as tableTools">
// second usage
// different instance is created... and it also passes the §scope
<tbody ng-controller="table as iterateTb">
But as described above - service is only one. While first controller passes its $scope, the second does after while as well.. and that is causing the issue.
We can use services inside of controllers. That is the design principle of angular. But we should not pass the $scope...
This is happening because you have two controller on the same page with two differenct element therefore scope is not binding properly.
The best code for you to do this is :-
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
<link data-require="bootstrap#*" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
<title>Insert title here</title>
<script>
var app = angular.module('app', []);
app.factory('Service', function() {
var typesHash = [ {
id :1,
name : 'lemon',
price : 100,
unit : 2.5
}, {
id : 2,
name : 'meat',
price : 200,
unit : 3.3
} ];
var localId = 3;
availableTags = [
"ActionScript",
"AppleScript",
"Asp",
"BASIC",
"C",
"C++",
"Clojure",
"COBOL",
"ColdFusion",
"Erlang",
"Fortran",
"Groovy",
"Haskell",
"Java",
"JavaScript",
"Lisp",
"Perl",
"PHP",
"Python",
"Ruby",
"Scala",
"Scheme"
];
var service = {
addTable : addTable,
getData : getData,
complete:complete
};
return service;
function complete($scope){
$( "#txt" ).autocomplete({
source: availableTags,
messages: {
noResults: '',
results: function() {}
}
});
$("#txt" ).on( "autocompleteselect", function( event, ui ) {
console.log($scope);
$scope.tableTools.inputData=ui.item.value;
} );
}
function addTable(name,price) {
typesHash.push({id:localId++, name:name, price:price,unit:1});
}
function getData() {
return typesHash;
}
});
app.controller('table', function(Service,$scope) {
//get the return data from getData funtion in factory
this.typesHash = Service.getData();
//get the addtable function from factory
this.addTable = Service.addTable;
this.complete=Service.complete($scope);
});
</script>
</head>
<body ng-app="app" ng-controller="table as tableTools" >
<form >
<div class="row commonRow" >
<div class="col-xs-1 text-right">
item:
</div>
<div class="col-xs-5">
<input id="txt" type="text" style="width: 100%;" ng-keyup="tableTools.complete()" ng-model="tableTools.inputData">
</div>
<div class="col-xs-2">
<button class="btn btn-primary" ng-click="tableTools.addTable(tableTools.inputData);tableTools.inputData=''">
click me
</button>
</div>
</div>
</form>
<div class="row commonRow">
<div class="col-xs-1"></div>
<div class="col-xs-10">
<table class="table table-hover">
<thead>
<tr>
<th>item</th>
</tr>
</thead>
<tbody> <!--No need for controller here-->
<tr ng-repeat="x in tableTools.typesHash track by x.id">
<td>
<div>{{x.name}}</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
Plunker
If you add
$scope.tableTools={}
right below
function complete($scope){
the second one works as expected.
can anyone explain the reason
As long as the object ‘$scope.tableTools‘ has not been defined, you cannot successfully add attributes to it

can not add text box input in table

I have the following code :
http://plnkr.co/edit/RqLurBaCsgjQjOYMtl8r?p=preview
here there is a textbox and when user add something to the textbox and push add button then the entered text should be added to the table Here is my javaScript code:
var app = angular.module('app', []);
app.factory('Service', function() {
var typesHash = [ {
id : '1',
name : 'lemon',
price : 100,
unit : 2.5
}, {
id : '2',
name : 'meat',
price : 200,
unit : 3.3
} ];
var service = {
addTable : addTable,
getData : getData,
};
return service;
function addTable(data) {
typesHash.push(data);
}
function getData() {
return typesHash;
}
});
app.controller('table', function(Service) {
//get the return data from getData funtion in factory
this.typesHash = Service.getData();
this.testData = {
id : '1',
name : "test",
price : 100,
unit : 2.5
};
//get the addtable function from factory
this.addTable = Service.addTable;
});
here as far as testData is static as follow it works:
this.testData = {
id : '1',
name : "test",
price : 100,
unit : 2.5
};
but here the text in the textbox is not added so I changed the above code as follow:
this.testData = {
id : '1',
name : $("#txt").val(),
price : 100,
unit : 2.5
};
the name gets nothing and row is added but name spot is empty?
Just a quick note that this is a simpler version of my real code and I have a reason to use factory.
Can ahyone help me to find out why this table does not connect to textbox correctly?
Modified version of the plnkr (ooo nice design changes SO).
Updated pasted a bad plnkr link before.
http://plnkr.co/edit/4g7LGRLBNEH2LeuEm1qN?p=preview
code from the post, let me know if this doesn't cover some scenario you were imagining. I tried getting rid of all the style cruft, that should be done in CSS or using things like text-right or text-center provided by bootstrap.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<link data-require="bootstrap#*" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
<title>Insert title here</title>
<script>
var app = angular.module('app', []);
app.factory('Service', function() {
var typesHash = [ {
id :1,
name : 'lemon',
price : 100,
unit : 2.5
}, {
id : 2,
name : 'meat',
price : 200,
unit : 3.3
} ];
var localId = 3;
var service = {
addTable : addTable,
getData : getData,
};
return service;
function addTable(name) {
typesHash.push({id:localId++, name:name, price:100,unit:1});
}
function getData() {
return typesHash;
}
});
app.controller('table', function(Service) {
//get the return data from getData funtion in factory
this.typesHash = Service.getData();
//get the addtable function from factory
this.addTable = Service.addTable;
});
</script>
</head>
<body ng-app="app" ng-controller="table as tableTools">
<form>
<div class="row commonRow">
<div class="col-xs-1 text-right">
item:
</div>
<div class="col-xs-5">
<input id="txt" type="text" style="width: 100%;" ng-model="tableTools.inputData" />
</div>
<div class="col-xs-2">
<button class="btn btn-primary" ng-click="tableTools.addTable(tableTools.inputData);tableTools.inputData=''">
click me
</button>
</div>
</div>
</form>
<div class="row commonRow">
<div class="col-xs-1"></div>
<div class="col-xs-10">
<table class="table table-hover">
<thead>
<tr>
<th>item</th>
</tr>
</thead>
<tbody ng-controller="table as iterateTb">
<tr ng-repeat="x in iterateTb.typesHash track by x.id">
<td>
<div>{{x.name}}</div>
</td>
<td>
<input type="text" ng-model="x.name"/>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
here is the updated plunker :-
http://plnkr.co/edit/uDIEAjRtpM7MnQu72LAA?p=preview
I just added data.name=$("#txt").val(); before pushing the data into array.
function addTable(data) {
data.name=$("#txt").val();
typesHash.push(data);
}
Hope it helps :-)

Custom directive breaking code in AngularJS

I need to add a custom directive to my code, but every time I add it, it breaks my code. I checked the console and is giving me the following error
Error: Argument 'guessGameController' is not a function, got undefined
at Error (native)
Now I am not sure if I am not setting my code right or if I am not adding the directive where is supposed to go. Here is my code, I appreciate all the help.
index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" ng-app="guessGameApp">
<head>
<title>Word Game 2.0 - AngularJS</title>
<!--Encoding-->
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<!-- JQuery -->
<script src="js/jquery-1.11.0.min.js"></script>
<!--Scripts-->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js" type="text/javascript"></script>
<script src="js/controllers/app.js" type="text/javascript"></script>
<script src="js/controllers/maincontroller.js" type="text/javascript"></script>
<!--Styles-->
<link rel="stylesheet" type="text/css" href="css/magicWord.css">
<!--<script src="js/jquery-1.11.0.min.js"></script>-->
</head>
<body>
<div ng-controller="guessGameController">
<p>
<header id="masthead">
<h2 align="center">{{appTitle}}</h2>
</header>
</p>
<div ng-controller="wordController">
<p>
<table align="center" width="300px" height="150px" border="solid 2px">
<tr>
<td id="guessBox">
<p align="center">
<input value="" type="text" id="guestGuess" placeholder="Enter Guess" ng-model="guestGuess"/>
</p>
<p align="center"><button ng-click="addGuess()" id="guessButton">Click to Check</button></p>
</td>
</tr>
<tr>
<td>
<h3 align="center">Your guesses so far are: </h3>
<p align="center" ng-repeat="words in guesses">{{words}}</p>
</td>
</tr>
<tr>
<td>
<p align="center">You have guessed:<b>{{guessed}}</b> times out {{allowed}} chances.</p>
<p align="center">You have <b>{{allowed - guessed}}</b> guesses left.</p>
</td>
</tr>
<tr>
<td>
<a custom-button>Click me</a>
<br />
<button custom-button>Hello</button>
</td>
</tr>
</table>
</p>
</div>
</div>
</body>
</html>
app.js
var gameApp = angular.module('guessGameApp', []);
var gameTemplate = angular.module('guessGameApp', []);
maincontroller.js
gameApp.controller("guessGameController", function($scope)
{
$scope.appTitle = "WELCOME TO THE GUESS GAME!";
});
gameApp.controller('wordController', function($scope)
{
$scope.guess = '';
$scope.guesses = [];
$scope.guessed= '';
$scope.allowed = 6;
$scope.wordToGuess = "Just";
$scope.pushGuess = function () {
$scope.guesses.push($scope.guestGuess);
$scope.guessed = $scope.guesses.length;
$scope.resetGuess();
}
$scope.resetGuess = function() {
$scope.guestGuess = '';
}
$scope.addGuess = function()
{
if ($scope.guestGuess == null || $scope.guestGuess == '')
{
$("input[type=text]").ready(function () { $("#guestGuess").addClass("blur"); });
$scope.result = " Please enter a guess\n\nDon't leave the box empty.";
alert($scope.result);
}
else if ($scope.guestGuess.toLowerCase() == $scope.wordToGuess.toLowerCase())
{
$("input[type=text]").ready(function () { $("#guestGuess").removeClass("blur"); });
$scope.pushGuess(guestGuess);
$scope.result = "You have guessed the correct word. Way to go!\n\n\t\t The word was: ";
alert($scope.result + $scope.wordToGuess);
}
else if ($scope.guestGuess != $scope.wordToGuess & ($scope.allowed - $scope.guessed) > 1)
{
$("input[type=text]").ready(function () { $("#guestGuess").removeClass("blur"); });
$scope.pushGuess(guestGuess);
$scope.result = "Please try again!";
alert($scope.result);
}
else if (($scope.allowed - $scope.guessed) <= 1)
{
$("input[type=text]").ready(function () { $("#guestGuess").addClass("doneBlur"); });
$scope.guesses.push($scope.guestGuess);
$scope.guessed = $scope.guesses.length;
$scope.result = "Game over! The word was: ";
alert($scope.result + $scope.wordToGuess);
}
$scope.guess = '';
}
});
gameApp.directive('customButton', function ()
{
$scope.wordToGuess = "Just";
return {
restrict: 'A',
replace: true,
transclude: true,
templateUrl: '../../templates/customTemplate.HTML',
link: function (scope, element, attrs)
{
element.bind("click",function()
{
alert("The value of 'guessWord' is " + scope.wordToGuess);
})
}};
});
customTemplate.html
<a href="" class="myawesomebutton" ng-transclude>
<i class="icon-ok-sign"></i>
</a>
In app.js remove the second module declaration
var gameApp = angular.module('guessGameApp', []);
//var gameTemplate = angular.module('guessGameApp', []); --> remove this line
You are also modifying DOM from the controller, this is not the angular way. If you want to add classes when some condition occurs, then have a look at ng-class

Resources