keep data in controller while routing in angular - angularjs

I have following problem:
In AngularJS app I use $routeProvider to load different parts into application
config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/view1', {templateUrl: 'partials/partial1.html', controller: 'MyCtrl1'});
$routeProvider.when('/view2', {templateUrl: 'partials/partial2.html', controller: 'MyCtrl2'});
$routeProvider.when('/users', {templateUrl: 'partials/users.html', controller: 'UserCtrl'});
$routeProvider.otherwise({redirectTo: '/view1'});
}]);
In my UserCtrl I display list of users, which I can manipulate. I read the list of users from json, which looks like this:
{"users":[{"id":1,"name":"test user 2","mail":"test#gmail.com","ringer":true,"active":true},{"id":2,"name":"test user 1","mail":"test#page.com","ringer":false,"active":true},{"id":3,"name":"admin","mail":"admin#example.com","ringer":false,"active":true}]}
In users.html I have controller, which calls service to load the data
'use strict';
/* Controllers */
angular.module('myApp.controllers', []).
controller('MyCtrl1', [function() {
}])
.controller('MyCtrl2', [function() {
}])
.controller('UserCtrl', ['$scope', 'UsersService', function($scope, UsersService) {
//alert(UsersService.fetchData().length);
UsersService.fetchData().then( function( data ){
// Do whatever you want with data, like:
$scope.users = data.users;
});
this.users = $scope.users;
this.selected = [];
this.searchTerm = '';
$scope.selected = this.selected;
}])
;
And finally the service:
'use strict';
/* Services */
// Demonstrate how to register services
// In this case it is a simple value service.
angular.module('myApp.services', []).
value('version', '0.1')
.factory('UsersService', function($http, $q) {
var data = [];
function fetchData() {
var deffered = $q.defer();
if ( _isDataPresent() ) {
deffered.resolve( _returnData() );
} else {
//var deffered = $q.defer();
$http.get('php/users.php')
.success(function (d) {
deffered.resolve(d);
data = d.users;
});
return deffered.promise;
}
}
function _isDataPresent() {
return data.length;
}
function _returnData() {
return data;
}
return { fetchData : fetchData };
});
The problem which I have is following: Every time I load users.html, the data are reloaded from json file. I want to load data only once and keep them in my controller. Now, when I make some changes to the data, switch to different view and come back, all changes are lost.
Any help would be appreciated.
Thanks,
Zbynek

This is a simple example of what I meant:
.factory( 'UsersService', function( $http, $q ) {
var users = [];
function fetchUsers() {
var deffered = $q.defer();
if ( _areUsersPresent() ) {
deffered.resolve( _getUsers() );
} else {
$http.get('php/users.php')
.success(function (response) {
deffered.resolve(response.users);
data = d.users;
});
return deffered.promise;
}
}
function _areUsersPresent() {
return ( users.length() ) ? true : false;
}
function _getUsers() {
return users;
}
function setUsers( newUsers ) {
users = newUsers;
}
return { fetchUsers : fetchUsers, setUsers : setUsers };
});
You can use this on your controllers as
UsersService.fetchUsers().then( function( users ){
// Do whatever you want with data, like:
$scope.users = users;
});
And, whenever you update the users on your controllers, you have to update the content of the service:
// Doing operations with $scope.service, when finish with them:
UsersService.setUsers( $scope.users );

Related

Lazy loading angular services using require.js

