I'm trying to share data via a service that uses the $HTTP function between controllers. I'm trying to pass the return data in SUCCESS to another controller. Something is wrong I think in the service the data doesn't get to the second controller. below is my code can someone take a look at it and tell me what I'm doing wrong point me to the right direction on what to do.
services.js
.factory('userService', function ($http) {
var url = "url.php";
var headers = {
'Content-Type' : 'application/x-www-form-urlencoded; charset-UTF-8'
};
var params = "";
return {
getUsers : function (entry, searchTypReturn) {
params = {
entry : entry,
type : searchTypReturn,
mySecretCode : 'e8a53543fab6f5e'
};
return $http({
method : 'POST',
url : 'https://servicemobile.mlgw.org/mobile/phone/phone_json.php',
headers : {
'Content-Type' : 'application/x-www-form-urlencoded; charset-UTF-8'
},
transformRequest : function (obj) {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data : params
})
.success(function (data, status, headers, config) {
return data;
});
}
}
})
controller.js
.controller('phoneController', function ($scope, md5, $http, userService, $ionicLoading, $location, $ionicPopup) {
userService.getUsers(form.entryText, searchTypReturn).success(function (data, status, headers, config) {
$ionicLoading.hide();
$scope.name = data.PlaceDetailsResponse.results[0].first_name;
if ($scope.name == 0) {
$scope.showAlert();
} else {
$location.path('phoneView');
$ionicLoading.hide();
}
}).error(function (data, status, headers, config) {
$scope.showAlert();
$ionicLoading.hide();
})
});
.controller('phoneViewController', function ($scope, userService) {
$scope.input = userService;
console.log('This is from phoneView', $scope.input);
});
Nasser's answer is the correct one. There are other ways of keeping track of things in memory if it is just session based.
For example there is http://lokijs.org/ which also claims that the data persist between sessions because it is written to a file as well. And it replaces SQLite.
The relationship between the controllers and the directives which get the data to be displayed from the scope of the directives are loosely coupled.
If there are no values to be displayed in the scope like {{valuetobedisplayedfromcontroller}} your html becomes funky.
There are 2 options to fix this. Either use ng-if conditionals in the html directives or encapsulate the whole controller in an if command which checks a global variable to see if the data is loaded and show a loading screen and prevent user input and return error with a timeout.
I'm very keen to learn if there are other/better solutions.
you can store received data from API in $rootScope or global var or you can store data in a factory.
example for using $rootScope
angularApp.controller('phoneController', function($scope, md5, $http, userService, $rootScope, $ionicLoading, $location, $ionicPopup) {
$rootScope.data = userService.getUsers(form.entryText,searchTypReturn).success(function(data, status, headers, config) {
$ionicLoading.hide();
$scope.name = data.PlaceDetailsResponse.results[0].first_name;
if ($scope.name == 0) {
$scope.showAlert();
} else {
$location.path('phoneView');
$ionicLoading.hide();
}
}).error(function(data, status, headers, config) {
$scope.showAlert();
$ionicLoading.hide();
})
}
});
.controller('phoneViewController', function($scope,$rootScope) {
$scope.input = $rootScope.data;
console.log('This is from phoneView',$scope.input);
})
using data factory (Recommended)
angularApp.factory('appDataStorage', function () {
var data_store = [];
return {
get: function (key) {
//You could also return specific attribute of the form data instead
//of the entire data
if (typeof data_store[key] == 'undefined' || data_store[key].length == 0) {
return [];
} else {
return data_store[key];
}
},
set: function (key, data) {
//You could also set specific attribute of the form data instead
if (data_store[key] = data) {
return true;
}
},
unset: function (key) {
//To be called when the data stored needs to be discarded
data_store[key] = {};
},
isSet: function (key) {
if (typeof data_store[key] == 'undefined' || data_store[key].length == 0) {
return false;
}else {
return true;
}
}
};
});
angularApp.controller('phoneController', function($scope, md5, $http, userService, $rootScope, $ionicLoading, $location, $ionicPopup , appDataStorage) {
var data = userService.getUsers(form.entryText,searchTypReturn).success(function(data, status, headers, config) {
$ionicLoading.hide();
$scope.name = data.PlaceDetailsResponse.results[0].first_name;
if ($scope.name == 0) {
$scope.showAlert();
} else {
$location.path('phoneView');
$ionicLoading.hide();
}
}).error(function(data, status, headers, config) {
$scope.showAlert();
$ionicLoading.hide();
});
appDataStorage.set('key1',data);
}
});
angularApp.controller('phoneViewController', function($scope,$rootScope,appDataStorage) {
$scope.input = appDataStorage.get('key1');
console.log('This is from phoneView',$scope.input);
})
Related
I am trying to return some data from an angular service that calls a REST endpoint, however I can't seem to get the data flow right as it is asynchronous.
My service method.
appService.getApplicationSupportFiles = function () {
$http.post("/SEFlex/SEFlexAdmin/GetApplicationSupportFiles")
.then(function (response, status, headers, config) {
if (response.data) {
var list = JSON.parse(response.data.data);
return list;
}
});
}
My controller
function ShowApplicationFilesController($scope, $http, $modalInstance, $log, SEApplicationService, $rootScope, AlertsService) {
$rootScope.closeAlert = AlertsService.closeAlert;
$scope.supportFiles = [];
$scope.originalSupportFiles = [];
$scope.isBusy = false;
$scope.newFile = {
localFile: "",
cloudFile: "",
}
// Load the initial files
$scope.supportFiles = SEApplicationService.getApplicationSupportFiles();
$scope.originalSupportFiles = $scope.supportFiles;
$scope.addApplicationFile = function() {
var file = { LocalPath: $scope.newFile.localFile, ShareName: "b", FolderName: "c", FileName: "d" };
$scope.supportFiles.push(file);
$scope.newFile = { localFile: "", cloudFile: "" };
}
}
The problem is the call to populate supportFiles is asynchronous so supportFiles is undefined when originalSupportFiles tries to copy the values. But also when I then click on addApplicationFile() it cannot push to the value $scope.supportFiles as it thinks it is undefined, so it looks like the values aren't getting loaded at all, even though the service gets called and returns data.
What have I done wrong?
You need to return a promise and go from there - here's one way:
return $http.post("/SEFlex/SEFlexAdmin/GetApplicationSupportFiles").then(function (response, status, headers, config) {
return response.data;
});
And then in your controller:
SEApplicationService.getApplicationSupportFiles().then(function(list) {
$scope.supportFiles = list;
});
I have searched around. People talked mostly about sharing data between controllers using factory. But in my case I would like to share code logic between controllers.
$scope.updatePost = function(){
$http.get(sprintf("/posts/%s.json", post.id)).
success(function(data, status, headers, config) {
$scope.post = data;
})
};
$scope.postComment = function(){
if(!$scope.post.logged){
window.location.href = "/users/sign_in";
}
var data = { post_id: $scope.post.id, content: $scope.content };
$http.post("/comments", data).
success(function(data, status, headers, config) {
$scope.comment_error_messages = [];
$scope.updatePost();
}).error(function(data, status, headers, config) {
$scope.comment_error_messages = data.errors;
});
};
I would like to share these two methods in two controllers. And how do I pass in the $scope from two different controller to my share methods?
Thanks for any help.
app.factory('postService', postService);
postService.$inject = ['$http'];
function postService($http) {
var updatePost = function(post) {
return $http.get(sprintf("/posts/%s.json", post.id))
}
var postComment = function(post, content) {
var data = { post_id: post.id, content: content };
return $http.post("/comments", data);
}
}
And then in your controller(s), you could call these methods
app.controller('myController', myController);
myController.$inject = ['$scope', 'postService'];
function myController($scope, postService) {
$scope.updatePost = function() {
postService
.updatePost($scope.post)
.success(function(data, status, headers, config) {
$scope.post = data;
});
}
$scope.postComment = function(){
// you could move this to the postService if you wanted
if(!$scope.post.logged){
window.location.href = "/users/sign_in";
}
postService
.postComment($scope.post, $scope.content)
.success(function(data, status, headers, config) {
$scope.comment_error_messages = [];
$scope.updatePost();
})
.error(function(data, status, headers, config) {
$scope.comment_error_messages = data.errors;
});
}
Angularjs has provided us with Factories and Servicece, Factories are for business logic to be used in controllers and Services should contain common code that is to be used in multiple places i.e controllers or factories. This is the way how logic is to be shared inside Angularjs app.
Happy Helping!
Create a postService, which you inject into all of the controllers you want. Then have the controllers manage the $scope changes and get the content itself from your service. Below should get you started...
app.factory('postService', function() {
var updatePost = function...
var postComment = function ...
}
app.controller('whateverController', function($scope, postService) {
$scope.post = postService.updatePost();
}
Update - Example of how to bind $scope element to value from service
HTML:
<div>{{comment_error_messages()}}</div>
In your controller:
$scope.comment_error_messages = function () {
return postService.getErrorMessages()
};
And your service:
var getErrorMessages = function () {
...
return val;
}
I can't understand why it does not update the $scope.user_free_status when I set a user free but when I unset the parameter it works perfectly. I need to reload page in one case and not the other...
The datas fetched are stored in the localstorage.
Here is the code:
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: 'InitialCtrl',
resolve: {
theUserFreeStatus: function(DataService) {
return DataService.getUserFreeStatus();
}
}
})
Controller:
.controller('InitialCtrl', function($scope, $state, DataService ,FreeService, SharedService, theUserFreeStatus) {
// Showing set free but not unset or not
if (FreeService.isSetFree()) {
$scope.showSetFree = false;
$scope.showUnSetFree = true;
} else {
$scope.showSetFree = true;
$scope.showUnSetFree = true;
}
// Show the Free status set when arriving on page/app
$scope.user_free_status = theUserFreeStatus;
// Set user as Free
$scope.setFree = function(activity, tags) {
FreeService.setFree(activity, tags).success(function() {
console.log($scope.user_free_status);
$scope.user_free_status = DataService.getUserFreeStatus();
console.log($scope.user_free_status);
$scope.showSetFree = false;
$scope.showUnSetFree = true;
SharedService.goHome();
})
}
//// Free status unset
$scope.unsetFree = function() {
FreeService.unsetFree().success(function() {
$scope.user_free_status = [];
$scope.showSetFree = true;
$scope.showUnSetFree = false;
SharedService.goHome();
});
};
})
The services:
.factory('FreeService', function(WebService, $localstorage, $ionicPopup, DataService, $sanitize, CSRF_TOKEN) {
var cacheFreeStatus = function(free_status) {
$localstorage.setObject('user_free_status', free_status)
};
var uncacheFreeStatus = function() {
$localstorage.unset('user_free_status')
}
return {
setFree: function(activity, tags) {
var status = { SOME STUFF BLABLABLA };
var setFree = WebService.post('setstatus/', sanitizeStatus(status));
setFree.success(function(response) {
console.log('available' + response.flash);
cacheFreeStatus(response.status_response);
})
setFree.error(freeError)
return setFree;
},
unsetFree: function() {
var details = {OTHER STUFF};
var unsetFree = WebService.post('unsetstatus/', details);
unsetFree.success(function(response) {
console.log('unset ' + response.flash);
uncacheFreeStatus(response.status_response);
})
unsetFree.error(freeError)
return unsetFree;
},
isSetFree: function() {
return $localstorage.get('user_free_status');
}
}
})
.service('DataService', function($q, $localstorage) {
return {
activities: $localstorage.getObject('activities'),
getActivities: function() {
return this.activities;
},
user_free_status: $localstorage.getObject('user_free_status'),
getUserFreeStatus: function() {
return this.user_free_status;
}
}
})
* Local Storage Service
------------------------------------------------------*/
.factory('$localstorage', ['$window', function($window) {
return {
set: function(key, value) {
$window.localStorage[key] = value;
},
unset: function(key) {
localStorage.removeItem(key);
},
get: function(key, defaultValue) {
return $window.localStorage[key] || defaultValue;
},
setObject: function(key, value) {
$window.localStorage[key] = JSON.stringify(value);
},
getObject: function(key) {
return JSON.parse($window.localStorage[key] || '{}');
}
}
}])
When setting the user's status, the console returns that the $http call worked but an empty array for the $scope variable I try to set. Once I reload the page I can see the updates displayed. If I unset the user's status, the $scope is properly updated without need to reload the page.
The Webservice is just the $http call.
What am I missing here to have the $scope.user_free_status updated correctly without having to reload the page??
Thanks for your time!
Your data service is injected as service but you have not appended the functions to this.rather you have returned it as part of literal like u do in factory
I'm facing a strange behavior from angularjs.
.factory('configService', function($http){
var base = 'http://Harold:Pituca521zkjOidksjdIIQUdjsdh120#localhost:3000/configuration/';
var getConfig = function(){
return $http.get(base + 'config');
};
var setConfig = function(config){
return $http.post(base + 'update', config);
};
return {
getConfig: getConfig,
setConfig: setConfig
};
})
.controller('ConfigurationController', function($scope, $http, $window, configService){
$scope.config = {};
configService.getConfig()
.success(function(data, status, headers, config){
console.log(data);
$scope.config = data;
});
$scope.saveConfiguration = function(config){
configService.getConfig(config)
.success(function(data, status, headers, config){
$window.location.reload();
});
};
});
When I do a console.log(data) I am getting a URL instead of a object from my localhost that was never hit.
/Users/rodrigoqueirolo/Desktop/factory/public/index.html
I think the problem is because of the URL with credentials but surprisingly I have other 6 routes doing the same thing(CRUD). Here is the full error when I click on a input form.
TypeError: Cannot assign to read only property 'daily_cota_premium_user' of /Users/user/Desktop/factory/public/index.html
at Oa (file://localhost/Users/user/Desktop/final/admin/angular.min.js:102:253)
at Function.d.assign (file://localhost/Users/user/Desktop/final/admin/angular.min.js:104:22)
at O (file://localhost/Users/user/Desktop/final/admin/angular.min.js:210:465)
at $$writeModelToScope (file://localhost/Users/user/Desktop/final/admin/angular.min.js:215:271)
at file://localhost/Users/user/Desktop/final/admin/angular.min.js:215:209
at k (file://localhost/Users/user/Desktop/final/admin/angular.min.js:213:285)
at g (file://localhost/Users/user/Desktop/final/admin/angular.min.js:213:215)
at $$runValidators (file://localhost/Users/user/Desktop/final/admin/angular.min.js:213:499)
at $$parseAndValidate (file://localhost/Users/user/Desktop/fina/admin/angular.min.js:215:130)
at $commitViewValue (file://localhost/Users/user/Desktop/final/admin/angular.min.js:214:272)
Thanks
I'm not a javascript expert, but here's my take:
change the return of your service to return a function that runs the function (odd sounding)
.factory('configService', function($http){
...
return {
getConfig: function(){
return getConfig();
},
setConfig: function(config){
return setConfig(config);
}
};
})
This should return a promise which you can use.
But maybe I'm wrong, looking at your code, it should do as you ask.
This is my html
<form name="change_profile_form" ng-controller="profileController" id="change_profile_form"
ng-submit="changeProfileForm()">
<input id="username" ng-model="data.User.username" name="username" class="profile_input" disabled value="{{ my_profile.User.username }}" required />
This is my js:
var angularApp = angular.module("myApp", ['ngResource', 'myApp.directives']);
// MyProfile constructor function to encapsulate HTTP and pagination logic
angularApp.factory('MyProfile', function($http) {
var MyProfile = function() {
this.user = [];
this.profile = [];
// this.page = 1;
// this.after = '';
// this.perPage = 6;
// this.maxLimit = 100;
// this.rowHeight = 308;
};
MyProfile.prototype.fetch = function() {
var url = "/users/my_profile?callback=JSON_CALLBACK";
$http.defaults.headers.get = { 'Accept' : 'application/json', 'Content-Type' : 'application/json' };
$http.get(url).success(function(data, status, headers, config) {
this.user = data.user;
}.bind(this));
};
return MyProfile;
});
angularApp.controller('profileController', ['$scope', 'MyProfile', '$users', '$parse', function($scope, MyProfile, $users, $parse) {
$scope.my_profile = new MyProfile();
$scope.my_profile.fetch();
$scope.changeProfileForm = function() {
var serverMessage = $parse('change_profile_form.email.$error.serverMessage');
$users.changeProfile(
$scope.data,
function(data, status, headers, config) {
if (typeof data.error === 'undefined' || typeof data.result === 'undefined') {
alert('Server cannot be reached. Please refresh webpage and try again!');
return;
}
if (data.result != null) {
title = "Profile Saved.";
message = "Your <strong>PROFILE</strong> has been<br />successfully changed and saved.";
options = new Object();
options["box-title"] = new Object();
options["box-title"]["padding-left"] = 5;
showOverlayForSuccess(title, message, options);
}
},
function(data, status, headers, config) {
console.log(data);
// error callback
$scope.errors = data.errors;
})
}
}
I checked my network tab in chrome dev tools. The /users/my_profile in the factory is not being triggered.
Where did I get this wrong?
I adopted the logic from http://sravi-kiran.blogspot.com/2013/03/MovingAjaxCallsToACustomServiceInAngularJS.html
My changes are:
a) realize that the input is already using ng-model, hence no point to use value attribute
b) rewrite the factory and use the $q
c) inside the controller, call the factory method directly
Change a)
<form name="change_profile_form" ng-controller="profileController" id="change_profile_form"
ng-submit="changeProfileForm()">
<input id="username" ng-model="data.User.username" name="username" class="profile_input" required style="position: absolute;left:151px;top:<?php echo -138 + $difference; ?>px;"/>
Change b)
// MyProfile constructor function to encapsulate HTTP logic
angularApp.factory('MyProfile', function($http, $q) {
return {
getProfile: function() {
// creating a deferred object
var deferred = $q.defer();
var url = "/users/my_profile?callback=JSON_CALLBACK";
// prepare headers so that CakePHP can accept the call
$http.defaults.headers.get = { 'Accept' : 'application/json', 'Content-Type' : 'application/json' };
// calling the url to fetch profile data
$http.get(url).success(function(data, status, headers, config) {
// passing data to deferred's resolve function on successful completion
deferred.resolve(data);
}).error(function() {
// sending a friendly error message in case of failure
deferred.reject("An error occurred while fetching data");
});
// returning the promise object
return deferred.promise;
}// end getProfile
}// end return
});
Change c) and this is how I call the factory inside the controller
angularApp.controller('profileController', ['$scope', 'MyProfile', '$users', '$parse', function($scope, MyProfile, $users, $parse) {
function getProfile() {
MyProfile.getProfile().then(function(data) {
$scope.data = data.user;
console.log($scope.data);
},
function(errorMessage) {
$scope.error = errorMessage;
});
}
getProfile();