AngularJS workflow problems - list -> detail -> list - angularjs

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.

Related

master detail in ionic using json data

I'm using ionic v1 and trying to create list and its own detail, but when I click on the item list the detail view doesn't display the data of the item selected.
Factory
.factory('Eventos', function($http){
var eventos = [];
return {
all : function() {
return $http.get("eventos.json").then(function(response) {
eventos = response;
return eventos;
});
},
get : function(eventoId) {
for(i = 0; i < eventos.length; i++) {
if(eventos[i].id === eventoId){
return eventos[i];
}
}
return null;
}
}});
Controller
.controller('EventosCtrl', function($scope, $stateParams, Eventos) {
Eventos.all().then(function(eventos) {
$scope.eventos = eventos.data;
});
})
.controller('EventoCtrl', function($scope, $stateParams, Eventos) {
$scope.evento = Eventos.get($stateParams.eventoId);
}
But if I use the data static in the code this works, but I don't know whats wrong here.
If you're not running a webserver of any kind and just testing with file://index.html, then you're probably running into same-origin policy issues.
Many browsers don't allow locally hosted files to access other locally hosted files.
Try referencing your data as an object
var obj = {list:null};
$http.get('data.json').success(function(data) {
// you can do some processing here
obj.list = data;
});
return obj;

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/

Load data to dropdown in angularjs

I am new to this angular js.
In my code a dropdown control has been loaded with some items, i know this list should be loaded from a xml file.
I want to know where/how it is been coded to load the data. Check this code and help me...
<select size="10" ng-model="model.addSelection" ng-options="a.name for a in availableList | availableList | propertyFilter:model.availableFilter" class="listBoxStyle" wat-focus/>
angular.module('readbackSelectModule', [])
.filter('availableList', [
function () {
return function (input) {
if (!angular.isArray(input)) return input;
var out = [];
for (var i = 0; i < input.length; i++) {
if (input[i].visible == true)
out.push(input[i]);
}
return out;
}
}
])
.filter('plotList', [
function () {
return function (input) {
if (!angular.isArray(input)) return input;
var out = [];
for (var i = 0; i < input.length; i++) {
if (input[i].visible == false)
out.push(input[i]);
}
return out;
}
}
])
.controller('readbackSelectCtrl', ['$scope', '$modalInstance', 'generalDataService', 'keyService', 'propertyFilterFilter', 'availableListFilter', 'plotListFilter', 'plotPanelService',
function ($scope, $modalInstance, generalDataService, keyService, propertyFilterFilter, availableListFilter, plotListFilter, plotPanelService) {
var CANCEL_MESSAGE = 'cancel';
var GET_DATA_MESSAGE = 'getdata';
var REQUEST_PROPERTY_LIST_MESSAGE = 'requestplotdata';
var SUBSCRIBE = 'plotsubscribe';
var UNSUBSCRIBE = 'plotunsubscribe';
var STREAM_TYPE = '4';
$scope.model = {};
$scope.model.addSelection;
$scope.model.removeSelection;
$scope.model.availableFilter;
// List of properties
$scope.availableList = [];
};
Use Angular's $http service.
You can do it in your readbackSelectCtrl, like this:
...
$scope.model = {};
$scope.model.addSelection;
$scope.model.removeSelection;
$scope.model.availableFilter;
$http.get(/** insert XML file URL here **/)
.success(function(data) {
// process the data and extract the XML elements you want
$scope.availableList = xmlExtractedArray;
})
.error(function(err) {
// handle error
});
Check this plunker for a working demo.

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..
}

Angular - getting data with a factory

I'm modifying the way an app gets it's data.
The app originally got data on the page load like this
<script>
var entries = <?php echo $this->entriesData; ?>;
</script>
and then in the angular controller
$scope.entries = entries;
Requirements changed so that a user would be able to go forward or backward one week so I added a factory to the app that is supposed to load the data when the page loads and then refresh the data when changeweek() is fired. According to the console the factory (loadData) fires when the page loads and then again on changeweek() and data comes down from the server as expected. The problem is that the view doesn't display the data. I'm assuming that I'm not returning the data to the $scope.entries correctly. Help please!
var timeEntry = angular.module('timeEntry', ['ui.select2']);
timeEntry.factory('loadData', function($http){
var url = 'index/getentries';
var returnData = '';
var factory = {};
factory.getData = function(sDate, eDate){
$.post(url, {week_start: sDate, week_end: eDate})
.done(function(json, textStatus){
if (textStatus === 'success') {
returnData = json;
}else{
// handle fail
}
})
.fail(function(){})
.always(function(){});
return returnData;
};
return factory;
});
timeEntry.controller('timeEntryController', ['$scope','loadData', function ($scope, loadData) {
$scope.loading = true;
$scope.date = [];
$scope.date.start = startDate;
$scope.date.end = endDate;
$scope.entries = loadData.getData($scope.date.start, $scope.date.end);
$scope.weekTotal = 0;
$scope.timeEntryError = [];
$scope.$watch('entries', function(newNames, oldNames) {
$scope.dataCount = newNames.length;
$scope.timeEntryError = [];
calculateTotals();
checkDayHours();
checkSpecialDays();
}, true);
$scope.changeWeek = function(change) {
sdate = moment($scope.date.start);
edate = moment($scope.date.end);
$scope.date.start = sdate.add(moment.duration(change, 'week')).format('YYYY-MM-DD');
$scope.date.end = edate.add(moment.duration(change, 'week')).format('YYYY-MM-DD');
$scope.entries = loadData.getData();
};
}]);

Resources