I can lazy load a controller by doing the following,
Step1: Add an additional config...
rootModule.config([
"$controllerProvider", function($controllerProvider) {
rootModule.registerController = $controllerProvider.register;
}
]);
Step2: Define the controller against the registerController defined in step 1
angular.module("rootModule").registerController("authController",
function ($scope, $location, $rootScope, authService) {
$scope.userName = "";
$scope.userPwd = "";
$scope.authenticate = function ()...
$scope.testFunction = function ()...
});
Step3: load the controller during routing by doing this,
rootModule
.config([
'$routeProvider',
function ($routeProvider) {
$routeProvider.when('/',
{
templateUrl: 'templates/Login.html',
resolve: {
load: ["$q", function($q) {
var defered = $q.defer();
require(["Controllers/authController"], function() {
defered.resolve();
});
return defered.promise;
}]
}
}).
Now, the problem is I have a service called "authService", which I would like to lazy load, how to do it? Here is the service...
define(function() {
angular.module("rootModule").service("authService", function ($http) {
return {
/////Something code here//////
});
});
It was very simple in the end, thanks to this great blog written by Dan Wahlin.
To load a service in run time according to the routing, I had to do this...
Step 1: Get a reference to $provide.service() method in my rootModule's (module which contains the routing info) config...
rootModule.config(["$controllerProvider","$provide",
"$controllerProvider", "$filterProvider","$compileProvider", function ($controllerProvider, $provide) {
rootModule.registerController = $controllerProvider.register; //for controllers
rootModule.registerService = $provide.service; //for services
rootModule.registerFilter = $filterProvider.register; //for filters
rootModule.registerDirective = $compileProvider.directive; //for directives
rootModule.registerFactory = $provide.factory; //for factory
}
]);
Step 2: Register the service to be loaded dynamically
define(function() {
angular.module("rootModule").registerService("reviewReportsService", function () {
return {
sampleData: "This is some sample data"
}
});
});
Step 3: Resolve the service script file, to load when the respective route is loaded
when('/ReviewAndSubmit',
{
controller: "reviewAndSubmitController",
templateUrl: "templates/ReviewAndSubmit.html",
resolve: {
load: ["$q", function ($q) {
var defered = $q.defer();
require(["Controllers/reviewAndSubmitController"], function () {
defered.resolve();
});
require(["Services/reviewReportsService"], function () {
defered.resolve();
});
return defered.promise;
}]
}
})
Hope this helps someone....

Resolve not resolved before loading controller

I am trying to fetch the value from server side before any controller loads. I am using resolvers to achieve this. Since fetched value needs to be visible to all controllers I extended routeConfig on the following way:
'use strict';
angular.module('myApp', [.....]).
config(['$routeProvider', function ($routeProvider) {
var universalResolves = {
user: function(User, $q, $rootScope) {
var deffered = $q.defer();
User.query({},
function (users) {
deffered.resolve(
$rootScope.activeUser = users[0]
)
}, function(){
deffered.reject();
}
);
return deffered.$promise;
}
};
var customRouteProvider = angular.extend({}, $routeProvider, {
when: function(path, route) {
route.resolve = (route.resolve) ? route.resolve : {};
angular.extend(route.resolve, universalResolves);
$routeProvider.when(path, route);
return this;
}
});
customRouteProvider.when('/users', {
templateUrl: 'partials/users.html',
controller: 'UserController'
});
customRouteProvider.otherwise({redirectTo: '/home'});
}]);
But when I try to print activeUser from the controller I am getting 'undefined'.
.controller('UserController', ['$scope', function ($scope) {
console.log($scope.activeUser.id);
.....
};
Here I am getting the following error:
TypeError: Cannot read property 'id' of undefined.
Why the value is not resolved before loading controller?
There is no such property $promise of deferred object, it is promise:
user: function(User, $q, $rootScope) {
var deffered = $q.defer();
User.query({}, function(users) {
deffered.resolve($rootScope.activeUser = users[0]);
}, function() {
deffered.reject();
});
return deffered.promise;
// ^ don't put $ here
}
Also it's better to inject resolved user to controller then using $rootScope:
.controller('UserController', ['$scope', 'user', function ($scope, activeUser) {
console.log(activeUser.id);
};
An finally just for better codding style, this assignment
$rootScope.activeUser = users[0]
is a little confusing. It's more readable:
$rootScope.activeUser = users[0];
deffered.resolve(users[0]);

$http.get to resource in angularjs

How would i change the following code form $http.get to a $resource
//The created resource (not using it for now)
hq.factory('LogsOfUser', function ($resource) {
return $resource('/HQ/Graph/GetLoggedinTimes?userName=:userName', {
userName: '#userName'
})
});
//The Controller
var ModalViewLogActionsCtrl = function ($scope, $http, $log, LogsOfUser, $modal) {
$scope.openLogs = function (userName) {
$http.get("/HQ/Graph/GetLoggedinTimes?userName=" + userName).success(function (data) {
var modalInstance = $modal.open({
templateUrl: 'LogView.html',
controller: 'ModalLogViewInstance',
resolve: {
items: function () {
//$scope.items = data;
$log.log(data);
$scope.items = data;
return $scope.items; //return data;
},
userName: function () {
return userName;
}
}
});
}).error(function () {
alert("eror :(");
});;
};
};
You've already done most of the work. All you need now is to call the service inside the controller :
LogsOfUser.query({
userName: userName
}, function success(data) {
//your code
}, function err() {
alert("Error")
});
Use query to get an array of data, and get to get a single document.
Here is a example how to call a resource from a controller:
app.controller('MainCtrl', function($scope, $resource) {
var userName = 'Bob';
var LoggedinTimes = $resource('/HQ/Graph/GetLoggedinTimes');
var data = LoggedinTimes.get({userName : userName}, function () {
console.log(data);
});
});
First, you would want to move data-related logic behind a Service, so your controller doesn't know about server-specifics. More importantly, your Service becomes reusable as all services in AngularJS are global singletons. your controller stays small, as it should be.
Next, your controller would call getLoggedIntimes() and work with the outcome as if the data is there. The result of a $resource.get() or similar functions return an empty object or array which fills itself when the REST call returns with data.
In your service you would do the actual $resource.get().
something along the lines of the following pseudo code:
//The Controller
var ModalViewLogActionsCtrl = function ($scope, MyService, $log, LogsOfUser, $modal) {
$scope.openLogs = function (userName) {
var items = MyService.getLoggedInTimes(userName);
var modalInstance = $modal.open({
templateUrl: 'LogView.html',
controller: 'ModalLogViewInstance',
resolve: {
items: function () {
$scope.items = items;
return $scope.items;
},
userName: function () {
return userName;
}
}
});
};
};
app.service('MyService', function ($resource) {
var loggedInResource = $resource('/HQ/Graph/GetLoggedinTimes/:userName');
return {
getLoggedInTimes: functio(username) {
return loggedInResource.get({
username: username
});
}
};
});

Prevent multiple ajax calls when re-using a controller/factory

just starting out really with Angular and need some advice regarding preventing repeated ajax requests for the same data when re-using a controller with multiple view.
So I have say 6 views all referencing the same controller but different views
app.js
(function() {
var app = angular.module('myApp', ['ngRoute','ui.unique']);
app.config(function ($routeProvider) {
// Routes
$routeProvider
.when('/',
{
controller: 'SamplesController',
templateUrl: 'app/views/home.html'
})
.when('/view2/',
{
controller: 'SamplesController',
templateUrl: 'app/views/main.html'
})
.when('/view3/:rangeName',
{
controller: 'SamplesController',
templateUrl: 'app/views/samples.html'
})
.when('/view4/:rangeName',
{
controller: 'SamplesController',
templateUrl: 'app/views/samples.html'
})
.when('/view5/',
{
controller: 'SamplesController',
templateUrl: 'app/views/basket.html'
})
.when('/view6/',
{
controller: 'SamplesController',
templateUrl: 'app/views/lightbox.html'
})
.otherwise({ redirectTo: '/' });
});
}());
samplesController.js
(function() {
var SamplesController = function ($scope, SamplesFactory, appSettings, $routeParams) {
function init() {
// back function
$scope.$back = function() {
window.history.back();
};
// app settings
$scope.settings = appSettings;
// samples list
SamplesFactory.getSamples()
.success(function(data){
var returnSamples = [];
for (var i=0,len=data.length;i<len;i++) {
if (data[i].range === $routeParams.rangeName) {
returnSamples.push(data[i]);
}
}
$scope.samples = returnSamples;
})
.error(function(data, status, headers, config){
// return empty object
return {};
});
// variables for both ranges
$scope.rangeName = $routeParams.rangeName;
// click to change type
$scope.populate = function(type) {
$scope.attributeValue = type;
};
};
init();
};
SamplesController.$inject = ['$scope','SamplesFactory', 'appSettings', '$routeParams'];
angular.module('myApp').controller('SamplesController', SamplesController);
}());
samplesFactory.js
(function () {
var SamplesFactory = function ($http) {
var factory = {};
factory.getSamples = function() {
return $http.jsonp('http://www.website.com/app/index.php?callback=JSON_CALLBACK');
};
return factory;
};
SamplesFactory.$inject = ['$http'];
angular.module('myApp').factory('SamplesFactory', SamplesFactory);
}());
So with this - every time a new view is loaded the ajax request is made again - how would I re-purpose to have only a single request happen?
As always thanks in advance
Carl
UPDATE: Answer marked below but I also had success by changing the "cache" config item/property (whatever its called) to true in the jsonp request
return $http.jsonp('http://www.website.com/app/index.php?callback=JSON_CALLBACK',{cache: true});
You could change your factory in this way:
(function () {
var SamplesFactory = function ($http) {
var factory = {},
samples = $http.jsonp('http://www.website.com/app/index.php?callback=JSON_CALLBACK');
factory.getSamples = function() {
return samples;
};
return factory;
};
SamplesFactory.$inject = ['$http'];
angular.module('myApp').factory('SamplesFactory', SamplesFactory);
}());
Now getSamples() returns a promise that you should manage in your controllers.

Load views after services in angularjs

In my angular app I have a view, a controller and a service.
The service load resources ex:service load persons and initialize value with the result.
I want to load my view after my service finish his function to load his resources.
var myApp = angular.module('myApp',[]);
myApp.controller('PersonsCtrl', ($scope, Persons) {
$scope.persons = Persons.data;
});
myApp.factory('Persons', {
data: [[function that load resources => take time]]
});
So I want to load my controller when my service finish his initialization.
Some ideas?
Assuming you have a route provider, here's a basic example. When the promise is resolved, "personData" will be injected into your controller. There's not much info about what your service does, so I had to give something very generic.
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/persons', {
controller: 'PersonsCtrl',
templateUrl: 'persons.html',
resolve: {
personData: ['Persons', function(Persons) {
return Persons.getData();
}]
}
});
}]);
myApp.controller('PersonsCtrl', ($scope, personData) {
$scope.persons = personData;
});
myApp.factory('Persons', {
getData: function() {//function that returns a promise (like from $http or $q)};
});
Maybe try using promises, example below
var myApp = angular.module('myApp',[]);
myApp.controller('PersonsCtrl', ($scope, Persons) {
$scope.persons = Persons.getData().then(function(response){
//do whatever you want with response
});
});
myApp.factory('Persons', function ($http, $q) {
return {
getData: function () {
var def = $q.defer();
$http.get('url').
success(function (response) {
def.resolve(response);
})
return def.promise();
}
}
});

Resources