Angular - getting data with a factory - angularjs

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();
};
}]);

Related

TypeError on Angular form submit

I'm getting the error TypeError: Cannot set property 'songname' of null, when trying to submit an update to a row in firebase. The console error is saying that its happening with the record.songname in the edit function. I'm able to add, but not edit rows.
myApp.controller('ProductsCtrl', ['$scope', '$firebaseArray', function($scope, $firebaseArray, $http){
var myProducts = new Firebase('https://url-to-db.firebaseio.com/songs');
$scope.products = $firebaseArray(myProducts);
$scope.showForm = function(){
$scope.addFormShow = true;
$scope.editFormShow = false;
clearForm();
window.scrollTo(0, 0);
}
$scope.hideForm = function(){
$scope.addFormShow = false;
}
function clearForm(){
$scope.songname = '';
$scope.artist = '';
$scopt.tags = '';
}
$scope.addFormSubmit = function(){
$scope.products.$add({
songname:$scope.songname,
artist:$scope.artist,
tags:$scope.tags,
date:Date.now()
});
$('.messages').addClass('alert alert-success').slideDown().show().html('The song has been added').fadeOut(2000);
clearForm();
}
$scope.dateFormat = 'MM-dd-yy # HH:mm:ss';
$scope.showProduct = function(product){
$scope.editFormShow = true;
$scope.addFormShow = false;
$scope.songname = product.songname;
$scope.artist = product.artist;
$scope.tags = product.tags;
$scope.date = product.date;
}
$scope.editFormSubmit = function(){
var id = $scope.id;
var record = $scope.products.$getRecord(id);
record.songname = $scope.songname;
record.artist = $scope.artist;
record.tags = $scope.tags;
$scope.products.$save(record);
$('.messages').addClass('alert alert-info').slideDown().show().html('The job has been edited').fadeOut(2000);
clearForm();
$('.edit-form').toggle();
}
}]);
The $scope.id attribute is probably not getting set.
Try setting the id in the scope in your showProduct function or whichever function initializes the $scope variables for the item.
$scope.showProduct = function(product){
$scope.id = product.id;
..............

Two $firebaseArrays on one page & one ctrl

I would like to use two different $firebaseArrays on one view with one controller. But only one of them works and the other only works if i put him in his own controller.
from my factory file:
.factory("AlphaFactory", ["$firebaseArray",
function($firebaseArray) {
var ref = firebase.database().ref('alpha/');
return $firebaseArray(ref);
}
])
.factory("BetaFactory", ["$firebaseArray",
function($firebaseArray) {
var ref = firebase.database().ref('beta/');
return $firebaseArray(ref);
}
])
and my controller:
.controller('DemoCtrl', function($scope, AlphaFactory, BetaFactory) {
$scope.alphaJobs = AlphaFactory;
$scope.addalphaJob = function() {
$scope.alphaJobs.$add({
Testentry: $scope.loremipsum,
timestamp: Date()
});
$scope.alphaJob = "";
};
$scope.betaJobs = BetaFactory;
$scope.addbetaJob = function() {
$scope.betaJobs.$add({
Testentry2: $scope.dolorest,
timestamp: Date()
});
$scope.betaJob = "";
};
)}
Are you sure it is not a simple matter of a promise has not finished?
var alphaJobs = AlphaFactory;
alphaJobs.$loaded().then(function() {
// Do something with data if needed
$scope.alphaJobs = alphaJobs;
});
var betaJobs = BetaFactory;
betaJobs.$loaded().then(function() {
// Do something with data if needed
$scope.betaJobs = betaJobs;
});

Firebase child_removed not working in real-time

I am following tutsplus Real time web apps with Angularjs and Firebase.
I have main.js (below) which allows me to add and change items in Firebase in real time with no refresh of the browser (in Chrome and Safari).
However when I delete a message from Firebase I have to refresh the browser for the message list to update - so not in real time. I can't see where the problem is.
/*global Firebase*/
'use strict';
/**
* #ngdoc function
* #name firebaseProjectApp.controller:MainCtrl
* #description
* # MainCtrl
* Controller of the firebaseProjectApp
*/
angular.module('firebaseProjectApp')
.controller('MainCtrl', function ($scope, $timeout) {
var rootRef = new Firebase('https://popping-inferno-9738.firebaseio.com/');
var messagesRef = rootRef.child('messages');
$scope.currentUser=null;
$scope.currentText=null;
$scope.messages=[];
messagesRef.on('child_added', function(snapshot){
$timeout(function() {
var snapshotVal = snapshot.val();
console.log(snapshotVal);
$scope.messages.push({
text: snapshotVal.text,
user: snapshotVal.user,
name: snapshot.key()
});
});
});
messagesRef.on('child_changed', function(snapshot){
$timeout(function() {
var snapshotVal = snapshot.val();
var message = findMessageByName(snapshot.key());
message.text = snapshotVal.text;
});
});
messagesRef.on('child_removed', function(snapshot){
$timeout(function() {
var snapshotVal = snapshot.val();
var message = findMessageByName(snapshot.key());
message.text = snapshotVal.text;
});
});
function deleteMessageByName(name){
for(var i=0; i < $scope.messages.length; i++){
var currentMessage = $scope.messages[i];
if(currentMessage.name === name){
$scope.messages.splice(i, 1);
break;
}
}
}
function findMessageByName(name){
var messageFound = null;
for(var i=0; i < $scope.messages.length; i++){
var currentMessage = $scope.messages[i];
if(currentMessage.name === name){
messageFound = currentMessage;
break;
}
}
return messageFound;
}
$scope.sendMessage = function(){
var newMessage = {
user: $scope.currentUser,
text: $scope.currentText
};
messagesRef.push(newMessage);
};
});
The code that is invoked when a message is deleted from Firebase:
messagesRef.on('child_removed', function(snapshot){
$timeout(function() {
var snapshotVal = snapshot.val();
var message = findMessageByName(snapshot.key());
message.text = snapshotVal.text;
});
});
This code never actually deletes the message from the HTML/DOM.
There is a convenient deleteMessageByName method to handle the deletion. So if you modify the above to this, it'll work:
messagesRef.on('child_removed', function(snapshot){
$timeout(function() {
deleteMessageByName(snapshot.key());
});
});

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