UI not updating when property changed within $interval callback - angularjs

I have a service which defines a background $interval. It watches local storage to see if some server updates didn't get made due to connectivity problems. When it finds some, it periodically tries to contact the server with the updates, deleting them from local storage when it succeeds.
Upon success, the callback also updates some properties of an angular view/model. Those changes should cause the UI to update, and elsewhere in the code of various controllers they do.
But within the $interval callback in that background service the changes do not cause the UI to update. I thought it might be some kind of failure to $apply, so I added a $rootScope.$apply() call. But that caused an error because a $digest was already in progress.
That tells me $digest is running after the callback executes -- which is what I would've expected -- but it's not causing the UI to update.
I then did a $rootScope.$emit() within the service, after the view/model was updated, and listened for the event within the controller where the UI should've updated. Within the listener I can see that the view/model the controller is based on was successfully updated by the service.
Here is the service:
app.factory('bkgndUpdates', function($interval, $q, $rootScope, dataContext, localStorage) {
var _interval = null;
var _service = {
get isRunning() { return _interval != null },
};
_service.start = function() {
if( _interval != null ) return;
_interval = $interval(function() {
// check to ensure local storage is actually available
// and has visits to process
var visits = localStorage.visits;
if( !visits || (visits.length == 0) ) return;
var recorded = [];
var promises = [];
var offline = false;
for( var idx = 0; idx < visits.length; idx++ )
{
var visit = visits[idx];
promises.push(dataContext.doVisitAction("/Walk/RecordVisit", visit)
.then(
function(resp) {
offline &= false;
var home = dataContext.findHomeByID(visit.addressID);
if( home != null ) {
// these values should cause a map icon to switch from yellow to blue
// elsewhere in the app that's what happens...but not here.
home.visitInfo.isDirty = false;
home.visitInfo.inDatabase = true;
home.visitInfo.canDelete = true;
home.visitInfo.followUpID = resp.FollowUpID;
}
recorded.push(visit.addressID);
},
function(resp) {
// something went wrong; do nothing, as the item is already
// in local storage
offline |= true;
})
);
}
$q.all(promises).then(
function(resp) {
for( var idx = 0; idx < recorded.length; idx++ )
{
localStorage.removeVisitByID(recorded[idx]);
}
if( !localStorage.hasVisits ) {
$interval.cancel(_interval);
_interval = null;
}
$rootScope.$emit("visitUpdate");
},
function(resp) {
alert("some promise failed");
});
}, 30000);
}
_service.stop = function() {
$interval.cancel(_interval);
_interval = null;
}
return _service;
});
Here's the controller for the map:
app.controller("mapCtrl", function ($scope, $rootScope, $location, dataContext) {
$scope.dataContext = dataContext;
$scope.mapInfo = {
center:dataContext.center,
zoom: dataContext.zoom,
pins: dataContext.pins,
};
$scope.$on('mapInitialized', function(evt, map){
$scope.dragend = function() {
$scope.dataContext.center = $scope.map.getCenter();
$scope.dataContext.getMapData($scope.map.getBounds());
}
$scope.zoomchanged = function() {
$scope.dataContext.zoom = $scope.map.getZoom();
$scope.dataContext.getMapData($scope.map.getBounds());
}
$scope.dataContext.getMapData($scope.map.getBounds())
.then(
function() {
$scope.mapInfo.pins = $scope.dataContext.pins;
},
function() {});
});
$scope.click = function() {
dataContext.pinIndex = this.pinindex;
if( dataContext.activePin.homes.length == 1)
{
dataContext.homeIndex = 0;
$location.path("/home");
}
else $location.path("/unit");
};
$rootScope.$on("visitUpdate", function(event) {
// I added this next line to force an update...even though the
// data on both sides of the assignment is the same (i.e., it was
// already changed
$scope.mapInfo.pins = $scope.dataContext.pins;
event.stopPropagation();
});
});
Here's the (partial) template (which relies on ngMap):
<div id="mapframe" class="google-maps">
<map name="theMap" center="{{mapInfo.center.toUrlValue()}}" zoom="{{mapInfo.zoom}}" on-dragend="dragend()" on-zoom_changed="zoomchanged()">
<marker ng-repeat="pin in mapInfo.pins" position="{{pin.latitude}}, {{pin.longitude}}" title="{{pin.streetAddress}}" pinindex="{{$index}}" on-click="click()"
icon="{{pin.icon}}"></marker>
</map>
</div>
So why isn't the UI updating?

