I have a service to share data between controllers and a controller to handle $http request in my application. I use list with ng-repeat to display
data and currently have hardcoded JSON data on service. I plan to modify the hardcoded method to get data from server using $http.post and I got the data but my list is not updated. I have tried adding $timeout but still no luck.
Here is my code:
app.service("Storage", function ($rootScope, $http, $q) {
var predicate = 'name';
var reverse = false;
var content = this;
content.items = [];
content.filteredItems = [];
return {
requestData: function() {
var deferred = $q.defer();
var data = $.param({
json: JSON.stringify({
req_type: 'type_1',
reg_name: 'name_2'
})
});
$http.post("http://yourdomain/json.php", data)
.success(function(data, status) {
deferred.resolve(data);
})
.error(function(data) {
deferred.reject(data);
});
return deferred.promise;
},
setContent : function(data){
content.items = data;
},
getContentItems : function () {
return content.items;
}
}
});
Controller:
app.controller('MainController', function($scope, $window, $filter, $timeout, $http, Storage) {
$scope.predicate = null;
$scope.reverse = null;
this.items = null;
Storage.requestData().then(function(data) {
Storage.setContent(data);
$timeout(function(){
$scope.predicate = Storage.getPredicate();
$scope.reverse = Storage.getReverse();
this.items = Storage.getContentItems();
});
//$scope.$apply();
console.log('After11 : ' + JSON.stringify(Storage.getContentItems()));
});
});
You made mistake at this line:
this.items = Storage.getContentItems();
this !=== controller's this
I always create mv variable at the top of controller and use mv instead of this for avoiding this mistake.
angular
.module('app', [])
.service('Storage', function($http, $q, $timeout) {
var content = this;
var items = [];
this.requestData = function() {
var deferred = $q.defer();
//TODO Some http request
$timeout(function() {
deferred.resolve(['A', 'B', 'C']);
}, 1000);
return deferred.promise;
};
this.setContent = function(data) {
items = data;
};
this.getContentItems = function() {
return items;
};
})
.controller('MainCtrl', function(Storage) {
var mv = this;
mv.items = [];
Storage.requestData().then(function(data) {
Storage.setContent(data);
mv.items = Storage.getContentItems();
});
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<meta charset="utf-8">
</head>
<body ng-app="app">
<ul ng-controller="MainCtrl as mv">
<li ng-repeat="item in mv.items" ng-bind="item"></li>
</ul>
</body>
</html>
Related
I am using AngularJS and I have 2 controllers which are siblings. The first controller gets data from an $http request on click. The second controller has to retrieve this data, but I don't know how this controller will get the data because it has to wait for the function in the first controller before it can get its data.
This is the first controller:
$scope.getMessageData = function(username, full_url, main_item, item_id, sub_id){
$scope.ajax_spinner = true;
$http({
method: "GET",
url: "/getMessageData",
params:{
"adUsername" : username,
"fullUrl" : full_url,
"mainItem" : main_item,
"tagId" : item_id,
"subId" : sub_id
}
}).then(function successCallback(response) {
$scope.adUsername = response.data.adUsername;
$scope.fullUrl = response.data.fullUrl;
$scope.main_item = response.data.main_item;
$scope.message = response.data.docs[0];
$scope.sub_item = response.data.sub_docs[0];
$scope.ajax_spinner = false;
Data.setItem($scope.message)
});
};
The factory I use to share the data with the second controller:
App.factory('Data', function(){
return {
setItem: function(item){
this.item = item;
},
getItem: function(){
return this.item;
}
};
});
The second controller:
App.controller("dataController", function($scope, $http, $sce, Data){
$scope.message = Data.getItem();
console.log($scope.message)
});
Now obviously $scope.message is undefined because the controller already loads on page load, but the getMessageData function is not ready called, so how can I "wait" for the second controller to load before getMessageData is fired?
You could use $watch in your controller to keep up with the changes in your service, e.g.
var unwatch = $scope.$watch(function() {
/* set Data.item value in your service */
return Data.item;
}, function(item) {
$scope.item = item || 'nothing here';
});
$scope.$on('$destroy', unwatch);
Or you could implement simple mechanism to register and unregister callbacks in your service to get notified when ever data is changed, e.g.
Template
<html ng-app="App">
<head>
...
</head>
<body>
<div ng-controller="SomeController">
SomeController
<button ng-click="getData()">Fetch</button>
<div ng-bind="data | json"></div>
</div>
<br>
<div ng-controller="OtherController">
OtherController
<div ng-bind="data | json"></div>
</div>
</body>
</html>
JavaScript
angular.module('App', [])
.controller('SomeController', function($scope, Data) {
$scope.getData = function() {
Data.getData().then(function(data) {
$scope.data = data;
});
};
})
.controller('OtherController', function($scope, Data) {
var callback = function(data) {
$scope.data = data;
};
Data.register(callback);
$scope.$on('$destroy', function() {
Data.unregister(callback);
});
})
.factory('Data', function($http) {
var callbacks = [];
return {
register: function(callback) {
var index = callbacks.indexOf(callback);
if (index === -1) {
callbacks.push(callback);
}
},
unregister: function(callback) {
var index = callbacks.indexOf(callback);
if (index !== -1) {
callbacks.splice(index, 1);
}
},
getData: function() {
/* where data.json is { "message": "My message" } */
return $http.get('data.json').then(function(response) {
var data = response.data;
data.timestamp = (new Date()).getTime();
/* move inside $interval out of this fn maybe... */
angular.forEach(callbacks, function(callback) {
callback(data);
});
return data;
});
}
};
});
I wanna use multiple ( in this case, 2 ) $http.gets in my service !
As you know the simple form of using $http.get is this :
app.factory('MyService', function ($http, $q) {
return {
getData: function() {
return $http.get('myfile.json')
.then(function(response) {
return response.data;
});
}
};
});
Now I wanna use 2 files ( 2 $http.gets ) and compare them to each other ( with some for loops and etc that I can ... ) !
What can I do now ? :(
use $q.all.
Add $q to controller's dependencies, exemple
$scope.req1 = $http.get('myfile.json');
$scope.req2 = $http.get('myfile2.json');
$q.all([$scope.req1, $scope.req2]).then(function(data) {
// data is array of your files
if ( JSON.stringify(data[0]) === JSON.stringify(data[1])){
console.log('is equal');
}
});
It is an extension of Hajji Tarik's solution. I was able to derive from your comments that you were still not clear with what to code in where. So I have developed a sample application which will assist you for the same.
//--app.module.js--//
angular.module('notesApp', []);
//--app.service.js--//
angular.module('notesApp')
.factory('notesFactory', ['$http',
function($http) {
var notesService = {};
notesService.getData = function(url, method) {
return $http({
url: url,
method: method
});
}
return notesService;
}
]);
//--app.controller.js--//
angular.module('notesApp')
.controller('MainController', ['$scope', '$http', '$log', '$q', 'notesFactory',
function($scope, $http, $log, $q, notesFactory) {
$scope.data = {};
var data1 = notesFactory.getData('http://localhost:3000/api/notes/1', 'GET');
var data2 = notesFactory.getData('http://localhost:3000/api/notes/2', 'GET');
var combinedData = $q.all({
firstResponse: data1,
secondResponse: data2
});
combinedData.then(function(response) {
$log.log(response.firstResponse.data);
$log.log(response.secondResponse.data);
//Write your comparison code here for comparing json results.
}, function(error) {
$scope.data = error;
});
}
]);
<html ng-app='notesApp'>
<head>
<title>
Notes Application
</title>
</head>
<body>
<div ng-controller='MainController'>
</div>
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js'></script>
<script src='app.module.js'></script>
<script src='app.controller.js'></script>
<script src='app.service.js'></script>
</body>
</html>
I'm an angular newbie and I'm writing an Ionic app.
I finished my app and am trying to refactor my controller avoiding code repetition.
I have this piece of code that manages my modal:
angular.module('starter')
.controller('NewsCtrl', function($scope, content, $cordovaSocialSharing, $timeout, $sce, $ionicModal){
$scope.news = content;
content.getList('comments').then(function (comments) {
$scope.comments = comments;
});
$scope.addComment = function() {
};
$scope.shareAnywhere = function() {
$cordovaSocialSharing.share("Guarda questo articolo pubblicato da DDay", "Ti stanno segnalando questo articolo", content.thumbnail, "http://blog.nraboy.com");
};
$ionicModal.fromTemplateUrl('templates/comments.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modal = modal;
});
$scope.showComment = function() {
$scope.modal.show();
};
// Triggered in the login modal to close it
$scope.closeComment = function() {
$scope.modal.hide();
};
$scope.$on('modal.shown', function() {
var footerBar;
var scroller;
var txtInput;
$timeout(function() {
footerBar = document.body.querySelector('#commentView .bar-footer');
scroller = document.body.querySelector('#commentView .scroll-content');
txtInput = angular.element(footerBar.querySelector('textarea'));
}, 0);
$scope.$on('taResize', function(e, ta) {
if (!ta) return;
var taHeight = ta[0].offsetHeight;
if (!footerBar) return;
var newFooterHeight = taHeight + 10;
newFooterHeight = (newFooterHeight > 44) ? newFooterHeight : 44;
footerBar.style.height = newFooterHeight + 'px';
scroller.style.bottom = newFooterHeight + 'px';
});
});
});
I have added this same code in 6 controllers.
Is there a way to avoid the repetition?
Probably what you are looking for is an angular service. This component is a singleton object, that you inject in every controller you need to execute this code.
Angular Services
Regards,
Below is an example of a service I created to retrieve address data from a Json file. Here is the working Plunk. http://plnkr.co/edit/RRPv2p4ryQgDEcFqRHHz?p=preview
angular.module('myApp')
.factory('addressService', addressService);
addressService.$inject = ['$q', '$timeout', '$http'];
function addressService($q, $timeout, $http) {
var addresses = [];
//console.log("Number of table entries is: " + orders.length);
var promise = $http.get('address.data.json');
promise.then(function(response) {
addresses = response.data;
// console.log("Number of table entries is now: " + orders.length);
});
return {
GetAddresses: getAddresses
};
function getAddresses() {
return $q(function(resolve, reject) {
$timeout(function() {
resolve(addresses);
}, 2000);
});
}
}
Here's an example of how I added dependencies for it and another service to my controller (This is NOT the only way to do dependency injection, but is my favorite way as it is easier to read). I then called my addressService.GetAddresses() from within my controller.
var app = angular.module('myApp', ['smart-table']);
app.controller('TableController', TableController);
TableController.$inject = [ "orderService", "addressService"];
function TableController( orderService, addressService) {
addressService.GetAddresses()
.then(function(results) {
me.addresses = results;
// console.log(me.addresses.length + " addresses");
},
function(error) {})
.finally(function() {
me.loadingAddresses = false;
});
});}
I also had to include my .js tag in a script element on my index.html.
<script src="addressdata.service.js"></script>
I have the following
Service:
angular.module('app')
.factory('UserFactory', function ($http) {
function profile () {
return $http.get('/gimme')
.then(function success (response) {
return response;
});
};
var user = {
profile: profile
};
return user;
It is used in a controller as follows:
Controller
angular.module('app')
.controller('HeaderCtrl', function ($scope, UserFactory) {
$scope.awesomeThings = [
'HTML5 Boilerplate',
'AngularJS',
'Karma'
];
$scope.user = UserFactory.profile().then(function (response) {
$scope.user = response.data;
});
$scope.change = function () {
$scope.user.name = 'New Name'
}
}
If I call the change() method in a directive which uses HeaderCtrl, what is the best way to make sure that that change, which temporarily changes the user.name, actually changes it on my server as well? In other words, how would I trigger the put request (I am assuming some function needs to be called on the Factory, but I am not sure the best way to make sure it is called or where to put the function call in the controller).
Thanks
Here's an example extending the code you provided, using free JSONPlaceholder API. I think example itself is enough of an answer?
HTML
<body ng-controller="Ctrl as vm">
<div>data: {{ vm.todo | json }}</div>
<div>response: {{ vm.response | json }} </div>
<hr>
<button type="button" ng-click="vm.change('my new title')">Change title</button>
</body>
JavaScript
app.controller('Ctrl', function(TodoService) {
var vm = this;
var id = 1;
TodoService.getPost(id).then(function(response) { // Get
vm.todo = response.data;
});
vm.change = function(val) {
vm.todo.title = val;
TodoService.putPost(vm.todo).then(function(response) { // Put
vm.response = response.status + ' ' + response.statusText;
}).catch(function(error) {
vm.response = angular.toJson(error);
});
};
});
app.factory('TodoService', function($http) {
var endpoint = 'http://jsonplaceholder.typicode.com/todos/';
var todoService = {};
todoService.getPost = function(id) {
return $http.get(endpoint + id).then(function(response) {
return response;
});
};
todoService.putPost = function(todo) {
return $http.put(endpoint + todo.id, todo).then(function(response) {
return response;
});
};
return todoService;
});
Related plunker here http://plnkr.co/edit/VBvVen
var nameSpace = angular.module("MyTutorialApp", []);
nameSpace.controller("MainController", ['$scope', '$http',
function($scope, $http)
{
$http.get("../api/api.php?fxn=" + encodeURIComponent("getCategories") +
"&jsn=" + encodeURIComponent("{'code':'1'}"))
.success(function(response)
{
$scope.names = response;
});
$scope.myData = {};
nameSpace.controller("MainController", ['$scope', '$http',
$scope.myData.doClick = function($event, name, $scope, $http,$config)
{
alert(name);
var element = name;
console.log(element);
$http.get("../api/api.php?fxn=" + encodeURIComponent("getSubCategories") +
"&jsn=" + encodeURIComponent("{'code':'element'}"))
.success(function(response)
{
$scope.subCat = response;
});
}]);
}
]);
<!DOCTYPE html>
<head>
<title>Learning AngularJS</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js" type="text/javascript"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<script src="js/maincontroller.js"></script>
</head>
<body ng-app="MyTutorialApp" >
<div ng-controller="MainController">
<table class="table">
<tr class="row1" ng-repeat="list in names.category">
<td ng-click="myData.doClick($event,list.name)">{{ list.name }}</td>
</tr>
</table>
</div>
</body>
</html>
Hi, i m not able to make the second http request , It says get property undefined. I tried for quite along time, i am not able to spot what is going wrong. Kindly Help me. I am just starting to use angular.
To explain what I am trying to achieve , the first http request calls for the list of categories , the list is populated and after that on click of any of the category , the category is sent as the jsn for the second http request . And it fetch's the sub category
Check this
// Code goes here
var nameSpace = angular.module("MyTutorialApp", []);
nameSpace.factory('factoryRefrence', ['$http', '$q',
function($http, $q) {
return {
getCategories: function() {
var deferred = $q.defer();
$http.get("../api/api.php?fxn=" + encodeURIComponent("getCategories") +
"&jsn=" + encodeURIComponent("{'code':'1'}"))
.success(function(response) {
deferred.resolve(response);
});
return deferred.promise;
},
getsubCategories: function(element) {
var deferred = $q.defer();
$http.get("../api/api.php?fxn=" + encodeURIComponent("getSubCategories") +
"&jsn=" + encodeURIComponent({
'code': element
}))
.success(function(response) {
deferred.resolve(response);
});
return deferred.promise;
}
}
}
]);
nameSpace.controller("MainController", ['$scope', '$http', 'factoryRefrence',
function($scope, $http, factoryRefrence) {
factoryRefrence.getCategories().then(function(response) {
$scope.names = response;
});
$scope.myData = {};
$scope.myData.doClick = function(event, name) {
alert(name);
var element = name;
console.log(element);
factoryRefrence.getsubCategories().then(function(response) {
$scope.subCat = response;
});
}
}
]);
Demo
this is the way to communicate with functions in factory. if you setup like this it should work fine. and besides in your code you are defining controller twice which is not okay.