Can't pass data between controllers? - javascript

I don't get what I'm doing wrong, I am trying to globally store and pass data from one controller to another via a service. I stored the data in one controller and confirmed that it was stored at the beginning of my buildReportController. Then, when I click a button on my UI, it opens reportResultsController. However, issue is, I can store the data correctly in buildReportController via locationHistoryService.store() but when I go to reportResultsController and calllocationHistoryService.get(), thepreviousLocationvariable inlocationHistoryService` is empty as if the data was never set. Any ideas on how why or how I can "globally" store data and pass it between controllers? Below is my attempt. Thanks!
In reportView.js
angular.module('reportView', [])
.service('locationHistoryService', function(){
var previousLocation = "";
return {
store: function(location){
previousLocation = location;
},
get: function(){
return previousLocation;
}
};
});
In buildReport.js
angular.module('buildReport', ['reportView'])
.controller('buildReportController', ['$rootScope', 'locationHistoryService'], function($rootScope, locationHistoryService){
$rootScope.$on('$locationChangeSuccess', function(e, newLocation, oldLocation){
locationHistoryService.store(oldLocation);
console.log("Old location: ", oldLocation);
});
}
In reportResults.js
angular.module('reportResults', ['reportView'])
.controller('reportResultsController', ['$rootScope', 'locationHistoryService'], function($rootScope, locationHistoryService){
console.log("location : ", locationHistoryService.get());
}

The locationHistoryService.get() method in reportResults.js is called before it is set in buildReport.js.
It would be better if you announce when the previousLocation variable has been set.
In reportView.js
angular.module('reportView', [])
.service('locationHistoryService',['$rootScope'] ,function($rootScope){
var previousLocation = "";
return {
store: function(location){
previousLocation = location;
$rootScope.$broadcast('previous-location-set');
},
get: function(){
return previousLocation;
}
};
});
In reportResults.js
angular.module('reportResults', ['reportView'])
.controller('reportResultsController', ['$rootScope', 'locationHistoryService'], function($rootScope, locationHistoryService){
$rootScope.$on('previous-location-set', function(){
console.log("location : ", locationHistoryService.get());
});
}

Your webapp should have only one module which is automatically bootstrapped by angular, and other modules as dependencies. The syntax of writing service is incorrect. You wrote .service but returning object which .factory should return. Here is working example of your code http://codepen.io/Chyngyz/pen/NxbdpW?editors=101
Also you wrote the safe for minification syntax of controllers incorrect, the function block of controller should be the last item in the array.

Related

Creating a function in Angular

I have two controllers both with a save button which essentially does the same thing. So I want to put it in a reusable function that both the controllers can use. I have tried to do this by creating a normal function and passing the model object, as well as $http, but the function is executing before the save button is pressed leading to all the params being set to undefined. What way should I create a function that both these controllers can use?
Here how code looks:
app.controller('addCtlr',['$scope','$http','$location',
function($scope, $http, $location){
$scope.save = function(){
var practices = [];
var url = "https://maps.googleapis.com/maps/api/geocode/json?address="+$scope.location.address.replace(/ /g,"+");
//If there are practices
if($scope.days){
for(dayName in $scope.days){ //Loop through the days object
var day = $scope.days[dayName]; //Gets the day pratice object
practices.push({day: dayName, start_time: day.startTime, end_time: day.endTime}); //Add the pratice object to the practices array
}
}
//Call to get the lat lng and formatted address from Google Map's service
$http.get(url)
.then(function(response){
locJSON = response.data.results[0]; //The JSON response
//createing an object to send to the backend to save
var locObj = {
name: $scope.location.name,
address: locJSON.formatted_address,
location: locJSON.geometry.location,
cost: $scope.location.cost,
practices: practices,
notes: $scope.location.notes
};
//Sending using POST since a new object is being created
$http.post('/api/locations', locObj)
.then(
$location.path('/')
);
});//*/
};
}]);
This is how my function looked:
function saveLocation(location, days, $http){
var practices = [];
var url = "https://maps.googleapis.com/maps/api/geocode/json?address="+location.address.replace(/ /g,"+");
//If there are practices
if(days){
for(dayName in days){ //Loop through the days object
var day = days[dayName]; //Gets the day pratice object
practices.push({day: dayName, start_time: day.startTime, end_time: day.endTime}); //Add the pratice object to the practices array
}
}
//Call to get the lat lng and formatted address from Google Map's service
$http.get(url)
.then(function(response){
locJSON = response.data.results[0];
//createing an object to send to the backend to save
var locObj = {
name: location.name,
address: locJSON.formatted_address,
location: locJSON.geometry.location,
cost: location.cost,
practices: practices,
notes: location.notes
};
//Sending using POST since a new object is being created
$http.post('/api/locations', locObj)
.then(
//$location.path('/') //Redirects the user back to the homepage
);
});
}
This is how I was calling the function in the new controller:
app.controller('addCtlr',['$scope','$http','$location',
function($scope, $http, $location){
$scope.save = saveLocation(location, days, $http);
}]);
You can use service for this. Service is a singleton so will be created only one instance. And You can inject it by a dependency injector to controllers. You can read more here
You can create a service for your shared functionality and can inject it into your controller like below
var app=angular.module('app',[])
app.service('myService',function($http){
this.saveLocation=function(){
//Your code
}
});
and then in your controller you can inject it like below
app.controller('myController',['$scope','myService',function($scope,myService){
//use myService function to call save functionality
}]);
Also if you are using $http, you should keep this in mind that it returns a promise so you need to write all the code which is dependent on the value of this promise in a success callback otherwise your code will run before this callback and you will have undefined values for those variables.
Use a factory() service. You can define a set of functions and return them as an object. This object can then be injected within any controller:
app.factory('sharedFactory', [function() {
"use strict";
return {
myFunction: function() {
console.log("sharedFunction");
}
};
}]);
app.controller('AnyController', ['sharedFactory', function(sharedFactory) {
"use strict";
sharedFactory.myFunction();
}]);

Best way to pass variables between controllers

I am using three Angular controllers:
**Controller1**
var fetchStudentDetails = function(){
var sDetails = myService.getList(//url-1 here);
sDetails.then(function (data) {
$scope.studentData = data.list;
var studentId = $scope.studentData[0].id;
});
}
fetchStudentDetails();
$scope.loadSecondLevel = function(){
$state.go('secondLevel');
}
**Controller2**
var fetchClassDetails = function(){
var sDetails = myService.getList(//url-2 here);
sDetails.then(function (data) {
$scope.classData = data.list;
var className = $scope.classData[0].name;
});
}
fetchClassDetails();
$scope.loadThirdLevel = function(){
$state.go('thirdLevel');
}
**Controller3**
$scope.putStudentDetails = function(){
// Here I need studentId,className for updateResource
var sDetails = myService.updateResource(//url-3 here);
sDetails.then(function (data) {
});
}
Where I have to pass studentId (in Controller1), className (in Controller2) into a function which in Controller3. I tried with $rootScope, it is working but when refresh the page $rootScope values become empty. Does anyone know how to do this?
Your question could be split into two aspects:
1. How to share data between controllers
The best practice to share data in Angular 1.x is using factory, store the shared data in a factory service, and expose access methods to controllers:
factory('DetailData', function(myService, $q){
var _details;
function __getDetailData(){
return details
}
function __setDetailData(){
return myService.getList().then(function(data){
_details = data;
})
}
return {
getDetailData: __getDetailData,
setDetailData: __setDetailData
}
})
controller('myContrller', function(DetailData, $scope){
$scope.data = DetailData.getDetailData();
})
2. How to persist data when page refreshed,
you can use localStorage to keep data persistent during page reloading, many tools & libraries can achieve this, for example ngStorage, or you could reset the data from server every time your angular application started:
//this would register work which would be performed
//when app finish loading and ready to start.
angular.module('app').run(function(DetailData){
DetailData.setDetailData();
})
Depending on what problem you are solving.
There are three options:
Is to save data to $rootScope
Is to use $scope.$emit & $scope.$on functions.
Use a custom Service to store the data
And if you need to save data, so it was available after full page reload - localStorage.
Hey this question are responded in Passing data between controllers in Angular JS?
But the simple response is in the services.

Passing data between controllers using service and confusion with using [ '$scope' , 'service' , function($scope, service){}]

I am badly stuck with this problem of passing data from one controller to other in angularJS. Before my code was: whenever I click on templateController's div, it would trigger setTemplate with the help of ng-click... Now my objective was to send templateController's selected data to ReplyController...
After reading forum posts here, i decided to create a service called 'selectionService', so that i can transmit data between 2 controllers...
//Defined Service
proApp.service('selectionService', function() {
var selected_template;
addTemplate = function(newObj) {
selected_template = newObj;
};
getTemplate = function(){
return selected_template;
};
});
//Controller 1... where templates are selected
proApp.controller('TemplateController',function($scope, selectionService)
{
$scope.templates = [
{template_name:"Template 1", template_text:"We apologize for the interruption."} ,
{template_name:"Template 2", template_text:"Thank you for contacting us."} ,
} ,
];
// on ng-click
$scope.setTemplate = function(tmp)
{
selectionService.addTemplate(tmp);
}
});
// Controller 2 ... supposed to catch the selected template.
proApp.controller('ReplyController', function($scope, selectionService)
{
$scope.template = selectionService.getTemplate();
});
So whenever i run this code, i started getting this error
Object [object Object] has no method addTemplate...
Again I tweeked the code where they were suggesting to use Squarebrackets and put $scope , servicename and then write function with same parameters.. I don't understand why I should do this? Event after doing some changes like
[ '$scope' , 'service' , function($scope, service){}] ,
I am still not able to figure out the solution to pass data from one controller to other using service.
Could you help? What am I missing? I am very new to angularJS way of doing stuff.
I think it's actually quite simple. You just need to add your methods to the service by using this.. Currently they are declared on window. Change your service declaration to use this...
proApp.service('selectionService', function() {
var selected_template;
this.addTemplate = function(newObj) {
selected_template = newObj;
};
this.getTemplate = function(){
return selected_template;
};
});
As for using the array notation for dependencies, it's a good practice but it's not required. It'll save you from headaches if you ever run your code through a minifier.

injected service is not being identified in the controller

var login_app = angular.module('login_app',[]);
login_app.factory('login_service', function($http) {
return {
login: function() {
//return the promise directly.
return $http.get('/service/login')
.then(function(result) {
//resolve the promise as the data
return result.data;
});
}
}
});
login_app.controller('login_controller',
['$scope',
function($scope,login_service){
$scope.login_username = "";
$scope.login_password = "";
$scope.remember_login = false;
$scope.login_button_action = function(){
login_service.login();
}
}]);
I have a login form which is under the scope of this controller and works fine.
whenever I press the login button, login_button_action is getting called via ng-click directive.
my problem is that I keep getting this error in my JavaScript console.
ReferenceError: login_service is not defined
Is there something wrong with the wya my controller is using the service ?
You need to add the login_service to the definitions above:
login_app.controller('login_controller', ['$scope','login_service',
function($scope,login_service){
// Your code using the service.
}]);
And of course...the service script file needs to be linked in the document.
To be complete...you do not have to do this at all if you are not considering minification or such. In that case it can just be
login_app.controller('login_controller', function($scope, login_service){
// Your code using the service.
}]);

save $location parameters state AngularJS

How do I save URL parameters state throughout lifecycle of application using pushState?
Page load.
Go to "/search" via href
submitSearch() through filter fields where $location.search(fields)
Go to "/anotherPage" via href
Go back to "/search" via href
Search paramters are set back to what they last were.
Is this a built in feature somewhere?
If not what's the best way to go about this?
If you're planning on a mostly single page website through pushState, you might want to get an intimate understanding of $routeProvider (http://docs.angularjs.org/api/ngRoute.%24routeProvider).
To go further down the rabbit hole, I would recommend looking at the ui-router module: (https://github.com/angular-ui/ui-router). $stateProvider (from ui-router) and $routeProvider work very similar, so sometimes the ui-router docs can give insights that you can't find in the poor documentation of the $routeProvider.
I reccomend going through the five page ui-router documentation (https://github.com/angular-ui/ui-router/wiki) page by page.
After all that preamble, here's the practical: you would set up a factory that holds history data and use the controller defined in your $routeProvider/$stateProvider to access and manipulate that data.
Note: the factory is a service. A service is not always a factory. The namespace goes:
angular.module.<servicetype[factory|provider|service]>.
This post explains the service types: https://stackoverflow.com/a/15666049/2297328. It's important to remember that they're all singletons.
Ex:
var myApp = angular.module("myApp",[]);
myApp.factory("Name", function(){
return factoryObject
});
The code would look something like:
// Warning: pseudo-code
// Defining states
$stateProvider
.state("root", {
url: "/",
// Any service can be injected into this controller.
// You can also define the controller separately and use
// "controller: "<NameOfController>" to reference it.
controller: function(History){
// History.header factory
History.pages.push(History.currentPage);
History.currentPage = "/";
}
})
.state("search", {
url: "/search",
controller: function(History, $routeParams) {
History.lastSearch = $routeParams
}
});
app.factory('<FactoryName>',function(){
var serviceObjectSingleton = {
pages: []
currentPage: ""
lastSearch: {}
}
return serviceObjectSingleton
})
If you're wondering what the difference between $routeProvider and $stateProvider is, it's just that $stateProvider has more features, mainly nested states and views... I think.
The easiest way is using cookies, angularjs provides a wrapping service for that.
Simply when you go to "/search" save your current URL parameters with "$cookieStore.put()" and once you've back you've got what you need with "$cookieStore.get()".
See the documentation at angularjs cookie store
I made a locationState service, you simply give it the values you want to persist and it stores them in the URL. So you can store all the state you want across all routes in your app.
Use it like this:
angular.module('yourapp')
.controller('YourCtrl', function ($scope, locationState) {
var size = locationState.get('size');
;
// ... init your scope here
if (size) {
$scope.size = size;
}
// ...and watch for changes
$scope.$watch('size', locationState.setter('size'));
}
Here's the code:
// Store state in the url search string, JSON encoded per var
// This usurps the search string so don't use it for anything else
// Simple get()/set() semantics
// Also provides a setter that you can feed to $watch
angular.module('yourapp')
.service('locationState', function ($location, $rootScope) {
var searchVars = $location.search()
, state = {}
, key
, value
, dateVal
;
// Parse search string
for (var k in searchVars) {
key = decodeURIComponent(k);
try {
value = JSON.parse(decodeURIComponent(searchVars[k]));
} catch (e) {
// ignore this key+value
continue;
}
// If it smells like a date, parse it
if (/[0-9T:.-]{23}Z/.test(value)) {
dateVal = new Date(value);
// Annoying way to test for valid date
if (!isNaN(dateVal.getTime())) {
value = dateVal;
}
}
state[key] = value;
}
$rootScope.$on('$routeChangeSuccess', function() {
$location.search(searchVars);
});
this.get = function (key) {
return state[key];
};
this.set = function (key, value) {
state[key] = value;
searchVars[encodeURIComponent(key)] = JSON.stringify(value);
// TODO verify that all the URI encoding etc works. Is there a mock $location?
$location.search(searchVars);
};
this.setter = function (key) {
var _this = this;
return function (value) {
_this.set(key, value);
};
};
});

Categories

Resources