How to reuse controller without adding a similar one? Angular1.5 - javascript

I've done 2 tables(customers , items ) CRUD pages in my web app.
CRUD customer table(customer.html)
CRUD Item table(item.html)
I'm trying to make a new page , which is similar to CRUD customer.html page but can CRUD one specific customer id's items.
the way i'm going to make it is copying two similar pages I've done.
customer.html -> crudOneCustomersItems.html
item.html -> item2.html
Then I created another 1 controller:itemCtrl2 in app.js , copy similar code but changed data source(items belong to one customer's id).
crudOneCustomersItems.html
item2.html
But I think maybe I can reuse same controllers , how can I make it in Angular1.5??
my demo plunker: https://plnkr.co/edit/4hfNde9tLIIDzZRJAlhe
app.controller('CustomerCtrl', function($scope) {
// making data start
$scope.customer = {
id:1,
name:'jason',
email:'pp#pp.cc'
};
$scope.customers = [];
for(var i = 1 ; i < 20 ; i++){
var temp = angular.copy($scope.customer);
temp.id = i;
temp.name = temp.name+i;
temp.email = temp.name+i;
$scope.customers.push(temp);
}
//making data end
});
app.controller('ItemCtrl', function($scope) {
// making data start
$scope.item = {
id:1,
customerId: 2,
name:'bomb',
description:'it can explode'
};
$scope.items = [];
for(var i = 1 ; i < 20 ; i++){
var temp = angular.copy($scope.item);
temp.id = i;
if(i<10){
temp.customerId =2;
}
else{
temp.customerId =3;
}
temp.name = temp.name+i;
temp.description = temp.name+i;
$scope.items.push(temp);
}
//making data end
});
app.controller('ItemCtrl2', function($scope) {
// getting selected data (customer id from querying parameter )
});
Thanks

Related

Multiple api calls using AngularJS

I am fairly new to AngularJS and I am practising below exercise with requirement
1.Using the API to get 20 posts and display them on the page along with the user’s
name who created the post and display them on the page.
For this exercise I am using https://jsonplaceholder.typicode.com/ as the data source.
I need to do 2 api calls in same controller
To get list of 20 posts which has userid in it(https://jsonplaceholder.typicode.com/posts)
Based on the above user Id I need to get username (https://jsonplaceholder.typicode.com/users/userId)
Please see my work done in plnkr, I am able to display Post but not username.
Script.js
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
$http.get("https://jsonplaceholder.typicode.com/posts").then(function(response) {
$scope.data = response.data;
var postList = [];
for (var i = 0; i < 20; i++) {
var display = {
UserName: $http.get("https://jsonplaceholder.typicode.com/users/" + $scope.data[i].userId).then(function(response) {
$scope.user = response.data;
}),
Post: $scope.data[i].title
}
postList.push(display);
}
$scope.list = postList;
});
});
Index.html
<div ng-repeat="x in list">
Post:{{ x.Post }}
UserName:{{x.UserName}}
</div>
I believe this area is wrong:
.then(function(response) {
$scope.data = response.data;
var postList = [];
for (var i = 0; i < 20; i++) {
var display = {
UserName: $http.get("https://jsonplaceholder.typicode.com/users/"+$scope.data[i].userId).then(function(response){
$scope.user = response.data;
}),
Post: $scope.data[i].title
}
postList.push(display);
}
$scope.list = postList;
});
where you stored a Promise object in your UserName property and produced unexpected result.
to correct this assign the postList after the request has finished:
.then(function(response) {
$scope.data = response.data;
var postList = [];
for (var i = 0; i < 20; i++) {
$http.get("https://jsonplaceholder.typicode.com/users/"+$scope.data[i].userId).then(function(response){
$scope.user = response.data;
var display = {
UserName: "",
Post: $scope.data[i].title
};
$scope.list.push(display);
});
}
$scope.list = postList;
});
Once you implemented this you will encounter a new problem:
since you called $http.get() in a loop and actually used the variable i inside .then() by the time .then() executes the value of i is already in its final form which is i = 20 | data.length which every .then() calls will receive.
in order to overcome this problem the best way I know is to format the entire data first before displaying it:
$http.get("https://jsonplaceholder.typicode.com/posts")
.then(function(response)
{
var data = response.data;
var postList = [];
// this will check if formatting is done.
var cleared = 0;
// create a function that checks if data mapping is done.
var allClear = function () {
if (postList.length == cleared)
{
// display the formatted data
$scope.list = postList;
}
};
for (var i = 0; i < data.length; i++)
{
// create a object that stores the necessary data;
var obj = {
// the ID will be needed to store name;
ID: data[i].userId,
Post: data[i].title,
UserName: ""
};
var url = "https://jsonplaceholder.typicode.com/users/" + obj.userId;
$http.get(url).then(function(response)
{
// find its entry in the array and add UserName;
postList.forEach(function (item)
{
if (item.ID == response.userId)
{
// just add the correct key, but I will assume it is `userName`
item.UserName = response.userName;
// break the loop
return item;
}
});
// increment cleared
cleared++;
// call allClear
allClear();
});
postList.push(obj);
}
}
);
in this way we are sure that the data is complete before displaying it in the view.
as this solution contains a loop to map the result with its original object, we can actually change postList as an object to make it a bit faster:
// var postList = [];
var postList = {};
// instead of pushing we will use the ID as key
// postList.push(obj);
postList[obj.ID] = obj;
and so in this section:
$http.get(url).then(function(response)
{
// instead of looking for the item in .forEach
postList[response.userId].userName = response.userName;
// increment cleared
cleared++;
// call allClear
allClear();
});
hope that helps.
The easy solution would be to add the username to the user object and then push it to the scope list when the promise is resolved
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
$http.get("https://jsonplaceholder.typicode.com/posts").then(function(response) {
$scope.data = response.data;
$scope.list = [];
for (var i = 0; i < 20; i++) {
$http.get("https://jsonplaceholder.typicode.com/users/" + $scope.data[i].userId)
.then(function(response) {
var user = {
UserName: response.data.username,
Post: $scope.data[i].title
}
$scope.list.push(user);
});
}
});
});

