How treat data with Firebase and AngularJS - angularjs

I'm trying building an App with AngularJS and Firebase - any one have tried?
I'm following this and using the fist method (implicit sync.) to synchronise my models with Firebase.
In my app I want to have two db/json files (Firebase Url) (kind of stuff) one for users and one for votes.
So I did :
var url = 'mypath.firebaseio.com/votes';
var promise = angularFire(url, $scope, 'votes', []);
promise.then(function() {
$scope.removeVotes = function() {
$scope.votes.splice($scope.toRemove, 1);
$scope.toRemove = null;
};
});
//then I push my votes with a function
$scope.addVote = function() {
$scope.votes.push({
from:user.username,
to:$scope.voteTo,
motivation:$scope.voteMotivation, date:today
});
};
and this works fine I can see my votes in the html rendered with my
<li ng-repeat="vote in votes">
ok
so I did same thing for users:
var url1 = 'mypath.firebaseio.com/users';
var promise1 = angularFire(url1, $scope, 'users', []);
promise1.then(function() {
$scope.users.push({
name: user.username,
pic: user.profile_image_url
});
$scope.removeUser = function() {
$scope.users.splice($scope.toRemove, 1);
$scope.toRemove = null;
};
});
Now I can see my users rendered in my html markup with
<li ng-repeat="user in users">
but that s no trace of my data at https://alex-jpcreative.firebaseio.com/users
Any ideas why?

Related

AngularJs requires page refresh after API call

