Executing subcontroller before parent controller - angularjs

I have the following HTML structure:
<div ng-controller="MainController">
<div ng-repeat="row in rows">
[[row.id]]
</div>
<div ng-controller="SubController">
<div ng-repeat="row in rows">
[[row.id]]
</div>
</div>
</div>
Angular code is:
myApp.controller('MainController', function ($scope, $http) {
$http.get('/foo/ajaxGetSomeData/').then(function (response) {
$scope.rows = response.data;
});
});
myApp.controller('SubController', function ($scope, $http) {
$http.get('/bar/ajaxGetAnotherThing/').then(function (response) {
var parentRows = $scope.$parent.rows;
var newRows = parentRows.merge(response.data);
$scope.rows = newRows;
});
});
The problem here is that sometimes the first request executes after the second. And the second depends on the first, so I'm getting an error.
How could I solve this?

Below elaborates a bit more on my comment. Here we would initialize both promises inside the DataRows service (through call to initData from MainController). The SubController is no longer dependent on MainController, but just the fact that something else has called initData. If something else hasn't called that function, then you will get console errors for calling "then" on undefined object.
I also used $timeout instead of $http to mock out async work. I don't know what your data looks like, so I just made an array of strings, you should be able to adapt.
angular.module('myApp', [])
// Using $timeout instead of $http for demo
.service('DataRows', function ($http, $q, $timeout) {
var someData,
anotherThing;
this.initData = function () {
// actual call. get rid of $timeout line in actual code
// someData = $http.get('/foo/ajaxGetSomeData/').then(function (response) {
someData = $timeout(function () { return {data: ['parentRow1', 'parentRow2', 'parentRow3']}; }, 1500).then(function (response) {
return response.data;
});
anotherThing = someData.then(function (parentRows) {
// actual call. get rid of $timeout line in actual code
// return $q.all([parentRows, $http.get('/bar/ajaxGetAnotherThing/')]);
return $q.all([parentRows, $timeout(function () {return {data: ['childRow1', 'childRow2', 'childRow3']}}, 1500)]);
}).then(function (promises) {
var parentRows = promises[0],
response = promises[1];
// from your original code -- merge is not available here. Mocking concatted rows from first call
// return parentRows.merge(response.data);
return parentRows.concat(response.data);
});
};
this.getSomeData = function () {
return someData;
};
this.getAnotherThing = function () {
return anotherThing;
};
})
.controller('MainController', function ($scope, DataRows) {
// initData first so both promises are ready
DataRows.initData();
// getSomeData is the first promise (call to /foo/ajaxGetSomeData)
DataRows.getSomeData().then(function (rows) {
$scope.rows = rows;
});
})
.controller('SubController', function ($scope, DataRows) {
// getAnotherThing is the second promise that depends on the first (/bar/ajaxGetAnotherThing)
DataRows.getAnotherThing().then(function (newRows) {
$scope.rows = newRows;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MainController">
<div ng-repeat="row in rows">
{{row}}
</div>
<div ng-controller="SubController">
<div ng-repeat="row in rows">
{{row}}
</div>
</div>
</div>

Related

JavaScript runtime error: [$injector:modulerr] in Angular Js

I am implementing logic through ui-router, Factory and Directive but getting error: JavaScript runtime error: [$injector:modulerr] in Angular Js.
Ui-Routing was working fine.
Index.html file:
<html><head><title>Employee Management System</title>
<link href="Content/bootstrap.css" rel="stylesheet" />
<script src="Scripts/jquery-1.9.1.min.js"></script>
<script src="Scripts/angular.min.js"></script>
<script src="Scripts/angular-ui-router.min.js"></script>
<script src="Scripts/bootstrap.min.js"></script>
<script src="Scripts/app/EmpRecord.js"></script>
<script src="Scripts/app/GetDataService.js"></script>
<script src="Scripts/app/EmpController.js"></script>
<script src="Scripts/app/EmpApp.js"></script></head>
<body ng-app="EmpApp">
<div class="page-header">Employee Management System
</div><div ng-include="'pageContents/menu.html'"></div>
<ui-view></ui-view></body></html>
EmpApp.js
var app = angular.module("EmpApp", ['ui.router']);
app.factory('EmpFact', ['$http', EmpFact])
.controller('EmpController', ['$scope', 'EmpFact',EmpController])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
templateUrl: '/home.html'
})
.state('Add', {
templateUrl: '/AddEmployee.html'
})
.state('List', {
templateUrl: 'ListEmp.html',
controller: 'EmpController'
}
)
})
.directive('emp-Record', EmpRecord);
ListEmp.html:
<div><div><h3>List of Employees</h3></div>
<div EmpRecord ng-repeat="e in Employees"></div></div>
EmpController
<div><div><h3>List of Employees</h3></div>
<div EmpRecord ng-repeat="e in Employees"></div></div>
GetDataService.js
var EmpFact = function ($http) {
var records = {}
$http.get('http://localhost/EmployeeApi/api/Emp')
.then(function (response) {
records= response.data;
});
return {
GetData: function () {
alert(records);
return records;
}
}
}
All Errors are gone Now but data is not coming.
In short:
Controller:
var EmpController= function ($scope,EmpFact) {
$scope.Employees = EmpFact.GetData();
console.log($scope.Employees);
};
Service:
var EmpFact = function ($http) {
var records = {}
$http.get('http://localhost/EmployeeApi/api/Emp')
.then(function (response) {
records= response.data;
});
return {
GetData: function () {
alert(records);
return records;
}}}
Àpp.js
app.factory('EmpFact', ['$http', EmpFact])
.controller('EmpController', ['$scope','EmpFact', EmpController])
.directive('empRecord', function () {
return {
template: "<tr><td>{{e.empid}}</td><td>{{e.empName}}</td><td>{{e.empEmail}}</td><td>{{e.empSalary}}</td>"
}});
HTML:
<div>
<div><h3>List of Employees</h3></div>
<div emp-Record ng-repeat="e in Employees"></div>
</div>
Ok, so as I suggested in the comment, because the error implies that you haven't injected the EmpFact factory into EmpController, changing
.controller('EmpController', ['$scope', EmpController])
Into:
.controller('EmpController', ['$scope', 'EmpFact', EmpController])
And also injecting it to the controller function:
var EmpController = function ($scope, EmpFact) { ... };
Made the error disappeared, but now you say that "data is not coming".
I suggest another change in your factory, instead of your current code, try this:
var EmpFact = function ($http) {
return {
GetData: function () {
// return a promise which resolve with the actual data returned from the server
return $http.get('http://localhost/EmployeeApi/api/Emp').then(
function (response) {
// return the actual results, instead of the whole response from the server
return response.data;
}
);
}
}
};
Now, in your controller, you should be able to get the data like this:
var EmpController = function ($scope, EmpFact) {
// Call the "GetData" from the factory, which return a promise with the actual results returned from the server
EmpFact.GetData().then(
function(data) {
// in the resolve callback function, save the results data in
// any $scope property (I used "$scope.Employees" so it will be
// available in the view via {{ Employees | json }})
$scope.Employees = data;
}
);
};
By returning a promise you are guaranteed to be able to handle the results returned from an asynchronous request (AJAX). You should be able to use the results in the view like this:
<div emp-Record ng-repeat="e in Employees"></div>
(Note that the above HTML snippet is taken from the comments below this answer)
Edit:
Looking at your directive, it doesn't look like a correct way to construct a table. Change emp-Record to emp-record and wrap it in a <table> tag to make it a valid HTML:
<table>
<tr emp-record ng-repeat="e in Employees"></tr>
</table>
And in your directive's template make sure you close the row tag (Add </tr>):
.directive('empRecord', function () {
return {
template: "<tr><td>{{e.empid}}</td><td>{{e.empName}}</td><td>{{e.empEmail}}</td><td>{{e.empSalary}}</td></tr>"
}
});
Thanks Alon for your help as I am new to Angular, converting my ASP.NET MVC code to HTML5/Angular only.
Finally I am able to resolve it.
Data Service/Factory:
var EmpFact = function ($http) {
return {
GetData: function () {
return $http.get('http://localhost/EmployeeApi/api/Emp');
}
}
}
Controller:
var EmpController = function ($scope, EmpFact) {
//EmpFact.GetData() is a promise.
EmpFact.GetData().then(
function (result) {
$scope.Employees= result.data;
}
);
}