Anyway to `angular.copy(..., ...)` without replacing data?

I have an angular factory with a function that loads a range of items from a list via an API. So if I wanted to load the first 10 I would say something like getRange(0, 10). Once the JSON returns from the API, I copy the items over to a local model using angular.copy... But how do I load the next 10 and copy the list items over, without deleting the old ones. Apparently, I can't continue to use angular.copy because it deletes the old items and then copies over the new items.
Any guidance?
You can think something like this:
var app = angular.module('plunker', []);
app.controller('demoController', function($scope) {
$scope.arr = []; // data from factory.
for (var i = 0; i < 100; i++) { // for demo filling it up
$scope.arr.push({ item: i }); // just like this
}
$scope.newArr = []; // the new item array;
$scope.len = $scope.newArr.length || 10;
$scope.getItems = getItems;
function getItems() {
for (var i = $scope.len - 10; i < $scope.len; i++) {
$scope.newArr.push(angular.copy($scope.arr[i]))
}
$scope.len += 10;
}
});
Take a look at the plnkr here.

Why ngRepeat is not populated

I'm using ngRepeat in my angular application and for some reason the ngRepeat did not populate even though the collection that it is connected to is populated with the correct data.
What i'm doing is sending http get request to a node server to request the data,
go over the result from the server and populate a collection on the scope that is connected to that specific ngRepeat.
The ngRepeat part of the Html file:
<div id="cellRow" ng-repeat="obj in rowsCollection track by obj.index">
<div class="inputsContainer">
<input ng-model="obj.col1"></input>
<input ng-model="obj.col2"></input>
<input ng-model="obj.col3"></input>
</div>
</div>
The Ctrl code:
angular.module('App').controller('Ctrl', ['$scope','dataUtils', function($scope,dataUtils) {
$scope.dataObj = null;
$scope.rowsCollection = [];
dataUtils.getDataObj()
.then($scope.initializeObjects)
.catch($scope.showError);
$scope.initializeObjects = function(data) {
if( data && data.length > 0 ) {
for(var index = 0; index < 21; index++) {
$scope.dataObj = {};
$scope.dataObj.index = index + 1;
$scope.dataObj.col1 = data[0][index];
$scope.dataObj.col2 = data[1][index];
$scope.dataObj.col3 = data[2][index];
$scope.rowsCollection.push($scope.dataObj);
}
}
};
$scope.showError = function(errorMsg) {
console.log(errorMsg);
};
}]);
The dataUtils.getDataObj calls an http get request from the server.
When using the controller in this form i see that the initializeObjects function is called and the rowCollection collection is populated but the ngRepeat stays empty.
After i changed the Ctrl ro the following code:
angular.module('App').controller('Ctrl', ['$scope','dataUtils', function($scope,dataUtils) {
$scope.dataObj = null;
$scope.rowsCollection = [];
dataUtils.getDataObj()
.then(initializeObjects)
.catch(showError);
function initializeObjects(data) {
if( data && data.length > 0 ) {
for(var index = 0; index < 21; index++) {
$scope.dataObj = {};
$scope.dataObj.index = index + 1;
$scope.dataObj.col1 = data[0][index];
$scope.dataObj.col2 = data[1][index];
$scope.dataObj.col3 = data[2][index];
$scope.rowsCollection.push($scope.dataObj);
}
}
}
function showError(errorMsg) {
console.log(errorMsg);
}
}]);
The ngRepeat did populate, why didn't the ngRepeat populate in the first Ctrl configuration but did in the second ?
If you want to use the first way, since you're calling a function, it must have () at the end.
So to achieve what you want you should change this:
.then($scope.initializeObjects)
for:
.then($scope.initializeObjects())
Note: The second way is better, since you need to reuse this $scope function in another moment, otherwise, keep it as a normal function().
In ur first implementation, it should work when u put the $scope.initializeObjects =... before using it in the promise.
$scope.initializeObjects = function(data) {
if( data && data.length > 0 ) {
for(var index = 0; index < 21; index++) {
$scope.dataObj = {};
$scope.dataObj.index = index + 1;
$scope.dataObj.col1 = data[0][index];
$scope.dataObj.col2 = data[1][index];
$scope.dataObj.col3 = data[2][index];
$scope.rowsCollection.push($scope.dataObj);
}
}
};
$scope.showError = function(errorMsg) {
console.log(errorMsg);
};
dataUtils.getDataObj()
.then($scope.initializeObjects)
.catch($scope.showError);
However, if u declare your function as your second attempt, it will be defined before the code in this closure get executed.
You may check this question to understand the difference.

