How to scale controllers with angular - javascript

I have some angular app, that is really easy. I've put everything into one controller, but i want to split it into multiple controllers so every controller should do action that belongs to it, not have a lot of different function of different meaning in one controller.
Here is a code:
var videoApp = angular.module('videoApp', ['videoAppFilters', 'ui.unique', 'angularUtils.directives.dirPagination']);
videoApp.controller('VideoListCtrl', function ($scope, $http, $filter) {
$scope.getFilteredResults = function (category, data, callback) {
callback = callback ||$filter('articleFilter');
$scope.videos = callback(category, data);
return $scope.videos;
};
$scope.setPageSize = function (pageSize) {
$scope.pageSize = pageSize;
return $scope.pageSize;
};
$scope.addFavorite = function (data, key) {
localStorage.setItem(key, data);
$scope.getFilteredResults(data, $scope.allData);
return alert(key + " "+ data + " was added to your favorite list.");
};
$scope.addSelectedClass = function (event) {
if($(event.target).hasClass("selected") == true)
{
$(event.target).removeClass("selected");
} else {
$(".selected").removeClass("selected");
$(event.target).addClass("selected");
}
};
$scope.formatDate = function (dateString) {
var date = new Date(parseInt(dateString));
return date.toDateString();
};
$scope.cacheLoad = function (url, allowCache) {
if(allowCache == false || localStorage.getItem(url) && (parseInt(localStorage.getItem(url + 'time')) + 20000) < (new Date().getTime()) || (!localStorage.getItem(url) )) {
$http.get(url).success(function (data) {
$scope.allData = data;
$scope.videos = data;
if(localStorage.getItem('category')) {
$scope.videos = $scope.getFilteredResults(localStorage.getItem('category'), $scope.allData);
} else {
$scope.videos = data;
}
$scope.categories = $filter('categoryFilter')(data);
if(allowCache == true && parseInt(localStorage.getItem(url + 'time')) + 20000 < (new Date().getTime() )) {
localStorage.setItem(url, JSON.stringify(data));
localStorage.setItem(url + 'time', new Date().getTime());
}
});
} else {
$scope.allData = JSON.parse(localStorage.getItem(url));
$scope.videos = JSON.parse(localStorage.getItem(url));
$scope.categories = $filter('categoryFilter')(JSON.parse(localStorage.getItem(url)));
}
};
$scope.pageSize = 12;
$scope.cacheLoad('http://academy.tutoky.com/api/json.php', true);
});
So, how to split this into multiple controllers and how to pass data between them?

You could split things out into Services, for example the following item could be a service in your code, that you then dependency inject into your controller:
Your Cache logic, This is normally something you would want to reuse so it makes sense to be a service.
You might also want to make the following item a filter or directive:
$scope.formatDate - Rather than calling this function everytime you want to format a date, it would be much easier in your html to call {{ date | formatDate }} or <div formatDate>{{ date }}</div>
You could probably strip out the pageSize too but it depends how granular you want to go.

Related

How to use rootScope in angularjs

I have a data in my commonservice and I want to use the data in my template and I will assign a rootscope and use it but it isn't working right now and I am not sure what's wrong can anyone please suggest help.
My js:
function addGoogleAddress(id, data, scope) {
var places = new google.maps.places.Autocomplete(document.getElementById(id));
google.maps.event.addListener(places, 'place_changed', function () {
var place = places.getPlace();
if (place && place.address_components) {
for (var i = 0; i < (place.address_components.length); i++) {
if (place.address_components[i].types[0] === 'locality') {
data.city.key = '1001';
data.city.name = place.address_components[i].long_name.toString();
} else if (place.address_components[i].types[0] === 'administrative_area_level_1') {
data.state.key = '1001';
data.state.name = place.address_components[i].long_name.toString();
} else if (place.address_components[i].types[0] === 'country') {
data.country.key = '1001';
data.country.name = place.address_components[i].long_name.toString();
}
}
}
});
$timeout(function () {
$('#' + id).removeAttr('placeholder');
}, 500);
$rootScope.data = data;
}
My controller:
console.log($rootScope.data)
Here i am getting undefined.
You can not broadcast data directly, you need to broadcast event and pass parameter like follows,
Note: I am assuming here you have injected dependency
$rootScope.$broadcast('eventName',data);
Then in controller like follows -
$scope.$on('eventName',function(event,data){
console.log('data---',data);
})
app.controller('ctrl1',['$scope','$rootScope',function($scope,$rootScope) {
$scope.xyz = $rootScope.xyz;
//console.log($scope.xyz); hey
}]);
app.run(function($rootScope){
$rootScope.xyz ="hey";
})

How to post inside a loop with AngularJS 1.4.8