returning defer.promise not working as expected

I've started doing Angular for three days now and I can't wrap my head around the concept of promise.
I'm trying to create a factory to share JSON data between two controllers. The data represent a serialized SQL datatable.
The factory fetches the data using $http
var app = angular.module('myApp', ['ngAnimate', 'ngSanitize', 'ui.bootstrap']);
app.factory('Tableau', function ($http, $q) {
var obj = {};
obj.getTable = function (page) {
var temp = {};
var defer = $q.defer();
$http.get('api/Table/' + page).then(function (resolve) {
defer.resolve(resolve.data);
});
return defer.promise;
}
return obj;
});
The first controller should display the data as an HTML table
app.controller("TableController", function ($scope, Tableau) {
$scope.elements = Tableau.getTable(2); // get the first ten rows with row id >= n x 10 (in this case 2 x 10)
});
And here's the HTML code with the ng directives
<tr ng-repeat="t in elements.myarray track by $index">
<td ng-repeat="(k,v) in t track by $index">
<div ng-if="k.indexOf('#')===0">
<span class="label label-default" ng-repeat="vv in v.split(';')">{{vv}}</span><br />
</div>
<div ng-if="k.indexOf('§')===0">
Lien
</div>
<div ng-if="k.indexOf('#')!=0 && k.indexOf('§')!=0">
{{v}}
</div>
</td>
</tr>
I have no problem calling $http from the controller, but when I moved the code to the factory, $scope.elements contains a promise object instead of my expected JSON object.
I did not implement the second controller, which would create a pagination for the table (using ui-bootstrap)
How can I wait for $http to complete before returning returning an object from the factory ?
If this isn't possible, how can I share data retrieved from a server in a single move ?
Thanks in advance.
You need to wait for the promise to complete before using its result. That means using .then():
app.controller("TableController", function ($scope, Tableau) {
Tableau.getTable(2)
.then(function (result) {
$scope.elements = result;
});
});
You've also fallen prey to the Explicit Promise Construction Antipattern.
Simpler implementation for your factory:
app.factory('Tableau', function ($http, $q) {
var obj = {};
obj.getTable = function (page) {
return $http.get('api/Table/' + page)
.then(function (result) {
return result.data;
});
};
return obj;
});
In your controller:
Tableau.getTable(2)
.then((data) => {
$scope.elements = data
})

