Variables between factories in angular.js - angularjs

I'm currently learning angular and have hit a roadblock.
The second factory (shown below) makes an http request like this: http://example.com/api/get_post/?post_id=7129&callback=JSON_CALLBACK');
I want the post ID to be a variable. So depending on which blog title is clicked, I can pass the correct variable into that http request.
In other words, I guess I want to take a result from the first factory (blogAPIservice) and use it in the second factory.
Makes sense??
<!-- FACTORIES -->
angular.module('blogApp.services',[])
.factory('blogAPIservice',function($http) {
var blogAPI = [];
var blogs = $http.jsonp('http://example.com/api/get_recent_posts/?count=10&callback=JSON_CALLBACK');
blogs.success(function(data) {
$.each(data.posts, function(i, blog) {
var fromNow = moment(blog.date).fromNow();
blogAPI.push({
url: blog.url,
title: blog.title,
excerpt: blog.excerpt,
date : fromNow,
id: blog.id
})
});
});
var factory = {};
factory.getBlogs = function () {
return blogAPI;
};
return factory;
})
.factory('singlePostService',function($http) {
var singleAPI = [];
var postID = '7129';
var singlePost = $http.jsonp('http://example.com/api/get_post/?post_id=7129&callback=JSON_CALLBACK');
singlePost.success(function(data) {
singleAPI.push({
title: data.post.title,
content: data.post.content
})
});
var factory = {};
factory.getSinglePost = function () {
return singleAPI;
};
return factory;
})
And here are the controllers:
angular.module('blogApp.controllers', [])
.controller('resultsController',function($scope, blogAPIservice) {
$scope.keywordFilter = null;
$scope.blogs = [];
init();
function init() {
$scope.blogs = blogAPIservice.getBlogs();
}
function grabID() {
$(this).attr('rel');
}
})
.controller('singlePostController',function($scope, singlePostService) {
$scope.keywordFilter = null;
$scope.singlePost = [];
init();
function init() {
$scope.singlePost = singlePostService.getSinglePost();
}
})
And finally the markup:
<li ng-repeat="blog in blogs">
{{ blog.title }}
</li>