Related

Angularjs State transition

I am building a hybrid mobile app using ionic framework and cordova (first time).I am having problems with state transition because by default angular renders the template before completing the transition.This makes the the app look slow (when you click a menu item and wait for it to come).This happens only for those who load data from local storage or service! My Question is: How can I make the template come empty in the moment I click the menu item , then show a loader until the template is ready.Below is some code is use in my menu controller for the state transition!
//I use ng-click="navigateTo('state name')"
$scope.navigateTo = function (stateName) {
$timeout(function () {
$mdSidenav('left').close();
if ($ionicHistory.currentStateName() != stateName) {
$ionicHistory.nextViewOptions({
disableAnimate: false,
disableBack: true
});
$state.go(stateName);
}
}, ($scope.isAndroid == true ? 1000 : 0));
};// End navigateTo.
Below is the controller code for the view that needs a solution
appControllers.controller("calendar_Ctrl", function($scope,$rootScope, $state,$stateParams, $ionicHistory, $filter, $q, $timeout, $log, MaterialCalendarData, $moment) {
$scope.isAnimated = $stateParams.isAnimated;
$scope.selectedDate = null;
$scope.weekStartsOn = 0;
$scope.dayFormat = "d";
$scope.disableFutureDates = false;
$scope.directionn = "horizontal";
$scope.setDirection = function(direction) {
$scope.directionn = direction;
$scope.dayFormat = direction === "vertical" ? "EEEE, MMMM d" : "d";
};
$scope.dayClick = function(date) {
$scope.msg = "You clicked " + $filter("date")(date, "MMM d, y h:mm:ss a Z");
};
$scope.setContentViaService = function() {
var today = new Date();
MaterialCalendarData.setDayContent(today, '<span> :oD </span>')
}
$scope.getItems = function(){
if(localStorage.getItem("eventsData")){
var eventsData = localStorage.getItem("eventsData");
return JSON.parse(eventsData);
}else{
return [];
}
}
var events = $scope.getItems();
// You would inject any HTML you wanted for
// that particular date here.
var numFmt = function(num) {
num = num.toString();
if (num.length < 2) {
num = "0" + num;
}
return num;
};
var loadContentAsync = false;
$log.info("setDayContent.async", loadContentAsync);
$scope.setDayContent = function(date) {
var key = [date.getFullYear(), numFmt(date.getMonth()+1), numFmt(date.getDate())].join("-");
var data = (events[key]||[{ type: ""}]);
if (loadContentAsync) {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve(data);
});
return deferred.promise;
}
return data;
};
$scope.isAnimated = $stateParams.isAnimated;
});
Thank You Very Much for your time and help!!
Hi Use $ionicLoading Service to solve this problem,
http://ionicframework.com/docs/api/service/$ionicLoading/

Angular.js - Digest is not including $scope member changes

