Hopefully this is an easy answer.
I have this directive that controls the background images on the site. It rotates between images, but before it starts setting the background images, it actually preloads them. This is my code for the directive:
// ---
// CONTROLLERS.
// ---
.controller('BackgroundSwitcherController', ['$rootScope', 'Options', 'BackgroundSwitcherService', function ($rootScope, options, service) {
var self = this;
// Declare our variables
self.model = {
staticImage: '',
loading: false,
rotate: false
};
// On state change
$rootScope.$on('$stateChangeStart', function (event, toState) {
// Get our data
var data = toState.data;
// Set our rotate flag
self.model.rotate = options.current.rotateBackground;
// If we are set to rotate
if (self.model.rotate) {
// Make a call to our service (this is called every state change, but only preloaded once)
service.get().then(function (response) {
// Return the images in the response
self.model.images = response;
});
} else {
// Otherwise, reset the images
self.model.images = null;
}
// If we have data
if (data) {
// Reset our variables
self.model.staticImage = data.backgroundImage;
//self.model.loading = true;
}
});
}])
// ---
// SERVICES.
// ---
.service('BackgroundSwitcherService', ['$q', 'PreloaderService', function ($q, preloader) {
this.loading = true;
this.successful = false;
this.percentLoaded = 0;
// Get our images
this.get = function () {
// Our images
var images = [
'assets/backgrounds/Avebury Steps.jpg',
'assets/backgrounds/Avebury Stripe.jpg',
'assets/backgrounds/Avebury.jpg',
'assets/backgrounds/Forest Hills.jpg',
'assets/backgrounds/Inteface.jpg',
'assets/backgrounds/New Oaklands.jpg',
'assets/backgrounds/Primo Delight.jpg',
'assets/backgrounds/Secure 3d.jpg',
'assets/backgrounds/Secure.jpg',
'assets/backgrounds/Sensation Heathers.jpg',
'assets/backgrounds/Sensation.jpg',
'assets/backgrounds/Woman.jpg'
];
// Create our promise
var deferred = $q.defer();
// Preload the images
preloader.preloadImages(images).then(
// Function to handle the changing of the flags when all images have loaded
function handleResolve(imageLocations) {
this.loading = false;
this.successful = true;
deferred.resolve(images);
},
// Function to handle any errors
function handleReject(imageLocation) {
this.loading = false;
this.successful = false;
deferred.reject();
},
// Function that notifies our percentage loaded flag
function handleNotify(event) {
this.percentLoaded = event.percent;
}
);
// Return our promise
return deferred.promise;
};
}])
// ---
// DIRECTIVES.
// ---
.directive('backgroundSwitcher', ['$interval', function ($interval) {
// Variables
var target = null,
timer = null,
images = null,
currentIndex = 0;
// Function to get a random value between two numbers
function getRandomInt(min, max) {
// Return our random number
return Math.floor(Math.random() * (max - min)) + min;
};
var getImageIndex = function (length) {
// Get our random index
var index = getRandomInt(0, images.length);
// If our index matches the current index
if (index == currentIndex) {
// Run again until we get a different index
return getImageIndex(length);
}
// Set our current index our the new index
currentIndex = index;
// Return our index
return index;
}
// Apply the image
var applyImage = function () {
// Get a random image
var image = images[getImageIndex(images.length)];
// Apply our image to our target
applyStaticImage(image);
};
// Apply a static image
var applyStaticImage = function (image) {
// Apply our image to our target
target.css('background-image', 'url(' + escape(image) + ')');
};
// Remove any images
var removeImage = function () {
// Remove our background image
target.css('background-image', 'none');
};
// Start timer function
var startTimer = function () {
// If our timer is not running
if (!timer || timer.$$state.status === 2) {
// Apply our first image
applyImage();
// Start our timer
timer = $interval(changeImage, 10000);
}
};
// Function to change the background image
var changeImage = function () {
// Apply our image
applyImage();
};
// Stop the timer
var stopTimer = function () {
// If we have started our timer
if (timer) {
// Stop it
$interval.cancel(timer);
}
};
return {
restrict: 'A',
controller: 'BackgroundSwitcherController',
link: function (scope, element, attr, controller) {
// Assign our element to our global variable
target = element;
// Watch our image
scope.$watch(function () {
// Return our image
return controller.model.staticImage;
}, function (staticImage) {
// If we have an image
if (staticImage) {
// Stop our timer
stopTimer();
// Apply our static image
applyStaticImage(staticImage);
} else {
// If we are not rotating
if (!controller.model.rotate) {
// Remove any images
removeImage();
}
}
})
// Watch our rotate
scope.$watch(function () {
// Return our rotate flag
return controller.model.images;
}, function (array) {
// Set our variable
images = array;
// If we have some images
if (images) {
// If we don't have a static image
if (!controller.model.staticImage) {
// Start rotating our images
startTimer();
}
} else {
// Remove any images
removeImage();
// Otherwise, stop our timer
stopTimer();
}
});
// Destroy function to cancel all timers
element.on('$destroy', function () {
// Stop our timer
stopTimer();
});
}
}
}]);
and for the actual preloader I have this:
// ---
// SERVICES.
// ---
.factory('PreloaderService', function ($q, $rootScope) {
function Preloader(imageLocations) {
this.imageLocations = imageLocations;
this.imageCount = imageLocations.length;
this.loadCount = 0;
this.errorCount = 0;
this.states = {
PENDING: 1,
LOADING: 2,
RESOLVED: 3,
REJECTED: 4
};
this.state = this.states.PENDING;
// When loading the images, a promise will be returned to indicate
// when the loading has completed (and / or progressed).
this.deferred = $q.defer();
this.promise = this.deferred.promise;
}
// I reload the given images [Array] and return a promise. The promise
// will be resolved with the array of image locations.
Preloader.preloadImages = function (imageLocations) {
var preloader = new Preloader(imageLocations);
return (preloader.load());
};
Preloader.prototype = {
// Best practice for 'instnceof' operator.
constructor: Preloader,
// ---
// PUBLIC METHODS.
// ---
// I determine if the preloader has started loading images yet.
isInitiated: function isInitiated() {
return (this.state !== this.states.PENDING);
},
// I determine if the preloader has failed to load all of the images.
isRejected: function isRejected() {
return (this.state === this.states.REJECTED);
},
// I determine if the preloader has successfully loaded all of the images.
isResolved: function isResolved() {
return (this.state === this.states.RESOLVED);
},
// I initiate the preload of the images. Returns a promise.
load: function load() {
// If the images are already loading, return the existing promise.
if (this.isInitiated()) {
return (this.promise);
}
this.state = this.states.LOADING;
for (var i = 0 ; i < this.imageCount ; i++) {
this.loadImageLocation(this.imageLocations[i]);
}
// Return the deferred promise for the load event.
return (this.promise);
},
// ---
// PRIVATE METHODS.
// ---
// I handle the load-failure of the given image location.
handleImageError: function handleImageError(imageLocation) {
this.errorCount++;
// If the preload action has already failed, ignore further action.
if (this.isRejected()) {
return;
}
this.state = this.states.REJECTED;
this.deferred.reject(imageLocation);
},
// I handle the load-success of the given image location.
handleImageLoad: function handleImageLoad(imageLocation) {
this.loadCount++;
// If the preload action has already failed, ignore further action.
if (this.isRejected()) {
return;
}
// Notify the progress of the overall deferred. This is different
// than Resolving the deferred - you can call notify many times
// before the ultimate resolution (or rejection) of the deferred.
this.deferred.notify({
percent: Math.ceil(this.loadCount / this.imageCount * 100),
imageLocation: imageLocation
});
// If all of the images have loaded, we can resolve the deferred
// value that we returned to the calling context.
if (this.loadCount === this.imageCount) {
this.state = this.states.RESOLVED;
this.deferred.resolve(this.imageLocations);
}
},
// I load the given image location and then wire the load / error
// events back into the preloader instance.
// --
// NOTE: The load/error events trigger a $digest.
loadImageLocation: function loadImageLocation(imageLocation) {
var preloader = this;
// When it comes to creating the image object, it is critical that
// we bind the event handlers BEFORE we actually set the image
// source. Failure to do so will prevent the events from proper
// triggering in some browsers.
var image = angular.element(new Image());
image.bind('load', function (event) {
// Since the load event is asynchronous, we have to
// tell AngularJS that something changed.
$rootScope.$apply(
function () {
preloader.handleImageLoad(event.target.src);
// Clean up object reference to help with the
// garbage collection in the closure.
preloader = image = event = null;
}
);
}).bind('error', function (event) {
// Since the load event is asynchronous, we have to
// tell AngularJS that something changed.
$rootScope.$apply(
function () {
preloader.handleImageError(event.target.src);
// Clean up object reference to help with the
// garbage collection in the closure.
preloader = image = event = null;
}
);
}).prop('src', imageLocation);
}
};
// Return the factory instance.
return (Preloader);
});
Now, when I use this directive all my other images / font awesome icons pause before they load. It causes an issue because the font awesome icons actually briefly show a rectangular box before fully loading and the profile image is blank until loaded.
If I remove my directive from my HTML everything loads fine.
Now I realise that this is happening because it has to load an array of images for the background switcher, so what I would like to do is to not start the preloading until everything else has loaded first.
Can someone tell me if there is a simple way to do this?
Related
In my angular app, I have 2 methods save() and saveTriggers(). saveTriggers() updates all records by calling a web service (C#). I want to make sure that a block of code is executed after all records are updated in saveTriggers() and control is returned to save(). I believe I need to pass something from the saveTriggers() to make finally block execute. I tried various things, nothing works. Using .then() also gives the same error. I am not that good at JS. Can you please guide me.
vm.updatedTriggers = []; // IDs are pushed in
vm.saveTriggers = function () {
if (vm.updatedTriggers.length === 0) {
vm.close();
} else {
vm.saving = true;
vm.save()
.finally(function () { // ERROR - Cannot read property 'finally' of undefined
console.log("Saved all. Closing..."); // Never REACHES here
vm.saving = false;
vm.updated = true;
$uibModalInstance.close(true);
});
}
};
vm.save = function () {
//vm.saving = true;
for (var i = 0; i < vm.updatedTriggers.length; i++) {
var trigger = vm.triggers.find(t => t.id === vm.updatedTriggers[i]);
var input = {
id: trigger.id,
target: trigger.target,
targetInfo: vm.targetData,
event: trigger.event,
eventQuantity: trigger.eventQuantity,
eventQuantityExtra: trigger.eventQuantityExtra
};
rpmService.editDeviceTrigger(input);
/*.finally(function () {
console.log("Updated event"); // Reaches here
vm.updated = true;
return Promise.resolve(2);
});*/ // Commenting this also doesn't help
}
return Promise.resolve(2);
};
rpmService.editDeviceTrigger(input)
public async Task EditDeviceTrigger(EditDeviceTriggerInput input) {
// calls other methods with await
// Doesn't return anything
}
EDIT: Updated Code: I got rid of the error, but the output is not is expected series.
vm.saveTriggers = function () {
vm.saving = true;
vm.save().then
(function success() {
console.log("Returned Result ");
console.log("Saved all. Closing..."); // These lines are executed before the event is upated
vm.saving = false;
$uibModalInstance.close(true);
});
};
vm.save = function () {
var deferred = $q.defer();
for (var i = 0; i < vm.updatedTriggers.length; i++) {
var trigger = vm.triggers.find(t => t.id === vm.updatedTriggers[i]);
var input = {
id: trigger.id,
....
};
rpmService.editDeviceTrigger(input)
.finally(function () {
console.log("Updated event"); // Successfully updates all events
vm.updated = true;
});
}
deferred.resolve();
return deferred.promise;
};
OUTPUT:
Returned Result
Saved all. Closing...
Updated event
EXPECTED OUTPUT:
Updated event
Returned Result
Saved all. Closing...
Thanks.
Usually you dont need $q.defer-related things, but u can do same using it if u want.
Here I guess you just need to collect all your save promises and return new resulting one using $q.all:
vm.save = function () {
const myAwesomePromises = []
for (var i = 0; i < vm.updatedTriggers.length; i++) {
...
const savePromise = rpmService.editDeviceTrigger(input);
savePromise.finally(() => console.log('edit device finally'));// <-- not sure u need this
myAwesomePromises.push(savePromise);
}
return $q.all(myAwesomePromises).finally(() => console.log('All edit device finally'));
};
I have the following function in my Angular-app
$scope.retrieveProjectData = function() {
$scope.projectNumberNoChange = false;
// Only retrieve Data if the ProjectNumber changed
if (currentlySelectedProjectNumber != $scope.feedback.projectNumber.content) {
currentlySelectedProjectNumber = $scope.feedback.projectNumber.content;
// Go to database-reference based on the projectNumber
var projectsRef = firebaseDatabaseRef.child("projects");
var currentChild = projectsRef.child(currentlySelectedProjectNumber);
// retrieve data once and fill $scope.feedback
currentChild.once("value",
// If the project is found
function (dataSnapshot) {
// Fill selectedProject and hand over to writeDataFromSelectedProject()
var selectedProject = dataSnapshot.val();
// Fill $scope.feedback
writeDataFromSelectedProject(selectedProject);
},
// If no data is found
function () {
console.log("No data found");
});
}
// If the projectNumber didn't change, the projectNumberNoChangeMessage will be shown
else {
$scope.projectNumberNoChange = true;
}
};
The user has the possibility to load some data regarding his project-number (for instance: Name, email, tel) to make it faster for the user to fill a form.
In the part:
currentChild.once("value",
// If the project is found
function (dataSnapshot) {
// Fill selectedProject and hand over to writeDataFromSelectedProject()
var selectedProject = dataSnapshot.val();
// Fill $scope.feedback
writeDataFromSelectedProject(selectedProject);
},
// If no data is found
function () {
console.log("No data found");
});
only the first Callback-function is called, even if the projectNumber was not found. How can I use the "failureCallbackOrContext" as described in the docs?
Thanks for taking the time!
The problem was solved. I just checked the dataSnapshot.val() for beeing an object or null!
This is bugging me a bit.
I have a service that handles logo panels and a function that is used to navigate between the different panels.
When getPanels is invoked it sets the currentPanel, index and length on the service when all promises have completed (see $q.all in the getPanels method):
.service('ConfiguratorLogoService', ['$q', 'UploadService', 'LogoService', 'ArrayService', 'SvgService', function ($q, uploadService, logoService, arrayService, helper) {
// Private function to build a file array
var _buildFileArray = function (panels, files) {
//--- Omitted for brevity ---//
};
// Create our service
var service = {
// Create our arrays
panels: [],
files: [],
currentPanel: null,
index: 0,
length: 0,
// Get our panels
getPanels: function (container, garmentId) {
// Create a deferred promise
var deferred = $q.defer();
// Create our arrays
var panels = []
files = [],
promises = [];
// If we have a container
if (container) {
// Get the containers children
var children = container.children()
// Loop through our panel's children
for (var i = 0; i < children.length; i++) {
// Get the current child
var child = angular.element(children[i]),
childId = child.attr('id'),
childTitle = helper.extractText(childId, ':', 1);
// Create our item
var panel = {
id: childId,
title: childTitle
};
// Try to get our item
promises.push(logoService.get(garmentId, panel.id).then(function (response) {
// If we have any data
if (response) {
// Add the file to our array
files.push(response);
}
}));
// Add our child to the array
panels.push(panel);
}
}
// After all the promises have been handled
$q.all(promises).then(function () {
// Get our files
service.files = _buildFileArray(panels, files);
service.panels = panels;
service.currentPanel = panels[0];
service.length = panels.length;
// Resolve our promise
deferred.resolve({
files: service.files,
panels: panels
});
});
// Return our promise
return deferred.promise;
},
// Get our next panel
navigateNext: function () {
// Create a deferred promise
var deferred = $q.defer();
// Get the next index or reset if we reached the end of our list
service.index = service.index === (service.length - 1) ? 0 : service.index += 1;
// Set our active panel
service.currentPanel = service.panels[service.index];
console.log(service.index);
// Resolve our promise
deferred.resolve();
// Return our promise
return deferred.promise;
},
// Get our previous panel
navigatePrevious: function () {
// Get the previous index or set to the end of our list
service.index = service.index === 0 ? service.length - 1 : service.index -= 1;
// Set our active panel
service.currentPanel = service.panels[service.index];
},
// Removes the file from azure
remove: function (index) {
//--- Omitted for brevity ---//
}
};
// Return our service
return service;
}])
which is fine, it works and the first panel is selected.
So, I have a controller, which is attached to a directive. The controller looks like this:
.controller('ConfiguratorLogosDirectiveController', ['ConfiguratorLogoService', 'RowService', function (service, rowService) {
var self = this;
// Set our current panel
self.currentPanel = service.currentPanel;
self.index = service.index;
self.length = service.length;
// Initialization
self.init = function (container, garmentId) {
// Get our panels
return service.getPanels(container, garmentId).then(function (response) {
self.panels = response.panels;
self.files = response.files;
// If we have any panels
if (self.panels.length) {
// Set our current panel
self.currentPanel = service.currentPanel;
self.index = service.index;
self.length = service.length;
}
// Return our response
return response;
})
};
// Map our service functions
self.upload = service.upload;
self.next = service.navigateNext;
self.previous = service.navigatePrevious;
self.remove = service.remove;
}])
As you can see, when I get my panels, I set the currentPanel, index and length on the controller itself which I didn't think I would have to do because when the controller is invoked, it already has a reference to the service values. I figured 2 way binding would come into play and when the service values update, the controller would update too.
Anyway, I update the values after the getPanels method completes successfully. In my directive I have this:
// Invoke on controller load
controller.init(container, scope.garmentId).then(function (response) {
// Map our properties
scope.panels = controller.panels;
scope.files = controller.files;
scope.currentPanel = controller.currentPanel;
scope.index = controller.index;
scope.length = controller.length;
});
which again works fine. In my template I can see the first panel and it looks fine.
So, then came the next step which was my navigate functions. I started with next which I have modified for testing purposes so I can output the controller.index as well as the console.log in the service navigation function.
The directive function looks like this:
scope.next = function () {
controller.next().then(function () {
console.log(controller.index);
});
};
When this method is invoked, I can see in my console that the service increases the index by 1 but the controller still shows 0 which means that 2 way binding is not working.
I am about to update my method in the controller to push the currentPanel and index to the controller, but before I do I thought I would ask here first.
So, does anyone know why my 2 way binding isn't working?
So my current workaround works, but I just don't like it.
In my directive I have done this:
scope.next = function () {
controller.next().then(function () {
console.log(controller.index);
scope.currentPanel = controller.currentPanel;
scope.index = controller.index;
scope.length = controller.length;
});
}
and in the directive controller I have done this:
self.next = function () {
// Try to navigate forward
return service.navigateNext().then(function () {
// Set our current panel
self.currentPanel = service.currentPanel;
self.index = service.index;
self.length = service.length;
console.log(self.index);
});
}
and in my service, it looks the same as before:
// Get our next panel
navigateNext: function () {
// Create a deferred promise
var deferred = $q.defer();
// Get the next index or reset if we reached the end of our list
service.index = service.index === (service.length - 1) ? 0 : service.index += 1;
// Set our active panel
service.currentPanel = service.panels[service.index];
console.log(service.index);
// Resolve our promise
deferred.resolve();
// Return our promise
return deferred.promise;
},
This works, but surely this is not the way it should work.
I have figured it out thanks to this article.
I just had to create an object in my directive and bind the values to that.
Doing that fixed the issues.
I have a function that after 3000 seconds call other function to check new data for a grid.
onViewDestroy: function(view) {
console.log('view destroy');
var me = this;
if(me.timer) clearTimeout(me.timer);
},
afterRender: function(){
// start timer to load log grid in 3 secs
me.logTimer = setTimeout(Ext.bind(me.checkProcessLog, me), 3000);
},
checkProcessLog: function() {
console.log('check process log');
var me = this,
requestGrid = me.getRequestGrid(),
logGrid = me.getProcessLog(),
logStore = logGrid.getStore();
var selectedRecord = requestGrid.getSelectionModel().getSelection()[0];
// if the grid is expanded and the status of the process is pending or in progress
if (!logGrid.collapsed && (selectedRecord.get('Status') == 0 || selectedRecord.get('Status') == 1)) {
logStore.load({
callback: function(records, op, success) {
if (success) {
// start timer to load again log grid in 3 secs
me.logTimer = setTimeout(Ext.bind(me.checkProcessLog, me), 3000);
}
}
});
}
},
The problem that I have is that if I close my view (destroy) when the function has been called then all my variables such:
requestGrid = me.getRequestGrid(),
logGrid = me.getProcessLog(),
logStore = logGrid.getStore();
I get the error:
Cannot read property 'getStore' of undefined
And that make sense because the view has been destroyed. Any workaround to avoid this?
A simple check will do it :
requestGrid = me.getRequestGrid(),
logGrid = me.getProcessLog();
if(!logGrid) return false
logStore = logGrid.getStore();
This also will stop the function beeing called.
I'm trying to learn firebase/angularjs by extending an app to use firebase as the backend.
My forge looks like this
.
In my program I have binded firebaseio.com/projects to $scope.projects.
How do I access the children?
Why doesn't $scope.projects.getIndex() return the keys to the children?
I know the items are in $scope.projects because I can see them if I do console.log($scope.projects)
app.js
angular.module('todo', ['ionic', 'firebase'])
/**
* The Projects factory handles saving and loading projects
* from localStorage, and also lets us save and load the
* last active project index.
*/
.factory('Projects', function() {
return {
all: function () {
var projectString = window.localStorage['projects'];
if(projectString) {
return angular.fromJson(projectString);
}
return [];
},
// just saves all the projects everytime
save: function(projects) {
window.localStorage['projects'] = angular.toJson(projects);
},
newProject: function(projectTitle) {
// Add a new project
return {
title: projectTitle,
tasks: []
};
},
getLastActiveIndex: function () {
return parseInt(window.localStorage['lastActiveProject']) || 0;
},
setLastActiveIndex: function (index) {
window.localStorage['lastActiveProject'] = index;
}
}
})
.controller('TodoCtrl', function($scope, $timeout, $ionicModal, Projects, $firebase) {
// Load or initialize projects
//$scope.projects = Projects.all();
var projectsUrl = "https://ionic-guide-harry.firebaseio.com/projects";
var projectRef = new Firebase(projectsUrl);
$scope.projects = $firebase(projectRef);
$scope.projects.$on("loaded", function() {
var keys = $scope.projects.$getIndex();
console.log($scope.projects.$child('-JGTmBu4aeToOSGmgCo1'));
// Grab the last active, or the first project
$scope.activeProject = $scope.projects.$child("" + keys[0]);
});
// A utility function for creating a new project
// with the given projectTitle
var createProject = function(projectTitle) {
var newProject = Projects.newProject(projectTitle);
$scope.projects.$add(newProject);
Projects.save($scope.projects);
$scope.selectProject(newProject, $scope.projects.length-1);
};
// Called to create a new project
$scope.newProject = function() {
var projectTitle = prompt('Project name');
if(projectTitle) {
createProject(projectTitle);
}
};
// Called to select the given project
$scope.selectProject = function(project, index) {
$scope.activeProject = project;
Projects.setLastActiveIndex(index);
$scope.sideMenuController.close();
};
// Create our modal
$ionicModal.fromTemplateUrl('new-task.html', function(modal) {
$scope.taskModal = modal;
}, {
scope: $scope
});
$scope.createTask = function(task) {
if(!$scope.activeProject || !task) {
return;
}
console.log($scope.activeProject.task);
$scope.activeProject.task.$add({
title: task.title
});
$scope.taskModal.hide();
// Inefficient, but save all the projects
Projects.save($scope.projects);
task.title = "";
};
$scope.newTask = function() {
$scope.taskModal.show();
};
$scope.closeNewTask = function() {
$scope.taskModal.hide();
};
$scope.toggleProjects = function() {
$scope.sideMenuController.toggleLeft();
};
// Try to create the first project, make sure to defer
// this by using $timeout so everything is initialized
// properly
$timeout(function() {
if($scope.projects.length == 0) {
while(true) {
var projectTitle = prompt('Your first project title:');
if(projectTitle) {
createProject(projectTitle);
break;
}
}
}
});
});
I'm interested in the objects at the bottom
console.log($scope.projects)
Update
After digging around it seems I may be accessing the data incorrectly. https://www.firebase.com/docs/reading-data.html
Here's my new approach
// Load or initialize projects
//$scope.projects = Projects.all();
var projectsUrl = "https://ionic-guide-harry.firebaseio.com/projects";
var projectRef = new Firebase(projectsUrl);
projectRef.on('value', function(snapshot) {
if(snapshot.val() === null) {
console.log('location does not exist');
} else {
console.log(snapshot.val()['-JGTdgGAfq7dqBpSk2ls']);
}
});
$scope.projects = $firebase(projectRef);
$scope.projects.$on("loaded", function() {
// Grab the last active, or the first project
$scope.activeProject = $scope.projects.$child("a");
});
I'm still not sure how to traverse the keys programmatically but I feel I'm getting close
It's an object containing more objects, loop it with for in:
for (var key in $scope.projects) {
if ($scope.projects.hasOwnProperty(key)) {
console.log("The key is: " + key);
console.log("The value is: " + $scope.projects[key]);
}
}
ok so val() returns an object. In order to traverse all the children of projects I do
// Load or initialize projects
//$scope.projects = Projects.all();
var projectsUrl = "https://ionic-guide-harry.firebaseio.com/projects";
var projectRef = new Firebase(projectsUrl);
projectRef.on('value', function(snapshot) {
if(snapshot.val() === null) {
console.log('location does not exist');
} else {
var keys = Object.keys(snapshot.val());
console.log(snapshot.val()[keys[0]]);
}
});
$scope.projects = $firebase(projectRef);
$scope.projects.$on("loaded", function() {
// Grab the last active, or the first project
$scope.activeProject = $scope.projects.$child("a");
});
Note the var keys = Object.keys() gets all the keys at firebaseio.com/projects then you can get the first child by doing snapshot.val()[keys[0])