You can inject the first service into the second one like this:
.factory('singlePostService',function($http, blogAPIservice) {
//Do something with blogAPIservice
}
For more information about depenency injection read the docs

Related

How to mock $window.Notification

I am still learning the ropes when it comes to unit testing with angular. I have an angular service that I use to create HTML5 notifications. Code is similar to the following:
(function() {
'use strict';
angular
.module('blah')
.factory('OffPageNotification', offPageNotificationFactory);
function offPageNotificationFactory($window) {
//Request permission for HTML5 notifications as soon as we can
if($window.Notification && $window.Notification.permission !== 'denied') {
$window.Notification.requestPermission(function (status) { });
}
function OffPageNotification () {
var self = Object.create(OffPageNotification.prototype);
self.visibleNotification = null;
return self;
}
OffPageNotification.prototype.startNotification = function (options) {
var self = this;
self.options = options;
if(self.options.showHtml5Notification && (!self.options.onlyShowIfPageIsHidden || $window.document.hidden)) {
if($window.Notification && $window.Notification.permission !== 'denied') {
self.visibleNotification = new $window.Notification('Notification', {
body: self.options.notificationText,
icon: self.options.notificationIcon
});
}
}
};
.
.
.
return new OffPageNotification();
}
})();
I am attempting to write unit tests for this but am unsure how to mock $window.Notification so it can be used as both a constructor...
self.visibleNotification = new $window.Notification(....)
and also contain properties
if($window.Notification && $window.Notification.permission !== 'denied')
and methods....
$window.Notification.requestPermission(
An example of something I have tried is:
describe('startNotification', function() {
beforeEach(function() {
var mockNotification = function (title, options) {
this.title = title;
this.options = options;
this.requestPermission = sinon.stub();
};
mockNotification.prototype.permission = 'granted';
mockWindow = {
Notification: new mockNotification('blah', {}),
document: {hidden: true}
};
inject(function (_OffPageNotification_) {
OffPageNotification = _OffPageNotification_;
});
});
it('should display a html5 notification if the relevant value is true in the options, and permission has been granted', function(){
var options = {
showHtml5Notification: true,
onlyShowIfPageIsHidden: true
};
OffPageNotification.startNotification(options);
});
});
I get an error saying '$window.Notification is not a constructor' with this setup and I understand why (I am passing in an instantiated version of the mockNotification). But if I set mockWindow.Notification = mockNotification then I get an error when it calls requestPermission since this is undefined.
Any help is appreciated
Notification should be a constructor. And it should have static properties and methods.
All of the relevant properties of mockNotification are instance properties, while they should be static:
function MockNotification() {}
MockNotification.title = title;
MockNotification.options = options;
MockNotification.requestPermission = sinon.stub();
mockWindow = {
Notification: MockNotification,
document: {hidden: true}
};

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

Delay loading data in Angular JS

I have code like this
(function (app) {
app.controller('productListController', productListController)
productListController.$inject = ['$scope', 'apiService', 'notificationService', '$ngBootbox', '$filter'];
function productListController($scope, apiService, notificationService, $ngBootbox, $filter) {
$scope.products = [];
$scope.page = 0;
$scope.pagesCount = 0;
$scope.getProducts = getProducts;
$scope.keyword = '';
$scope.search = search;
$scope.deleteProduct = deleteProduct;
$scope.selectAll = selectAll;
$scope.deleteMultiple = deleteMultiple;
function deleteMultiple() {
var listId = [];
$.each($scope.selected, function (i, item) {
listId.push(item.ID);
});
var config = {
params: {
checkedProducts: JSON.stringify(listId)
}
}
apiService.del('/api/product/deletemulti', config, function (result) {
notificationService.displaySuccess('Deleted successfully ' + result.data + 'record(s).');
search();
}, function (error) {
notificationService.displayError('Can not delete product.');
});
}
$scope.isAll = false;
function selectAll() {
if ($scope.isAll === false) {
angular.forEach($scope.products, function (item) {
item.checked = true;
});
$scope.isAll = true;
} else {
angular.forEach($scope.products, function (item) {
item.checked = false;
});
$scope.isAll = false;
}
}
$scope.$watch("products", function (n, o) {
var checked = $filter("filter")(n, { checked: true });
if (checked.length) {
$scope.selected = checked;
$('#btnDelete').removeAttr('disabled');
} else {
$('#btnDelete').attr('disabled', 'disabled');
}
}, true);
function deleteProduct(id) {
$ngBootbox.confirm('Are you sure to detele?').then(function () {
var config = {
params: {
id: id
}
}
apiService.del('/api/product/delete', config, function () {
notificationService.displaySuccess('The product hase been deleted successfully!');
search();
}, function () {
notificationService.displayError('Can not delete product');
})
});
}
function search() {
getProducts();
}
function getProducts(page) {
page = page || 0;
var config = {
params: {
keyword: $scope.keyword,
page: page,
pageSize: 20
}
}
apiService.get('/api/product/getall', config, function (result) {
if (result.data.TotalCount == 0) {
notificationService.displayWarning('Can not find any record.');
}
$scope.products = result.data.Items;
$scope.page = result.data.Page;
$scope.pagesCount = result.data.TotalPages;
$scope.totalCount = result.data.TotalCount;
}, function () {
console.log('Load product failed.');
});
}
$scope.getProducts();
}
})(angular.module('THTCMS.products'));
So my problem is when i loading data the application take me some time to load data.
I need load data as soon as
Is the any solution for this?
Since you are loading data via api call, there will be a delay. To handle this delay, you should display a loading screen. Once the data is loaded, the loading screen gets hidden and your main screen is visible. You can achieve this using $http interceptors.
See : Showing Spinner GIF during $http request in angular
The api-call is almost certainly causing the delay. Data may be received slowly via the api-call so you could display any sort of loading text/image to notify the use that the data is being loaded.
If u want the data ready at the time when controller inits, u can add a resolve param and pass the api call as a $promise in the route configuration for this route.

View doesn't get updated from Service

I am trying to display an object (songTitle) from my service. The initial state (tmp) is displayed. If I am changing the object in the service, the view doesnt get updated.
Js:
var party = angular.module("party", []);
party.run(function () {
var tag = document.createElement('script');
tag.src = "http://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
});
party.service('PlayerService', function ($window) {
this.playlist = [
"https://www.youtube.com/watch?v=fnW2uLwHAas",
"https://www.youtube.com/watch?v=iPT8DA32U6U",
"https://www.youtube.com/watch?v=eGjEnfQl37s",
"https://www.youtube.com/watch?v=nFtTY2S20mI",
"https://www.youtube.com/watch?v=UmXQiPLoLTk",
"https://www.youtube.com/watch?v=PbVx85DS9zc",
"https://www.youtube.com/watch?v=ciidn3nEoiE",
"https://www.youtube.com/watch?v=sm0DgkBEnUI",
"https://www.youtube.com/watch?v=J2OCSWF7sAw",
"https://www.youtube.com/watch?v=y_-giRHtuv8",
"https://www.youtube.com/watch?v=iPT8DA32U6U",
"https://www.youtube.com/watch?v=eGjEnfQl37s",
"https://www.youtube.com/watch?v=nFtTY2S20mI",
"https://www.youtube.com/watch?v=UmXQiPLoLTk",
"https://www.youtube.com/watch?v=PbVx85DS9zc"
];
this.player = {};
this.pbTimer = null;
this.songTitle = "tmp";
$window.onYouTubeIframeAPIReady = function () {
this.player = new YT.Player('ytplayer', {
height: '100',
width: '100',
videoId: 'ciidn3nEoiE',
events: {
'onReady': onPlayerReady
}
});
}
function onPlayerReady() {
console.log("db ready");
songTitle = player.getVideoData().title;
console.log(songTitle);
}
this.playVideo = function (url) {
console.log("db playVideo " + url);
player.loadVideoById(url.split("watch\?v=")[1], 0, "large");
console.log(player);
}
});
party.controller("FrontController", function ($scope) {
$scope.front = {};
$scope.front.title = "PARTY";
});
party.controller("PartyController", ['$scope', 'PlayerService', function ($scope, PlayerService) {
$scope.party = {};
$scope.party.title = "PARTY";
Sortable.create(playlist, { /* options */ });
$scope.playlist = PlayerService.playlist;
$scope.playVideo = function (url) {
PlayerService.playVideo(url);
}
$scope.songTitle = PlayerService.songTitle;
}]);
HTML
<body ng-app="party">
<div ng-controller="PartyController" class="container-fluid">
...
<p id="playertitle">{{songTitle}}</p>
...
Log:
db ready
Blackmill Feat. Veela - Life (Full Version)
The problem is in your onPlayerReady function. The line songTitle = player.getVideoData().title; doesn't set songTitle on your service, but rather on the global scope, which is the window object. Simply using this.songTitle won't help either, because this doesn't refer to your service too in the scope of onPlayerReady.
The easiest solution would be to save a reference to your service outside of onPlayerReady and then use it to assign songTitle:
var self = this;
function onPlayerReady() {
console.log("db ready");
self.songTitle = player.getVideoData().title;
console.log(self.songTitle);
}
Still, this is not enough. Because you change songTitle from outside the Angular world (the Youtube player callbacks), you need to call $scope.$apply to notify Angular something has changed.
For that, you need to inject $rootScope into your service:
party.service('PlayerService', function ($window, $rootScope)
and change songTitle using $rootScope.$apply:
var self = this;
function onPlayerReady() {
console.log("db ready");
$rootScope.$apply(function() {
self.songTitle = player.getVideoData().title;
console.log(self.songTitle);
});
}

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

Resources