I have a service that includes:
newStatusEvent = function(account, eventId, url, deferred, iteration) {
var checkIteration;
checkIteration = function(data) {
if (iteration < CHECK_ITERATIONS && data.Automation.Status !== 'FAILED') {
iteration++;
$timeout((function() {
return newStatusEvent(account, eventId, url, deferred, iteration);
}), TIME_ITERATION);
} else {
deferred.reject('failure');
}
};
url.get().then(function(data) {
if (data.Automation.Status !== 'COMPLETED') {
checkIteration(data);
} else {
deferred.resolve('complete');
}
});
return deferred.promise;
};
runEventCheck = function(account, eventId, modalInstance, state) {
newStatusEvent(account, eventId, urlBuilder(account, eventId),
$q.defer(), 0)
.then(function() {
scopeMutateSuccess(modalInstance, state);
}, function() {
scopeMutateFailure(modalInstance);
})["finally"](function() {
modalEventConfig.disableButtonsForRun = false;
});
};
var modalEventConfig = {
disableButtonsForRun: false,
statusBar: false,
nodeStatus: 'Building',
statusType: 'warning'
}
function scopeMutateSuccess(modalInstance, state){
/////////////////////////////////////////////////
//THE SCPOPE DATA MEMBERS THAT ARE CHANGED BUT
//CURRENT DIGEST() DOES NOT INCLUDE THE CHANGE
modalEventConfig.statusType = 'success';
modalEventConfig.nodeStatus = 'Completed Successfully';
//////////////////////////////////////////////////
$timeout(function() {
scopeMutateResetValues();
return modalInstance.close();
}, TIME_CLOSE_MODAL);
state.forceReload();
}
modalEventConfig.scopeMutateStart = scopeMutateStart;
modalEventConfig.close = scopeMutateResetValues;
return {
runEventCheck: runEventCheck,
modalEventConfig: modalEventConfig
};
And here is the controller:
angular.module('main.loadbalancer').controller('EditNodeCtrl', function($scope, $modalInstance, Configuration, LoadBalancerService, NodeService, StatusTrackerService, $state, $q) {
NodeService.nodeId = $scope.id;
$q.all([NodeService.getNode(), LoadBalancerService.getLoadBalancer()]).then(function(_arg) {
var lb, node;
node = _arg[0], lb = _arg[1];
$scope.node = node;
return $scope.save = function() {
$scope.modalEventConfig.scopeMutateStart();
return NodeService.updateNode({
account_number: lb.customer,
ip: node.address,
port: node.port_number,
label: node.label,
admin_state: node.admin_state,
comment: node.comment,
health_strategy: {
http_request: "" + node.healthMethod + " " + node.healthUri,
http_response_accept: "200-299"
},
vendor_extensions: {}
}).then(function(eventId) {
return StatusTrackerService.runEventCheck(lb.customer, eventId,
$modalInstance, $state);
});
}
});
$scope.modalEventConfig = StatusTrackerService.modalEventConfig;
The issue I am having is in the service. After a successful resolve in newStatusEvent and scopeMutateSuccess(modalInstance, state); runs... the modalEventConfig.statusType = 'success'; and modalEventConfig.nodeStatus = 'Completed Successfully'; changes aren't reflected in the view.
Normally, this would be because a digest() is needed to make angular.js aware of a change. However, I have verified in the stack(chromium debugger) that a digest() was called earlier in the stack and is still in effect when the scope members are mutated in function scopeMutateSuccess(modalInstance, state);
What is weird, if I add $rootScope.$apply() after modalEventConfig.nodeStatus = 'Completed Successfully';...then Angular.js will complain a digest() is already in progress...BUT...the view will successfully update and reflect the new changes in from the scope members nodeStatus and statusType. But, obviously this is not the answer/appropriate fix.
So, the question is why isn't the digest() that is currently running from the beginning of the stack(stack from chromium debugger) making angular.js aware of the scope changes for modalEventConfig.statusType = 'success' and modalEventConfig.nodeStatus = 'Completed Successfully'? What can I do to fix this?
$scope.modalEventConfig = StatusTrackerService.modalEventConfig; is a synchronous call, you need treat things asynchronously .
You need wait on promise(resolved by service) at calling area also, i.e. in the controller .
Fixed it.
function scopeMutateSuccess(modalInstance, state){
/////////////////////////////////////////////////
//THE SCPOPE DATA MEMBERS THAT ARE CHANGED BUT
//CURRENT DIGEST() DOES NOT INCLUDE THE CHANGE
modalEventConfig.statusType = 'success';
modalEventConfig.nodeStatus = 'Completed Successfully';
//////////////////////////////////////////////////
$timeout(function() {
scopeMutateResetValues();
state.forceReload();
return modalInstance.close();
}, TIME_CLOSE_MODAL);
}
I am using ui-router and I do a refresh with it useing $delegate. I place state.forceReload(); in the $timeout...the scope members update as they should. I have no idea why exactly, but I am glad this painful experience has come to a end.

$watch not updating scope variable

First I want to say that I am a complete beginner in AngularJS and just attempting to understand the basic concepts. I have a background in Java and PHP.
I am building a part of a website. Right now the angular app only consists of opening and closing 2 drop down menus registrationDropDown and loginDropDown. I want them to work so that only one can be open at a time ie. if I open one, and the other is already open, the older one is forced to close.
I have a service to manage the variables that determine whether the drop downs should be open or closed and 2 controllers, one for login and one for registration, both include $watch for the respective variables.
THE PROBLEM
I want the app to work so that only one of the drop downs can be open at one time.
JSFIDDLE: http://jsfiddle.net/F5p6m/3/
angular.module("ftApp", [])
.factory('dropDownService', function () {
var loginDropDownStatus = false;
var registrationDropDownStatus = false;
return {
getLoginDropDownStatus: function () {
return loginDropDownStatus;
},
showLoginDropDown: function () {
console.log("showing login drop down");
registrationDropDownStatus = false;
loginDropDownStatus = true;
console.log("loginDropDownStatus" + loginDropDownStatus + "registrationDropDownStatus" + registrationDropDownStatus);
},
hideLoginDropDown: function () {
console.log("hiding login drop down");
loginDropDownStatus = false;
console.log("loginDropDownStatus" + loginDropDownStatus);
},
getRegistrationDropDownStatus: function () {
return registrationDropDownStatus;
},
showRegistrationDropDown: function () {
console.log("showing registration drop down");
registrationDropDownStatus = true;
loginDropDownStatus = false;
console.log("registrationDropDownStatus" + registrationDropDownStatus);
},
hideRegistrationDropDown: function () {
console.log("hiding registration drop down");
registrationDropDownStatus = false;
console.log("registrationDropDownStatus" + registrationDropDownStatus);
}
};
}) .controller("LoginDropDownController", function ($scope, dropDownService) {
$scope.loginDropDownStatus = dropDownService.getLoginDropDownStatus();
$scope.$watchCollection('loginDropDownStatus', function(newValue, oldValue) {
console.log("watcher is working");
console.log("value is " + newValue + oldValue);
console.log("LOGIN new value is " + newValue);
$scope.loginDropDownStatus = newValue;
});
$scope.toggleDropDown = function () {
if ( $scope.loginDropDownStatus == false ) {
dropDownService.showLoginDropDown();
dropDownService.hideRegistrationDropDown();
$scope.loginDropDownStatus = true;
} else if ( $scope.loginDropDownStatus == true ) {
dropDownService.hideLoginDropDown();
$scope.loginDropDownStatus = false;
}
};
})
.controller("RegistrationDropDownController", function ($scope, dropDownService) {
$scope.registrationDropDownStatus = dropDownService.getRegistrationDropDownStatus();
$scope.$watch('registrationDropDownStatus', function(newValue, oldValue) {
console.log("watcher is working");
console.log("value is " + newValue + oldValue);
console.log("new value is " + newValue);
$scope.registrationDropDownStatus = newValue;
});
$scope.toggleDropDown = function () {
if ( $scope.registrationDropDownStatus == false ) {
dropDownService.showRegistrationDropDown();
dropDownService.hideLoginDropDown();
$scope.registrationDropDownStatus = true;
} else if ( $scope.registrationDropDownStatus == true ) {
dropDownService.hideRegistrationDropDown();
$scope.registrationDropDownStatus = false;
}
};
})
Edit:
Here is probably the shortest option:
angular.module("ftApp", [])
.controller("ctrl", function ($scope) {
$scope.toggle = function(menu){
$scope.active = $scope.active === menu ? null : menu;
}
})
FIDDLE
One controller, no service.
Previous Answer:
I think you have quite a bit of code to get something very simple done. Here is my solution:
angular.module("ftApp", [])
.service('dropDownService', function () {
this.active = null;
this.toggle = function(menu){
this.active = this.active === menu ? null : menu;
}
})
.controller("LoginDropDownController", function ($scope, dropDownService) {
$scope.status = dropDownService;
$scope.toggleDropDown = function () {
dropDownService.toggle("login");
};
})
.controller("RegistrationDropDownController", function ($scope, dropDownService) {
$scope.status = dropDownService;
$scope.toggleDropDown = function () {
dropDownService.toggle("reg");
};
})
FIDDLE
You can make it even shorter by only using one controller. You wouldn't even need the service then.
You are overcomplicating things. All you need your service to hold is a property indicating which dorpdown should be active.
Then you can change that property's value from the controller and check the value in the view to determine if a dropdown should be shown or hidden.
Something like this:
<!-- In the VIEW -->
<li ng-controller="XyzController">
<a ng-click="toggleDropdown()">Xyz</a>
<div ng-show="isActive()">Dropdown</div>
</li>
/* In the SERVICE */
.factory('DropdownService', function () {
return {
activeDropDown: null
};
})
/* In the CONTROLLER */
controller("XyzDropdownController", function ($scope, DropdownService) {
var dropdownName = 'xyz';
var dds = DropdownService;
$scope.isActive = function () {
return dropdownName === dds.activeDropdown;
};
$scope.toggleDropdown = function () {
dds.activeDropdown = (dds.activeDropdown === dropdownName) ?
null :
dropdownName;
};
})
See, also, this short demo.
Based on what exactly you are doing, there might be other approaches possible/preferrable:
E.g. you could use just on controller to control all dropdowns
or you could use two instances of the same controller to control each dropdown.
See my updated fiddle. I simplified the code and removed the service. Because you just used two variables to control visibility, you don't need a service nor $watch. You need to keep variables in the $rootScope, otherwise changes in a controller is not visible to another controller due to isolated scopes.
angular.module("ftApp", [])
.controller("LoginDropDownController", function ($scope, $rootScope) {
$rootScope.loginDropDownStatus = false;
$scope.toggleDropDown = function () {
if ($rootScope.loginDropDownStatus == false) {
$rootScope.registrationDropDownStatus = false;
$rootScope.loginDropDownStatus = true;
} else if ($rootScope.loginDropDownStatus == true) {
$rootScope.loginDropDownStatus = false;
}
};
}).controller("RegistrationDropDownController", function ($scope, $rootScope) {
$rootScope.registrationDropDownStatus = false;
$scope.toggleDropDown = function () {
if ($rootScope.registrationDropDownStatus === false) {
$rootScope.loginDropDownStatus = false;
$rootScope.registrationDropDownStatus = true;
} else if ($scope.registrationDropDownStatus === true) {
$rootScope.registrationDropDownStatus = false;
}
};
})
This code can be simplified further. I'll leave that to you.

AngularJS local storage - initialize app retrieving local-stored data

I'm pretty new to angular and I'm trying to avoid losing items added on a simple cart application when the user refreshes the page.
I'm using angularLocalStorage (https://github.com/agrublev/angularLocalStorage) but don't know how to retrieve it back the content.
My lines:
var myApp = angular.module('ionicApp', ['ionic','angularLocalStorage']);
myApp.factory('prodottiData', function($http) {
return {
getFooOldSchool: function(callback) {
$http.get('http://192.168.1.128/hongkongapp/?json=get_recent_posts&post_type=product&custom_fields=all').success(callback);
}
}
});
myApp.factory('DataService', function() {
var myCart = new shoppingCart("AngularStore");
return {
cart : myCart
};
});
myApp.controller('MyController', function MyController ($scope, storage, $ionicSideMenuDelegate, prodottiData, DataService, $sce) {
$scope.toggleLeft = function() {
$ionicSideMenuDelegate.$getByHandle('mainMenu').toggleLeft();
};
$scope.toggleMySecondMenuLeft = function() {
$ionicSideMenuDelegate.$getByHandle('mySecondMenu').toggleLeft();
};
//adding menu data to the scope object
prodottiData.getFooOldSchool(function(data) {
$scope.menu = data;
});
//adding the cart to the scope object
$scope.cart = DataService.cart;
$scope.to_trusted = function(html_code) {
return $sce.trustAsHtml(html_code);
}
images = $scope.menu;
$scope.showloader = function(){
$scope.shownImage = this.post.thumbnail_images.full.url;
$scope.itemDesc = this.post.content;
$scope.itemPrice = this.post.custom_fields._price[0];
$scope.productName = this.post.title;
$scope.skuProdotto = this.post.id;
}
});
Now, if I check local storage on the console I can see something is really stored, but I miss the way to re-populate the cart at startup.
Any help would be great!
why not just using browser local storage ?
you can add it to your services.js as a new service and just used that.
var storeService = myAppServices.factory('storeService', function() {
var service =
{
setClientData:function(client_details)
{
window.localStorage.setItem( "client_data", JSON.stringify(client_details) );
client_data = client_details;
},
getClientData:function()
{
if (client_data == null)
{
client_data = JSON.parse(window.localStorage.getItem("client_data"));
}
return client_data;
}
}
var client_data = null;
return service;
});
From the documentation, to retrieve, it's storage.get('key')
So, to check after refresh:
if (storage.get('someKey')){
$scope.retrieved_value = storage.get('someKey');
}else{
// whatever
}
You can use localStorage instead windows.localStorage.
if(typeof(Storage)!=="undefined")
{
// Code for localStorage/sessionStorage.
var hello = "Hello World!!";
localStorage.setItem("hello",hello);
// get string
console.log(localStorage.getItem("hello")); // will return 'Hello World!!'
var me = {name:'abel',age:26,gender:'male'};
localStorage.setItem("user", JSON.stringify(me));
//fetch object
console.log(localStorage.getItem("user")); // will return {"name":"myname","age":99,"gender":"myGender"}
var objetos = JSON.parse(localStorage.getItem("user"));
console.log(objetos.name);
}
else
{
// Sorry! No Web Storage support..
}

AngularJS workflow problems - list -> detail -> list

I'm new to Angular and am trying to implement an infinite scroll. I'm running into two challenges that I'm not sure how to solve. First I want to show a list of apps using the ngInfiniteScroll. This is working nicely. I used their example code to get up and running.
Now I want to click on an app to view the details. I have the view displaying, but I'm not sure how to share the apps list from one controller to the next.
//// AppCatalog constructor function to encapsulate HTTP and pagination logic
applicationCatalog.factory('AppCatalog', function($http) {
var AppCatalog = function() {
this.apps = [];
this.selectedApps = [];
this.busy = false;
this.after = '';
this.page = 1;
this.maxresults = 50;
};
AppCatalog.prototype.nextPage = function() {
if (this.busy) return;
this.busy = true;
var restUrl = "/web-portal/apps/catalog/search?page="+this.page+"&maxresults="+this.maxresults;
$http({method: 'GET', url: restUrl}).success(function(data) {
var apps = data;
for (var i = 0; i < apps.length; i++) {
this.apps.push(apps[i]);
}
this.after = this.apps[this.apps.length - 1].id;
this.page += 1;
this.busy = false;
}.bind(this));
};
return AppCatalog;
});
applicationCatalog.controller('appCatalogController', ['$scope', 'AppCatalog',
function(scope, AppCatalog) {
scope.appcatalog = new AppCatalog();
}
]);
So first off, instantiating a new AppCatalog() into appcatalog doesn't feel right, as it results in starting over every time I go from list to details back to list. This code does work and infinite scroll correctly produces the next batch of apps. Shouldn't the apps list be stored for the lifecycle of my angular page and only refreshed when I refresh the page or navigate away from it. How would I change this code to do this?
My second challenge, but probably related, is that when I want to view details of an app, which I already have downloaded in the apps list I don't know how to access them.
applicationCatalog.controller("appDetailsController", ['$scope', '$routeParams', 'AppCatalog',
function(scope, params, appcatalog) {
scope.appcatalog = appcatalog;
var id = params.id;
scope.id = id;
///TODO - this doesn't work appcatalog is empty
var appcatalog = $scope.appcatalog;
for(var i = 0; i < appcatalog.apps.length; i++) {
var app = appcatalog.apps[i];
if(app.id == params.id) {
$scope.app = app;
return;
}
}
}
]);
In the appDetailsController I want to pull the app from the list based on the id. But I don't know how to access the apps list from the second controller. It should already be in memory.
Finally when I return the list (this relates to my first question) from this details controller it starts over. My paging info, current location in the scroll list, etc are all lost. This workflow must be a common one, but I'm not sure where to go from here.
Thanks in advance for your help.
Jared
Ok. After deciding that my JavaScript needs a little work and I need to be careful about copying and pasting solutions from the web here is my updated solution. This is the fixed solution to my questions above. I don't know if it's perfect and am open to suggestions for improvement.
var applicationCatalog = angular.module('applicationCatalog', ['infinite-scroll', 'ngRoute']);
///necessary hack
applicationCatalog.run(function($http){
window.http = $http;
});
/*
* Small config with template mapping
*/
applicationCatalog.config(function($routeProvider){
$routeProvider
.when("/", {
templateUrl: '../assets/scripts/angular/views/appList.html'
})
.when("/apps/details/:id", {
templateUrl: '../assets/scripts/angular/views/appDetails.html'
})
.otherwise({
redirectTo: "/"
});
});
applicationCatalog.factory('AppCatalog', function($http) {
var apps = [];
var activeApps = [];
var activeAppIds = '';
var busy = false;
var after = '';
var page = 1;
var maxresults = 50;
var nextPage = function () {
if (busy) return;
busy = true;
var restUrl = "/web-portal/apps/catalog/search?page="+page+"&maxresults="+maxresults;
$http({method: 'GET', url: restUrl}).success(function(data) {
var newApps = data;
for (var i = 0; i < newApps.length; i++) {
apps.push(newApps[i]);
}
after = apps[apps.length - 1].id;
page += 1;
busy = false;
}.bind(this));
};
var addNewApp = function (id, appcatalog) {
this.activeApps.push(id);
var ids = "";
for(var i = 0; i < this.activeApps.length; i++) {
if(ids.length > 0) ids += ",";
ids += this.activeApps[i];
}
this.activeAppIds = ids;
}
return {
apps: apps,
activeApps: activeApps,
activeAppIds: activeAppIds,
busy: busy,
page: page,
after: after,
maxresults: maxresults,
nextPage: nextPage,
addNewApp: addNewApp
};
});
applicationCatalog.controller('appCatalogController', ['$scope', 'AppCatalog',
function(scope, appcatalog) {
var catalog = appcatalog;
if(catalog.apps.length == 0) catalog.nextPage();
scope.appcatalog = catalog;
}
]);
applicationCatalog.controller('appCatalogSelectedController', ['$scope', 'AppCatalog',
function(scope, appcatalog) {
var catalog = appcatalog;
scope.appcatalog = catalog;
}
]);
applicationCatalog.controller('appDetailsController', ['$scope', '$routeParams', 'AppCatalog',
function(scope, params, appcatalog) {
var catalog = appcatalog;
scope.appcatalog = catalog;
var id = params.id;
for(var i = 0; i < catalog.apps.length; i++) {
var app = catalog.apps[i];
if(app.id == params.id) {
scope.app = app;
return;
}
}
}
]);
The major difference here is in how the factory is set up. It's not constructing a new object in the controller, but rather using dependency injection to put the factory into the scope of the controllers. This is example is working with a list and 3 controllers which all share the appcatalog factory. This is working very nicely now.
I'm still not sure about the best way to remember my location in the scroll area and make sure it returns to the same spot when coming from detail and returning to the list.

Resources