Angular Factory and Service work but not as expected

My app is looking up google place details and displaying some of the information. I have a list of place id's in a json file broken down by type of establishment. A factory accesses and makes available the ids to the controller. I also have a service that loops through all the id's, looking up the details and adding them to an object that is made available to the controller.
I can get it to work in the sense that I can access the json data, look up the details, and return the object. However, no matter how I do it, if I try and return multiple objects, one for each type of business, I get all the businesses together or an error (more on that in a minute).
I have structured this a number of ways but I will show the code for 2 ways that I have tried. I'm new to Angular so I may have this completely wrong and not even using services and factories correctly so please go easy on me.
locations.json
{
"restaurants": {
"Michaels": "ChIJwaTJAL4n5IgRgyJupbpQhjM",
"Collage": "ChIJw5HgNzAm5IgRqbkEqKXIpC4",
"Scarlet": "ChIJT9ImkZUn5IgREb1hYwKA1Nc",
"Maya": "ChIJofgqBJYn5IgRVa-HQvp6KDk",
"Ice": "ChIJnXpQpewn5IgR7k9yxWXUu1M",
"Sangrias": "ChIJITcc_ZUn5IgR90iEna6FRGM",
"Columbia": "ChIJ8xR18JUn5IgRfwJJByM-quU",
"Harrys": "ChIJ8aLBaJYn5IgR60p2CS_RHIw"
},
"bars":
{
"Scarlet": "ChIJT9ImkZUn5IgREb1hYwKA1Nc",
"Lion": "ChIJqVCL_b0n5IgRpVR5CFZWi4o",
"Tradewinds": "ChIJpwF4ZJYn5IgRTDzwBWvlSIE",
"Ice": "ChIJnXpQpewn5IgR7k9yxWXUu1M",
"Stogies": "ChIJlwkiApYn5IgR6XVFMyqLAS4",
"Rondeazvous": "ChIJkz3V7pUn5IgRQhui26imF1k",
"Meehan": "ChIJK8NZGZYn5IgRA91RrGETwrQ",
"Sangrias": "ChIJITcc_ZUn5IgR90iEna6FRGM",
"NoName": "ChIJA-VeCb4n5IgRmbuF8wdOGaA",
"StGeorge": "ChIJ4yo36JUn5IgRXgiRD7KMDe0"
}
}
Method 1
locations.js
angular.module('app.locations', [])
.factory('restsFact', function($http){
var restaurants = [];
return {
getRests: function(){
return $http.get('locations.json').then(function(response){
restaurants = response.data.restaurants;
return restaurants;
});
}
};
})
.factory('barsFact', function($http){
var bars = [];
return {
getBars: function() {
return $http.get('locations.json').then(function(response){
bars = response.data.bars;
return bars;
});
}
};
})
.service('locationsService', function (ngGPlacesAPI) {
var x, id, details, push, placeDetails = [];
// Takes list of specific type of locations as argument and looks up Place details for each location
this.details = function(type) {
for (x in type) {
if (type.hasOwnProperty(x)) {
id = type[x];
ngGPlacesAPI.placeDetails({placeId: id}).then(push);
}
}
return placeDetails;
};
push = function (data) {
details = data;
placeDetails.push(details);
};
});
Controllers
.controller('RestCtrl', function($scope, locationsService, restsFact) {
// Location Details Object
restsFact.getRests().then(function(locs){
$scope.restaurants= locationsService.details(locs);
});
})
//
// Bar Controller
//
.controller('BarsCtrl', function($scope, locationsService, barsFact){
// Locations Details Object
barsFact.getBars().then(function(locs){
$scope.bars = locationsService.details(locs);
});
})
Method 2
With this method I can load one page but if I move to the next I get an error: [$rootScope:inprog] $digest already in progress. I read up on the error and get the idea of why I get it but just not sure how to go about fixing it.
locations.js
angular.module('app.locations', [])
.factory('locationsFact', function($http){
var locations = [];
return {
getlocations: function(){
return $http.get('locations.json').then(function(response){
locations = response;
return locations;
});
}
}
})
.service('locationsService', function (ngGPlacesAPI) {
var x, id, details, push, placeDetails = [];
// Takes list of specific type of locations as argument and looks up Place details for each location
this.details = function(type) {
for (x in type) {
if (type.hasOwnProperty(x)) {
id = type[x];
ngGPlacesAPI.placeDetails({placeId: id}).then(push);
}
}
return placeDetails;
};
push = function (data) {
details = data;
placeDetails.push(details);
};
});
Controller
.controller('locationsCtrl', function($scope, locationsService, locationsFact){
// Locations Details Object
locationsFact.getlocations().then(function(locs){
$scope.restaurants = locationsService.details(locs.data.restaurants);
$scope.bars = locationsService.details(locs.data.bars);
});
})
So I read a lot over the last week and learned a lot as well. I completely rewrote that mess up above into something resembling decent code, there were a lot of problems with it originally. I got everything working anyway. Here is how it looks now.
Factory
angular.module('app.factories', [])
.factory('data', function($http){
// Get JSON With Place ID's and create array of
// place id objects for each category
var places = {};
places.ids = function(){
return $http.get('locations.json')
.success(function(data){
places.rests = data.restaurants;
places.bars = data.bars;
places.lodg = data.lodging;
places.att = data.attractions;
});
};
return places;
})
.factory('details', function(ngGPlacesAPI, $q){
var details = {};
// Split ID Array into array of arrays <= 10.
// Google won't return more than 10 details request at one time.
details.process = function(type) {
var idSets = {},
size = 10,
i, j, k;
for (i=0, j=type.length, k=0; i<j; i+=size){
idSets[k] = type.slice(i, i+size);
k++;
}
return idSets;
};
// Lookup Details by Place ID
// Loop through ID array and return array of details objects
details.getDetails = function(idSet, pageNum) {
var page = idSet[pageNum],
promises = [];
for(var i=0; i<page.length; i++) {
promises.push(ngGPlacesAPI.placeDetails({placeId: page[i][i]}));
}
return $q.all(promises);
};
// Return Details Object
return details;
});
Controller
//
// Restaurants Controller
//
.controller('restaurantsCtrl', function(details, data, $scope) {
var vm = this;
// Get JSON file with placeIds and set some variables
data.ids().then(function() {
var page = details.process(data.rests),
pageNum = 0,
numPages = page.length;
vm.moreData = true;
// Loads more place details on scroll down
vm.loadMore = function() {
if (pageNum <= numPages - 1) {
pageNum++;
details.getDetails(page, pageNum).then(function(response) {
vm.rests.push(response);
vm.$broadcast('scroll.infiniteScrollComplete');
});
}else{vm.moreData=false}
};
// Load first page of place details
details.getDetails(page, pageNum).then(function(response){
vm.rests = response;
console.log(vm.rests);
});
// Watches for when to load more details
$scope.$on('$stateChangeSuccess', function(){
vm.loadMore();
});
});
})