I am writing an angularjs app. The requirement is to display the user's data once the user logs in. So when an user successfully logs in, he/she is routed to the next view. My application is working fine upto this point. Now as the next view loads I need to display the existing records of the user. However at this point I see a blank page, I can clearly see in the console that the data is being returned but it is not binding. I have used $scope.$watch, $scope.$apply, even tried to call scope on the UI element but they all result in digest already in progress. What should I do? The page loads if I do a refresh
(function () {
"use strict";
angular.module("app-newslist")
.controller("newsController", newsController);
function newsController($http,$q,newsService,$scope,$timeout)
{
var vm = this;
$scope.$watch(vm);
vm.news = [];
vm.GetTopNews = function () {
console.log("Inside GetTopNews");
newsService.GetNewsList().
then(function (response)
{
angular.copy(response.data, vm.news);
}, function () {
alert("COULD NOT RETRIEVE NEWS LIST");
});
};
var el = angular.element($('#HidNews'));
//el.$scope().$apply();
//el.scope().$apply();
var scpe = el.scope();
scpe.$apply(vm.GetTopNews());
//scpe.$apply();
}
})();
Thanks for reading
you don't show how you're binding this in your template.. I tried to recreate to give you a good idea.
I think the problem is the way you're handling your promise from your newsService. Try looking at $q Promises. vm.news is being updated by a function outside of angular. use $scope.$apply to force refresh.
the original fiddle is here and a working example here
(function() {
"use strict";
var app = angular.module("app-newslist", [])
.controller("newsController", newsController)
.service("newsService", newsService);
newsController.$inject = ['$http', 'newsService', '$scope']
newsService.$inject = ['$timeout']
angular.bootstrap(document, [app.name]);
function newsController($http, newsService, $scope) {
var vm = this;
vm.news = $scope.news = [];
vm.service = newsService;
console.warn(newsService)
vm.message = "Angular is Working!";
vm.GetTopNews = function() {
console.log("Inside GetTopNews");
newsService.GetNewsList().
then(function(response) {
$scope.$apply(function() {
$scope.news.length > 0 ? $scope.news.length = 0 : null;
response.data.forEach(function(n) {
$scope.news.push(n)
});
console.log("VM", vm);
})
}, function() {
alert("COULD NOT RETRIEVE NEWS LIST");
});
};
}
function newsService($timeout) {
return {
GetNewsList: function() {
return new Promise(function(res, rej) {
$timeout(function() {
console.log("Waited 2 seconds: Returning");
res({
data: ["This should do the trick!"]
});
}, 2000);
})
}
}
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.9/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.min.js"></script>
<body>
<div class="main">
<div class="body" ng-controller="newsController as vm">
Testing: {{ vm.message }}
<br>{{ vm.news }}
<br>{{ vm }}
<br>
<button class="getTopNewsBtn" ng-click="vm.GetTopNews()">Get News</button>
<br>
<ul class="getTopNews">
<li class="news-item" ng-repeat="news in vm.news track by $index">
{{ news | json }}
</li>
</ul>
</div>
</div>
</body>

Shared data between controllers using factory and promises

Ok so there is a lot out there about sharing data between controllers using factory/service, but I'm not finding what applies to my issue. Either I'm interpreting the answers incorrectly or this is a valid question I'm about to ask! Hopefully the latter.
I would like controller 2 to recognize when a new http call has been made and the factory images object has been updated. at the moment it resolves once and then ignores any subsequent updates. What am i overlooking?
My view:
<div>
<ul class="dynamic-grid" angular-grid="pics" grid-width="150" gutter-size="0" angular-grid-id="gallery" refresh-on-img-load="false" >
<li data-ng-repeat="pic in pics" class="grid" data-ng-clock>
<img src="{{pic.image.low_res.url}}" class="grid-img" data-actual-width = "{{pic.image.low_res.width}}" data-actual-height="{{pic.image.low_res.height}}" />
</li>
</ul>
</div>
factory:
.factory('imageService',['$q','$http',function($q,$http){
var images = {}
var imageServices = {};
imageServices.homeImages = function(){
console.log('fire home images')
images = $http({
method: 'GET',
url: '/api/insta/geo',
params: homeLoc
})
};
imageServices.locImages = function(placename){
console.log('fire locImages')
images = $http({
method: 'GET',
url: '/api/geo/loc',
params: placename
})
};
imageServices.getImages = function(){
console.log('fire get images', images)
return images;
}
return imageServices;
}]);
controller1:
angular.module('trailApp.intro', [])
.controller('introCtrl', function($scope, $location, $state, showTrails, imageService) {
// run the images service so the background can load
imageService.homeImages();
var intro = this;
intro.showlist = false;
intro.data = [];
//to get all the trails based on user's selected city and state (collected in the location object that's passed in)
intro.getList = function(location) {
intro.city = capitalize(location.city);
intro.state = capitalize(location.state);
//get placename for bg
var placename = {placename: intro.city + ',' + intro.state};
imageService.locImages(placename);
... do other stuff...
controller2:
angular.module('trailApp.bkgd', [])
.controller('bkgdCtrl', ['$scope','imageService', 'angularGridInstance', function ($scope,imageService, angularGridInstance) {
$scope.pics = {};
imageService.getImages().then(function(data){
$scope.pics = data;
console.log($scope.pics);
});
}]);
Your controller2 implementation only got the images once, you probably need a $watch to keep to updated:
angular.module('trailApp.bkgd', [])
.controller('bkgdCtrl', ['$scope','imageService', 'angularGridInstance', function ($scope,imageService, angularGridInstance) {
$scope.pics = {};
$scope.$watch(function(){
return imageService.getImages(); // This returns a promise
}, function(images, oldImages){
if(images !== oldImages){ // According to your implementation, your images promise changes reference
images.then(function(data){
$scope.pics = data;
console.log($scope.pics);
});
}
});
}]);

Linking user's contacts in firebase with md-contacts-chips

I am having difficulty getting my head around on how I could link my users contacts in Firebase with md-contacts-chips from https://material.angularjs.org/0.11.2/#/demo/material.components.chips
Basically, each registered user can add people they know via email to their contacts list. The users firebase structure is as follows:
firebase
-$uid1
contacts
$uid2 - userObject
$uid3 - userObject
-$uid2
contacts
$uid1 - userObject
$uid3 - userObject
-$uid3
contacts
$uid1 - userObject
$uid2 - userObject
etc..
Is it possible to ng-repeat a users contacts as an array of objects?
How should I configure the md-contacts-chip?
The example has a function called loadContacts() which has the contacts set.
How would I be able to set my user objects as contacts? The return object is contact and I would like to find a way for it to return the queried object.
function loadContacts() {
var contacts = [
'Marina Augustine',
'Oddr Sarno',
'Nick Giannopoulos',
'Narayana Garner',
'Anita Gros',
'Megan Smith',
'Tsvetko Metzger',
'Hector Simek',
'Some-guy withalongalastaname'
];
return contacts.map(function (c, index) {
var cParts = c.split(' ');
var contact = {
name: c,
email: cParts[0][0].toLowerCase() + '.' + cParts[1].toLowerCase() + '#example.com',
image: 'http://lorempixel.com/50/50/people?' + index
};
contact._lowername = contact.name.toLowerCase();
return contact;
});
}
Thanks
I'm by no means an expert, just a trial and error fanatic. That being said I did get this to work. I the issues is that "md-contact-chip" uses "push and splice" to adjust the array, and as firebase states that's a bad idea. If we had access to replace push with $add() or splice with $remove() this should work properly.
I was looking at the custom chips setup and it seems possible because you can call a function during the chip's add and remove, then with a custom chip template could maybe get the same look as the contact-chips.
Anyway here is what I did to get it working with md-contact-chips. Also I've adjusted this one to work with a list of items, not contacts, cause I wanted the picture for the items.
The key to it should be get your whole person obj, then set the ng-model="ctrl.person.contacts" inside the controller make sure to have an array created if person.contacts does not exist. "ctrl.person.contacts = ctrl.person.contacts || [];
Yes your are not properly updating the firebase object but you when you run
ctrl.person.$save() you are just completely updating the db.
Html
<div layout="column" ng-cloak>
<div>
<p>Items selected</p>
<pre>{{ctrl.item.installedItems}}</pre>
</div>
<input type="button" ng-click="ctrl.updateInstalledItems()" value='update'>
<md-content class="md-padding autocomplete" layout="column">
<md-contact-chips
ng-model="ctrl.item.installedItems"
md-contacts="ctrl.querySearch($query)"
md-contact-name="alseSn"
md-contact-image="image"
md-require-match="true"
md-highlight-flags="i"
filter-selected="ctrl.filterSelected"
placeholder="Select installed items">
</md-contact-chips>
</md-content>
</div>
Controller
app.controller('ItemChipCtrl', ['items', 'item', '$q', '$log',
function (items, item, $q, $log) {
var ctrl = this;
ctrl.items = items;
ctrl.item = item;
ctrl.item.installedItems = ctrl.item.installedItems || [];
ctrl.querySearch = querySearch;
ctrl.allItems = loadItems(ctrl.items);
ctrl.filterSelected = true;
ctrl.updateInstalledItems = function() {
$log.info('Update....')
$log.log(ctrl.installedItems);
ctrl.item.$save();
}
/**
* Search for contacts.
*/
function querySearch (query) {
var results = query ?
ctrl.allItems.filter(createFilterFor(query)) : [];
return results;
}
/**
* Create filter function for a query string
*/
function createFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(item) {
return (item.alseSn.indexOf(lowercaseQuery) != -1);
};
}
function loadItems(items) {
/*var items = $scope.items */
return items.map(function (c, index) {
var item = {
alseSn: c.alseSn || c,
alseCard: c.alseCard,
installedOn: c.installedOn || null,
image: 'img/items/47/'+c.alseCard+'.jpg' || null
};
return item;
});
}
}
]);
route injections
.when('/settings/:alseSn', {
templateUrl: 'settings.html',
controller: 'ItemChipCtrl as ctrl',
resolve: {
auth: function($location, Auth){
return Auth.$requireAuth().then(function(auth) {
return auth;
},function(error){
$location.path('/login');
});
},
item: function($route, Items, Auth){
return Auth.$requireAuth().then(function(){
return Items.getItem($route.current.params.alseSn).$loaded();
});
},
items: function(Items, Auth){
return Auth.$requireAuth().then(function(){
return Items.all.$loaded();
});
}
}
})

Cordova: Data that is filled inside array could not populate on page

I am not able to populate data filled inside the array.
My main controller front is this:
<body ng-controller="MainCtrl"> // MainCtrl controller
<ion-nav-view animation="slide-left-right"></ion-nav-view>
<script src="js/jquery-2.1.1.min.js"></script>
<script src="js/owl.carousel.min.js"></script>
</body>
the MainCtrl initially contain nothing like this:
.controller('MainCtrl', function($scope, $ionicSideMenuDelegate, $ionicPopover, $state, $timeout) {
$scope.users = [];
$scope.devices = [];
})
Initially when i do login the controller named intro is getting called that controller calls rest & validate user & on successfully validating it does following:
.success(function(data, status, headers, config) {
if (data.alert === 'SUCCESS'){
var UserData = data.userdata;
var Username = UserData.personal_information.first_name+" "+UserData.personal_information.last_name;
var Email = UserData.username;
var LastLogin = new Date(UserData.last_visited * 1000);
$scope.users = [{ username: Username, email: Email, location: true, id: null, avatar: 'img/men.jpg', enabled: 'true', lastLogin: LastLogin}];
if(data.ownteam == true) {
$.each(data.ownteamdata, function( index, value ){
var TeamId = value.team_id;
var TeamName = value.team_name;
var Status = value.role;
var surveyArray = {id : TeamId, name :TeamName, icon: 'ion-document-text', status : Status, color : Color};
$scope.devices.push(surveyArray);
});
}
now when i console the array it shows me the data inserted.
then i move to dashboard page where the data of devices array need to be polupated & shown like:
<div ng-repeat="device in devices | filter: { featured: true}">
<div class="padding-horizontal">
<div class="item item-icon-left" on-tap="deviceTap('router.device', device)">
<i class="icon" ng-class="device.icon"></i>
{{ device.name }}
<span class="badge" ng-class="device.color">
{{ device.status }}
</span>
</div>
</div>
</div>
but it is not showing the data to me in the page??? is there something i m missing?
You're trying to filter the devices where featured = true
<div ng-repeat="device in devices | filter: { featured: true}">
but you don't seem to have that element in your object:
var surveyArray = {
id : TeamId,
name :TeamName,
icon: 'ion-document-text',
status : Status,
color : Color
};
Another problems could be the fact that you're maybe - trying to access the array from 2 different controllers.
I would suggest to check your console in chrome (F12) and use some sort of debug.
UPDATE:
One approach if you want to share data between controllers is to use a shared service, like a session, so you can move of the login for the login and validation there (clean controllers).
You can have a look at this plunker.
In app.services.js you will find a service called userAccountService.
(function () {
'use strict';
var services = angular
.module('app.services', [])
.factory('userAccountService', userAccountService);
userAccountService.$inject = ['$q'];
function userAccountService($q) {
var service = {
users: [],
devices: [],
logIn: logIn,
};
return (service);
function logIn(username, password)
{
var deferred = $q.defer();
this.users.push({username: username});
this.devices = [{name: 'android'}, {name: 'iphone'}];
deferred.resolve({users: this.users, devices: this.devices });
return deferred.promise;
}
};
})();
It will be responsible for the log-in an for sharing data between controllers ... and other modules. It is a singleton.
This service contains 2 arrays: users and devices:
var service = {
users: [],
devices: [],
logIn: logIn
};
The login method will, presumably, authenticate the user through an http call and will populate the shared members: users and devices.
(I've used promises there to match your $http call).
Now you can have a look at the app.controller.js file where my controllers are defined.
They depend on userAccountService.
view1Controller is the starting point and it is in charge for the login:
$scope.login = function()
{
userAccountService.logIn('username', 'password')
.then(function(result){
$scope.users = result.users,
$scope.devices = result.devices
})
.catch(function(reason){
console.log(reason);
});
$state.go('view2');
}
if the login is successful it will populate the two local members to be displayed in the page.
<div ng-repeat="device in devices">
<div class="padding-horizontal">
<div class="item item-icon-left">
{{ device.name }}
</span>
</div>
</div>
</div>
Same thing happens with view2. Now we don't need to login again; we can simply read the shared member of our service:
.controller('view2Controller', function($scope, userAccountService) {
$scope.users = [];
$scope.devices = [];
function init()
{
$scope.users = userAccountService.users;
$scope.devices = userAccountService.devices;
}
init();
})

Map AngularJS restAPI request via URL

Being new to angular I'm stocked to figure out how to call a web service which should be parsed and maped via URL, like if the URL is getting called directly to get listed request with the given params
what I mean let say I have
/api/products -- calling all products(this is the access point)
/api/products/?page=2&orderby=asc -- calls products with pagination and orderby and here is what's bothering me because the api is getting called via ajax and there is no URL mapping of the target
My Codes
Html markup
<div ng-controller="MainCtrl">
<div class="row">
<div class="col-lg-7 col-md-7 col-sm-7">
<pagination total-items="totalItems" num-pages="totalPages" ng-model="currentPage" ng-change="selectPage(currentPage)" max-size="5" class="pagination-sm" boundary-links="true"></pagination>
</div>
<div ng-repeat="product in products"></div>
</div>
</div>
contoller
//called when navigate to another page in the pagination
$scope.selectPage = function(page) {
$scope.filterCriteria.pageNumber = page;
$scope.fetchResult();
};
//The function that is responsible of fetching the result from the server and setting the grid to the new result
$scope.fetchResult = function() {
return api.items.search($scope.filterCriteria).then(function(data) {
$scope.products = data;
$scope.totalPages = data.total;
$scope.productsCount = data.TotalItems;
}, function() {
$scope.products = [];
$scope.totalPages = 0;
$scope.productsCount = 0;
});
};
Service
.factory('api', function(Restangular) {
//api call to
return {
products: function() {
return Restangular.all('products').getList();
},
product: function(id) {
Restangular.one("products", id ).get().then(function(c) {
return c;
});
},
update: function(id) {
Restangular.one("products", id).put().then(function(c) {
return c;
});
},
items: {
search: function(query) {
return Restangular.all('products').getList(query);
}
},
};
});
How do I create params URL and function of this make restAPI calls or what are the workarounds in this case
You can get at the search parameters with the $location service. So if a user goes to somedomain.com/products/?page=2&orderby=asc then $location.search() will equal {page: 2, orderby: 'asc'}.
So when your controller loads you just need to set the filterCriteria and fetch the results.
controller
var myCtrl = function($location, $scope) {
$scope.selectPage = function(page) {
...
};
$scope.fetchResult = function() {
...
};
$scope.filterCriteria = $location.search();
$scope.fetchResults();
}
First you need to create a restangular object by mentioning the base url. In your case it will be
// It will creat a url /api
var base = Restangular.all('api');
Now you can create multiple scenarios like if you want to get all products then it will be:
// /api/products/
base.getList('products')
.then(function(products) {
$scope.products= products
})
Now if you want to apply pagination as well as include orderBy param
$scope.products = base.getList("products", [{page: 2},{orderby:asc}]);

Resources