Angular Js is not working properly

angular js not displaying anything even like simple expressions. i am tying to execute below code but no hope. can anyone help me out.
below code is for view to display.
`<html>
<head>
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script type="text/javascript" src="/../../Scripts/angularsample.js"></script>
</head>
<body ng-app="spiceApp">
<div>
<div ng-controller="SpicyController">
<p> lets try some code by using service </p>
<input ng-init="message='Girish'" ng-model="message" />
<button ng-click="notify(message);">Notify{{1+2}}</button>
<p>alert will display only by clicking three times.</p>
</div>
<div ng-controller="List">
<button ng-click="bringList()">getList</button>
<table>
<tr ng-repeat="app in appslist">
<td>
{{app.Name}}
</td>
</tr>
</table>
</div>
</div>
</body>
</html>`
js code
var myApp = angular.module('spiceApp', []);
myApp.controller('SpicyController', ['$scope', '$http', 'userService', , function ($scope, $http, userService) {
//below code is using sservice
$scope.notify = function (msg) {
userService(msg);
};
}]);
myApp.controller('List', ['$scope', 'getList', function ($scope, getList) {
$scope.bringList = function () {
getList.getAppsList().then(function (list) {
$scope.appslist = list;
});
};
}]);
myApp.factory('getList', ['$http',function ($http) {
//this code for getting list from controller.
return getList.getAppsList=function(){
$http({
method: 'GET',
url: 'Home/GetAppsList'
})
.success(function (response) {
return response.data;
}, function (error) {
console.log(error);
});
}
}]);
myApp.factory('userService', ['$window', function (win) {
var msgs = [];
return function (msg) {
msgs.push(msg);
if (msgs.length == 3) {
win.alert(msgs.join('\n'));
msgs = [];
}
};
}]);`
please tell me where i am wrong. nothing is working. expression is displaying like {{1+2}} in the ouptut.
You have a typo here:
myApp.controller('SpicyController', ['$scope', '$http', 'userService', , function
with the 2 comas so the dependancies are messed up.
i tried in different way with same view but i modified the js file now it's working fine.
var myApp = angular.module('spiceApp', []);
myApp.controller('SpicyController', ['$scope', '$http', 'userService',function ($scope, $http, userService) {
//below code is using sservice
$scope.notify = function (msg) {
userService(msg);
};
}]);
myApp.factory('userService', ['$window', function (win) {
var msgs = [];
return function (msg) {
msgs.push(msg);
if (msgs.length == 3) {
win.alert(msgs.join('\n'));
msgs = [];
}
};
}]);
myApp.controller('List', ['$scope', 'getList', function ($scope, getList) {
$scope.bringList = function () {
getList.getAppsList.then(function (data) {
$scope.appslist = data;
});
};
}]);
var getList = angular.module("spiceApp").factory("getList", ['$http', function ($http, getList) {
return {
getAppsList: (function (response) {
return $http({
method: 'GET',
url: 'GetAppsList'
})
.then(function (response) {
console.log("coming from servicejs", response.data);
//return data when promise resolved
//that would help you to continue promise chain.
return response.data;
});
})()
};
return getList;
}]);

Calling $http only once in a controller

I have a controller for the landing page. My problem is that $http gets called again whenever I view the page since the controllers for that view gets executed resulting in $http executing all the time.
app.controller('landingCtrl', function($scope, $splash, $http, sentiment) {
//get JSON DATA FROM SERVER
var restURL = {};
restURL.getSentiments = "http://localhost:3000/getSent";
//get JSON DATA FROM SERVER
$http.get(restURL.getSentiments).then(function(res) {
log(res);
return res;
}); /*AJAX ENDS*/
});
Is there any way where I call my $http only once or have some freedom of control as when I want to call? As of now the $http is always getting executed.
To keep my code clean and structured, I wrap those $http calls in services. Also when you have different REST calls, you have less code to change, when you have to edit your api path.
Here is an example:
'use strict';
angular.module('YourApp')
.service('Sentiments', function ($http) {
var sentiments = [];
var api = 'http://localhost:3000/getSent';
return {
all: function(callback) {
var cb = callback || angular.noop;
if(sentiments.length !== 0) {
cb(sentiments);
}else{
$http.get(api)
.success(function(result) {
sentiments = result;
cb(result);
})
.error(function() {
cb();
})
}
}
}
})
.controller('landingCtrl', function ($scope, Sentiments) {
Sentiments.all(function(sentiments) {
$scope.sentiments = sentiments;
});
});
Lets indrocude once from function programming. The wrapped function is fired only once because a fn variable is used to ensure the function is only executed once.
angular.module('app', [])
.controller('onceCtrl', function($scope, messages) {
$scope.messages = messages.get()
}).factory('messages', function($timeout, once) {
var messages = []
return {
get: once(function() {
$timeout(function() { // some delay to simulate $http request
messages.push({
date: Date.now()
})
}, 1000)
return messages
})
}
}).factory('once', function() {
return function once(fn, context) {
var result;
return function() {
if (fn) {
result = fn.apply(context || this, arguments);
fn = null;
}
return result;
};
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="onceCtrl">
First exection {{ messages }}
</div>
<div ng-controller="onceCtrl">
Second execution {{ messages }}
</div>
<div ng-controller="onceCtrl">
Third execution {{ messages }}
</div>
</div>

Scope doesn't refresh

i have a problem with an http.get.
Index.html
<div ng-repeat="element in elements">
<p>{{element.elementText}}</p>
</div>
app.js
I have two controllers. First one initialize $scope.elements with a json and works:
$http.get('someUrl')
.success(function (response) {
$scope.elements = response;
})
Seconde one update $scope.elements with a another json when a scope function is called by ng-click:
$scope.updateScope = function () {
$http.get('someOtherUrl')
.then(function (response) {
$scope.elements = response.data;
});
But when i call updateScope nothing appens. I try use .success but nothing. I try using $scope.$apply after assign response to $scope.elements but it generates an error (Error: [$rootScope:inprog] http://errors.angularjs.org/1.3.11/$rootScope/inprog?p0=%24digest).
UPDATE -
If I reload page ng-repeat on scope element works correctly.
So $scope.elements contains right values but ng-repeat doesn't update itself.
Sorry for my english...
Could you help me please?
.then(function (response) { and .success(function (response) { gets different objects in their callbacks. In the first case you get the response's data directly, in second it will be wrapped in an object (that has also other properties - like status, config, statusText, and so on).
If you use .then your response's body will be in sth.data, not in sth. So in your case:
$scope.updateScope = function () {
$http.get('someOtherUrl').then(function (response) {
$scope.elements = response.data;
});
You can use angular.merge
angular.merge(object1, object2)
To share data you want to use a service, not root scope. Consider an example like this:
HTML
<div ng-app="app">
<div ng-controller="controller1 as vm">
<input type="text" ng-model="vm.dataService.data" />{{vm.dataService.data}}</div>
<div ng-controller="controller2 as vm">
<input type="text" ng-model="vm.dataService.data" />{{vm.dataService.data}}</div>
</div>
JS
var app = angular.module('app', []);
app.factory('DataService', function () {
var data;
return {
data: 'Hello, World!'
};
});
app.controller('controller1', function (DataService) {
var vm = this;
vm.dataService = DataService;
});
app.controller('controller2', function (DataService) {
var vm = this;
vm.dataService = DataService;
});
Here is a jsFiddle that runs that code.
you can try following code.(you need to include $timeout)
$scope.updateScope = function () {
$http.get('someOtherUrl')
.then(function (response) {
$scope.elements = response;
$timeout(function(){
$scope.$apply();
});
});

Resources