Setting initial value in select/dropdown with AngularJS

This has been driving me crazy as I'm sure it's simple, but I've tried a multitude of different approaches over the last 2 days and nothing worked properly.
I have two select-dropdowns in my view HTML:
<p>Spend between
<select
id ='minSpend'
ng-model ="cheapest"
ng-options="offer.PriceAUD as '$'+Math.round(offer.PriceAUD) for offer in offers | uniquePrice:'PriceAUD'">
</select>
and
<select
id ='maxSpend'
ng-model ="dearest"
ng-options="offer.PriceAUD as '$'+Math.round(offer.PriceAUD) for offer in offers | uniquePrice:'PriceAUD'" >
</select>
They populate nicely with unique values once data is received.
The uniquePrice filter looks like this:
app.filter('uniquePrice', function() {
return function(input, key) {
if (typeof input == 'undefined'){return;}
var unique = {};
var uniqueList = [];
for(var i = 0; i < input.length; i++){
if(typeof unique[input[i][key]] == "undefined"){
unique[input[i][key]] = "";
input[i][key] = Math.round(input[i][key]);
uniqueList.push(input[i]);
}
}
return uniqueList;
};
});
What I like is the lowest price and highest price to be selected initially. I managed to write a fn that does it "on-click":
$scope.setValues = function(){
$scope.cheapest = $scope.offers[0].PriceAUD;
$scope.dearest = $scope.offers[ $scope.offers.length-1].PriceAUD;
}
[ $scope.offers comes from the DB sorted by PriceAUD ASC ]
Triggered in the HTML by a simple:
<button ng-click="setValues()">Set Values</button>
Which works fine. But how can I trigger this when data-loading is complete? I tried:
$scope.offers = Items.query();
$scope.offers.$promise.then(function (result) {
$scope.setValues();
});
But it doesn't work.
And when I set $scope.dearest = $scope.offers[0].PriceAUD in the controller directly it doesn't work because the promise isn't resolved. I'm sure I just need to put this in the right place, but where?
Thanks for your help.
EDIT: The code to get Items:
var appServices = angular.module('appServices', ['ngResource']);
appServices.factory('Items', ['$resource',
function($resource){
var url = '/api/get/offers.json';
var paramDefaults = {};
var actions = "{ query: {method:'GET', params:{id:'Items'}, isArray:true}";
var optns = '';
return $resource(url, paramDefaults, actions , optns);
}]);

Categories

Resources