I have found other answers to similar questions of mine, but none of them worked for me.
I'm trying to create a cards game. Here I'm trying to shuffle through the cards and give each player 5 random Money cards ( $scope.MoneyCards ). Instead, this code gives all the 15 cards to the last player on the loop.
The results with ( Angular 1.0.8 ) is exactly how I want it. But when I'm using any later version this doesn't work. I'm working on migrating to 1.4.8 .
This is the code I'm using:
$scope.StartGame = function() {
$http.get('/api/MoneyCards')
.success(function(data) {
$scope.MoneyCards = data;
console.log(data);
// Loop through players
player = 1;
while(player < 4) {
// Loop through money cards to give each player 5 cards
i=0;
while(i < 5) {
// Shuffle
var Mfloor = Math.floor(Math.random()*$scope.MoneyCards.length);
$scope.randomCard = $scope.MoneyCards.splice(Mfloor, 1 );
// Create the card in the Deck table and attach it to a player
$scope.createDecks($scope.randomCard[0],player,0);
i++;
}
player++;
}
})
.error(function(data) {
console.log('Error: ' + data);
});
};
This is the createDecks function
$scope.createDecks = function(card, player, type) {
$scope.formData.player = player;
$scope.formData.type = type;
$scope.formData.card = card;
$http.post('/api/Decks', $scope.formData)
.success(function(data) {
$scope.formData = {};
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
I'll try to adopt this to JSFiddle.
Code provided by georgeawg using $q and still not working
$scope.StartGame = function() {
$http.get('/api/MoneyCards')
.success(function(data) {
$scope.MoneyCards = data;
console.log(data);
var player = 0;
while(player < 3){
var i = 0;
var qPromise = $q.when(0);
while(i < 5) {
// Shuffle
var Mfloor = Math.floor(Math.random()*$scope.MoneyCards.length);
var randomCard = $scope.MoneyCards.splice(Mfloor, 1 );
// Create the card in the Deck table and attach it to a player
qPromise = qPromise.then( function (response) {
return $q.all([
response,
$scope.createDecksPromise(randomCard[0],player,0)
]);
});
i++;
};
player += 1;
};
qPromise.then ( function(response) {
//do whatever
console.log(response);
}) .catch ( function (error) {
throw error;
});
})
.error(function(data) {
console.log('Error: ' + data);
});
};
First yourcreateDecks function should return promises.
$scope.createDecksPromise = function(card, player, type) {
var formData = {};
formData.player = player;
formData.type = type;
formData.card = card;
return $http.post('/api/Decks', formData);
};
Your iterative loop should then chain those promises.
var i = 0;
var qPromise = $q.when([]);
while(i < 5) {
// Shuffle
var Mfloor = Math.floor(Math.random()*$scope.MoneyCards.length);
var randomCard = $scope.MoneyCards.splice(Mfloor, 1 );
// Create the card in the Deck table and attach it to a player
// Add closure for randomCard and player
(function (randomCard, player) {
qPromise = qPromise.then( function (response) {
response.push(
$scope.createDecksPromise(randomCard[0],player,0)
);
return $q.all(response);
});
})(randomCard, player);
i++;
};
qPromise.then ( function(response) {
//do whatever
}) .catch ( function (error) {
throw error;
});
Remember that when chaining promises you should always return something to the .then method.
Also be aware that the .then method has a different response structure than the .success method.
The AngularJS team has come to their senses and deprecated .success. We should all be using the .then method now. To learn more about the deprecation (or should I say failure) of .success and .error, see the latest AngularJS $http API Docs.
To learn more about $q.when, look at the AngularJS $q Service API Docs.
I have finally found the answer, here is the code:
$scope.StartGame = function() {
$http.get('/api/MoneyCards')
.success(function(data) {
$scope.MoneyCards = data;
console.log(data);
var qPromise = [];
var player = 1;
while(player < 4){
var i = 0;
while(i < 5) {
qPromise.push([
Mfloor = Math.floor(Math.random()*$scope.MoneyCards.length),
randomCard = $scope.MoneyCards.splice(Mfloor, 1 ),
$scope.createDecksPromise(randomCard[0],player,0)
]);
i++;
};
player += 1;
};
return $q.all(qPromise);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
Thanks to georgeawg, the $http.post $scope.createDeckPromise() should return promises:
$scope.createDecksPromise = function(card, player, type) {
var formData = {};
formData.player = player;
formData.type = type;
formData.card = card;
return $http.post('/api/Decks', formData);
};
For whoever is having the same issue. This is the solution without my code:
$scope.RunCode(){
var Promises = [];
var i = 0;
while (i < 5){
Promises.push($http.post('/api/whatever', formData));
// to add multiple functions to the promise use [], example: Promises.push([doThing1, doThing2]);
i++;
};
return $q.all(Promises);
}

Angular Nested Promise

I’m really struggling to write a complex function in Angular that depends on promises. This is my first time writing a promise and I'm still not sure I fully understand how to do what I want to do with my code.
I have a variable var query = searchQuery.getQuery() in a controller ProfileNavCtrl. Then in my searchQuery service, getQuery fetches the value of localStorage.getItem('searchQuery') and checks if it’s an empty string or null. If it’s not empty or null, it simply returns the value to the controller. The value should be an array of slugs like ['foo','foo-bar','foo-bar-baz'].
If it is null or empty, it executes an $http.get call to fetch a JSON object and parse it. This is where things break down for me. I need getQuery() to return the value from $http.get (if the initial value of query is null) so that the controller variable query is assigned that value. As it is now, query (in the controller) is always set to null or undefined.
The $http.get call also calls setQuery() so that the query is persisted and future calls are avoided.
Here is my controller:
app.controller('ProfileNavCtrl', ['$scope', '$http', '$location', '$q', 'searchQuery',
function($scope, $http, $location, $q, searchQuery){
var query = searchQuery.getQuery;
// do something with query
And here is my service:
app.service('searchQuery', ['$http', '$timeout', '$q', function($http, $timeout, $q){
var query = [];
this.getQuery = new Promise(function(){
var query = localStorage.getItem('searchQuery');
if(query == "" || query == [""] || query == null){
var slugArray = [];
var query = $http.get('/companies.json')
.then(function(resp) {
if(resp && resp.data) {
for(var i in resp.data) {
var result = resp.data[i];
if(resp.data[i].name){
slugArray.push(resp.data[i].name.toLowerCase().split(' ').join('-'));
}
}
setQuery(slugArray);
} else {
resetQuery();
}
}, function(err) {
resetQuery();
}).then(function(resp){
return resp;
})
return query;
} else {
return query;
};
}).then(function(success){
return success;
});
UPDATE: 2nd Attempt
Here is my controller code:
var getQuery = searchQuery.getQuery();
getQuery.then(function(query){
query = searchQuery.getQuery();
// Check if user is on main site or portal
if(location.pathname.split('/')[3] == null){
var currentProfile = location.pathname.split('/')[1];
} else {
var currentProfile = location.pathname.split('/')[3];
};
// Get the next/prev query element (if any)
console.log('6: ');
console.log(query);
var prev = query.slice(query.indexOf(currentProfile)-1)[0];
var next = query.slice(query.indexOf(currentProfile)+1)[0];
// Check if next/prev is undefined and if so, set to first/last element in query array
if(prev){
var prevProfile = prev;
} else {
var prevProfile = query.pop();
};
if(next){
var nextProfile = next;
} else {
var nextProfile = query[0];
};
$scope.goToPrev = function() {
// Check if user is on main site or portal
if(location.pathname.split('/')[3] == null){
var profileUrl = location.origin + '/' + prevProfile;
// window.location = profileUrl;
console.log(profileUrl);
} else {
var profileUrl = location.origin + '/' + location.pathname.split('/').slice(1,3).join('/') + '/' + prevProfile;
// window.location = profileUrl;
console.log(profileUrl);
}
};
$scope.goToNext = function() {
// Check if user is on main site or portal
if(location.pathname.split('/')[3] == null){
var profileUrl = location.origin + '/' + nextProfile;
// window.location = profileUrl;
console.log(profileUrl);
} else {
var profileUrl = location.origin + '/' + location.pathname.split('/').slice(1,3).join('/') + '/' + nextProfile;
// window.location = profileUrl;
console.log(profileUrl);
}
};
});
Here is my updated service:
this.getQuery = function(){
return new Promise(function(){
var query = localStorage.getItem('searchQuery');
if(query == "" || query == [""] || query == null){
var slugArray = [];
return $http.get('/companies.json')
.then(function(resp) {
if(resp && resp.data) {
for(var i in resp.data) {
var result = resp.data[i];
if(resp.data[i].name){
slugArray.push(resp.data[i].name.toLowerCase().split(' ').join('-'));
}
}
setQuery(slugArray);
} else {
resetQuery();
}
return slugArray;
}, function(err) {
resetQuery();
});
} else {
return query;
};
});
};
In Angular promises are provided through the $q service. See the documentation for more detail.
The basic outline to implement $q promise in your service is outlined below, I'll leave the detail on how to save to local storage etc to you:
this.getQuery = function(){
var deferred = $q.defer();
var query = localStorage.getItem('searchQuery');
if(query == "" || query == [""] || query == null){
$http.get('yoururl').then(function(resp) {
// assuming resp is an array, else do your parsing to get array
query = resp;
deferred.resolve(query);
}, function(err) {
query = null;
deferred.reject(err);
});
} else {
deferred.resolve(query);
};
return deferred.promise;
};
You can then use this in your controller like:
var query = null;
searchQuery.getQuery().then(function(result) {
query = result;
}, function(err) {
// Error occured
});

Moving code into a factory in an Angular app

I am trying to clean up a controller that has too many lines of code in it. In the controller below where you find a function called getProductDetails, I would like to move the filter to a factory or a service, but I am not sure how to do it.
'use strict';
(function () {
var userQuoteBuild = angular.module('priceApp');
userQuoteBuild.controller('quoteBuilderController', function ($scope, $http) {
// loads of controller logic here...
$scope.getProductDetails = function (item) {
$scope.listOfProductVariants = item.default_variant_attributes;
// TODO: put this in its own factory?
$scope.selectedProductAttributes = $scope.listOfAttributes.filter(function (item) {
var validated = false, i, length = $scope.listOfProductVariants.length;
for (i = 0; i < length; i++) {
if (item.name === $scope.listOfProductVariants[i]){
validated = true;
}
}
return validated;
});
};
});
(function () {
'use strict';
angular
.module('priceApp')
.factory('filterService', filterService);
function filterService() {
var service = {
getValidated: getValidated
}
return service;
function getValidated(list, variants) {
return list.filter(function (item) {
var validated = false, i, length = variants.length;
for (i = 0; i < length; i++) {
if (item.name === variants[i]) {
validated = true;
}
}
return validated;
});
}
}
})();
Simply inject this filterService to your controller and then use it as in example here:
$scope.selectedProductAttributes = filterService
.getValidated($scope.listOfAttributes,
$scope.listOfProductVariants)
I followed John Papa's AngularJS Style Guide. Make sure to choose a better name than filterService. : )
Check this:
userQuoteBuild.factory('myService', function() {
var service = {
getProductDetails: function(item) {
// your logic
return value;
}
}
return service;
});

how to get data from json file using angularjs

I am getting data from json file to display in a table, but first time only i got data from getdata() next time i got following error:
Uncaught TypeError: Cannot read property 'length' of undefined Controller.js:95
$http return value is fine, i got all data from json file first time, next time i could not get data from getdata().
getData() function is not working properly, it is working only first time, next time i could not get data.
how to solve this issue.
controller.js
var app = angular.module('main', ['ngTable', 'claimApp']);
app.controller('DemoCtrl', function ($scope, $filter, ngTableParams, appFactory,$http) {
$scope.datasets = ["1", "2", "3"];
$scope.dataset = "1";
var data1 = [];
var data2 = [];
var data3 = [];
$scope.totalCnt = function () {
return window.document.getElementById("tablesort").getElementsByTagName("TR").length - 1;
};
var getData = function () {
if ($scope.dataset == "1") {
$http.get('json/json0.json').success(function(data) {
data1 = data;
});
return data1;
} else if ($scope.dataset == "2") {
$http.get('json/json1.json').success(function(data) {
data2= data;
});
return data2;
} else if ($scope.dataset == "3") {
$http.get('json/json2.json').success(function(data) {
data3= data;
});
return data3;
}
};
$scope.$watch("dataset", function () {
$("#tablesort").fadeOut('slow', function () {
$scope.tableParams.reload();
$scope.tableParams.page(1);
$("#tablesort").fadeIn('slow');
});
});
$scope.tableParams = new ngTableParams({
page: 1, // show first page
count: 10, // count per page
sorting: {
name: 'asc' // initial sorting
}
}, {
total: function () {
return getData().length;
console.info("==="+getData().length);
}, // length of data
getData: function ($defer, params) {
var filteredData = getData();
console.info("filteredData"+filteredData);<!--i could not get this data second time only it is working first time-->
var orderedData = params.sorting() ?
$filter('orderBy')(filteredData, params.orderBy()) :
filteredData;
var lastPage = (1 + Math.floor((orderedData.length - 1) / params.count()));
$("#lastpage").html(lastPage);
$defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
},
$scope: {
$data: {}
}
});
});
according to the doc
Request transformations:
If the data property of the request configuration object contains an object, serialize it into JSON format.
Response transformations:
If XSRF prefix is detected, strip it (see Security Considerations section below).
If JSON response is detected, deserialize it using a JSON parser.
so use in your response
$http.get('json/json0.json').success(function(data) {
data1 = JSON.parse(data);
});
EDIT
var getData = function () {
if ($scope.dataset == "1") {
$http.get('json/json0.json').success(function(data) {
$scope.response = data;
});
} else if ($scope.dataset == "2") {
$http.get('json/json1.json').success(function(data) {
$scope.response = data;
});
} else if ($scope.dataset == "3") {
$http.get('json/json2.json').success(function(data) {
$scope.response = data;
});
}
return $scope.response;
};

Categories