Fetching JSON data from multiple web api methods ($q.all) - angularjs

I have a simple app with a form. When the form loads I want to call a couple of web api methods to fetch the json data used to initialize the form. The form is working fine with hardcoded data in my factory class. I am unsure how I can make multiple request in that file and is kind of stuck.
The form:
<div class="modal-header" style="text-align:center">
<h3 class="modal-title">Configure</h3>
<div style="margin-top:10px">
<button tabindex="100" class="btn btn-success pull-left" type="submit">Submit</button>
<button class="btn btn-warning pull-right" ng-click="close($event)">Close</button>
</div>
</div>
<div class="modal-body">
<div class="col-sm-6" style="width: 100%;">
<form name="joinForm">
<div class="form-horizontal">
<div class="form-group">
<label class="control-label col-sm-3">Symbol</label>
<div class="col-sm-9">
<select ng-model="simulationsettings.symbols" ng- options="key as value for (key,value) in symbols"></select>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3">Interval</label>
<div class="col-sm-9">
<select ng-model="simulationsettings.intervals" ng-options="key as value for (key,value) in intervals"></select>
</div>
</div>
</div>
</form>
</div>
The controller:
mainApp2.controller("moduleConfigformController",
function moduleConfigformController($scope, moduleConfigformService,$uibModalInstance) {
$scope.close = function(e) {
$uibModalInstance.dismiss();
e.stopPropagation();
};
$scope.simulationsettings = moduleConfigformService.simulationsettings;
$scope.symbols = $scope.simulationsettings.symbols;
$scope.intervals = $scope.simulationsettings.intervals;
});
The factory class that holds the (hard coded) data for the form:
mainApp2.factory("moduleConfigformService",
function () {
return {
simulationsettings: {
symbols: {
'symbol1': "symbol1",
'symbol2': "symbol2"
},
intervals: {
'60': "1 minute",
'120': "2 minutes",
'180': "3 minutes"
}
}
}
});
Instead of hard coded values I want to call the server but is pretty stuck after several hours of research and trail and error:
mainApp2.factory("moduleConfigformService",
function () {
function getSymbols() {
return $http.get("/api/getsymbols");
}
function getIntervals() {
return $http.get("/api/getIntervals");
}
return {
simulationsettings: {
symbols : getSymbols()
},
intervals : getIntervals()
}
});
Can you point me in the right direction?

The $http Service doesn't return values. It returns promises. See AngularJS $http Service API Reference - General Usage. Also using promise based APIs from a factory can be a bit tricky. I would suggest writing and debugging the code in the controller and later re-factor to use a factory.
app.factory("moduleConfigformService",
function ($q,$http) {
function getSymbols() {
return $http.get("/api/getsymbols")
.then(function (response) {
return response.data;
});
}
function getIntervals() {
return $http.get("/api/getIntervals")
.then(function (response) {
return response.data
});
}
return {
getSymbols: getSymbols,
getIntervals: getIntervals,
simulationsettings: function () {
var promiseHash = {};
promiseHash.symbols = getSymbols();
promiseHash.intervals = getIntervals();
return $q.all(promiseHash);
}
}
});
Usage
var settingsPromise = moduleConfigformService.simulationsettings();
settingsPromise.then(function(settings) {
$scope.simulationsettings = settings;
}).catch(function(error) {
throw error;
});

If i take a look at your controller, i don't see that you wait for the http request to return an answer.
You should take a look at angularjs promises
In short you have to have to use something like
moduleConfigformService.simulationsettings.then(function (response) {
//doSomeThingWithResponse
})

Related

How can I access $scope asynchronous data from another method in same controller

When my scope has two properties that each contain an array retrieved asynchronously from the database, I cannot elegantly access the one from the other. I'm sure I must be missing something because my current solution feels like a hack.
I have an Angular CRUD page that contains a table and a form which contains a dropdown select control. When I click on a row in the table and want to update it, I need to populate the dropdown with the current value. In order to get this right, I'm currently declaring a global variable and assign the array used to populate the dropdown to that variable once it is retrieved from the database.
var columnHeaderArray;
var app = angular.module('myApp', []);
app.controller('ColumnHeadingController',
function ($scope, $http) {
$scope.GetAllData = function () {
$http({
method: "get",
url: "/api/Staat8Maintenance/GetAllColumnHeadings"
}).then(function (response) {
$scope.columnheaders = response.data;
$scope.GetGroupHeaderData();
},
function () { alert("Error Occured"); });
};
$scope.GetGroupHeaderData = function () {
$http({
method: "get",
url: "/api/Staat8Maintenance/GetGroupHeadingsForCombo"
}).then(function (response) {
$scope.groupheaders = response.data;
columnHeaderArray = response.data;
},
function () { alert("Error Occured"); });
};
$scope.UpdateColumnHeading = function (cho) {
document.getElementById("OriginalOrder").innerHTML = cho.ColumnOrder;
document.getElementById("OriginalColumnHeading").innerHTML = cho.ColumnHeading;
$scope.ColumnHeading = cho.ColumnHeading;
$scope.ColumnOrder = cho.ColumnOrder;
$scope.SelectedOption = columnHeaderArray[columnHeaderArray.findIndex(x => x.GroupingHeaderId == cho.GroupingHeaderId)];
document.getElementById("btnSave").setAttribute("value", "Update");
document.getElementById("btnSave").style.backgroundColor = "Yellow";
document.getElementById("formColumnHeading").style.display = "block";
};
}
);
<div id="SubAccountGrouping" class="tabcontent"
ng-controller="ColumnHeadingController"
ng-init="GetAllData()">
<h2>Column Headings</h2>
<h5>This is where the column headings will be maintained.</h5>
<div id="formColumnHeading" class="form" role="form">
<div class="container">
<div class="row">
<div class="form-horizontal">
<div class="form-group">
<label class="col-sm-1 edittextwide">Heading:</label>
<div class="col-sm-6">
<input type="text" class="form-control edittextwide"
id="inputColumnHeading"
placeholder="Column Heading"
ng-model="ColumnHeading" />
</div>
</div>
<div class="form-group">
<label class="col-sm-1">Order:</label>
<div class="col-sm-6">
<input type="number" class="form-control"
id="inputColumnOrder"
placeholder="Order"
ng-model="ColumnOrder" />
</div>
</div>
<div class="form-group">
<label class="col-sm-1 edittextwide">Group Heading:</label>
<div class="col-sm-6">
<select class="form-control edittextwide"
name="groupHeadings"
id="selectGroupHeadings"
ng-model="SelectedOption"
ng-options="gh as gh.HeadingName for gh in groupheaders track by gh.GroupingHeaderId">
</select>
</div>
</div>
</div>
</div>
<div class="row">
<div>
<input type="button" id="btnSave" class="form-control btn-default"
value="Submit" ng-click="InsertData()" />
</div>
</div>
</div>
<div class="hiddenlabel">
<label id="OriginalOrder">0</label>
<label id="OriginalColumnHeading">ABC</label>
</div>
</div>
<div class="scrolldiv">
<table class="table">
<thead>
<tr>
<th>Heading</th>
<th>No of Sub-Accounts</th>
<th>Column Order</th>
<th>Group Heading</th>
<th>Parent Group Heading</th>
<th>Include in Staat 8</th>
<th>Actions</th>
</tr>
</thead>
<tr ng-repeat="cho in columnheaders">
<td>{{cho.ColumnHeading}}
</td>
<td>{{cho.NumberOfEntries}}
</td>
<td>{{cho.ColumnOrder}}
</td>
<td>{{cho.GroupHeading}}
</td>
<td>{{cho.ParentGroupHeading}}
</td>
<td>{{cho.IncludeInStaat8?'Yes':'No'}}
</td>
<td>
<input type="button" class="btn btn-warning"
value="Update"
ng-click="UpdateColumnHeading(cho)" />
</td>
</tr>
</table>
</div>
</div>
When I try to set $scope.SelectedOption using $scope.groupheaders directly, it bombs out. I realise this is because of the asynchronous nature, but I suspect there must be a more elegant way to achieve this?
// include $q to use promises
function ($scope, $http, $q) {
// create a deferred promise
var q = q.defer();
// call the first $http method and store the promise
var promise1 = $http(config1);
// call the second $http method and store the promise
var promise2 = $http(config2);
// handle when the first promise resolves
promise1
.then( (data) => {
$scope.columnheaders = response.data;
})
.catch( (errors) => q.reject(errors));
// handle when the second promise resolves
promise2
.then( (data) => {
$scope.groupheaders = response.data;
})
.catch( (errors) => q.reject(errors));
// wait for both promises to resolve then do final actions
$q.all([promise1, promise2])
.then( () => {
columnHeaderArray = response.data;
q.resolve();
});
// return the promise for calling methods to wait until this resolves
return q.promise;
}
Reference for using $q
Reference for using $http
You can streamline the above code to make it more condensed, but I've broken it out some to be a little easier to follow.

AngularJS Textarea If data Is loading

I have a textarea that relies upon a dropdown menu to populate. When the dropdown is changed, a file is pulled and the contents are loaded to the textarea.
While the textarea is loading, it just says [object Object]. I'd like it to be a bit nicer than that. Something like 'Loading...'.
I cant find away to specifically do this with a textarea though.
Another wrench in the wheel is that the Save functionality actually relies upon the value of the text area to save, so I cant just alter the content of the text area to display 'Saving...' otherwise the content that is written to the file is just 'Saving...'.
Here is the code:
View
<div id="Options" class="panel-collapse collapse">
<div class="panel-body">
<div class="form-group">
<div class="input-group">
<span class="input-group-addon input-sm">Config Select</span>
<select ng-change="update()" ng-model="configFileName" class="form-control input-sm">
<option>--</option>
<option ng-repeat="conf in configList" value="{{conf.name}}">{{conf.name}}</option>
</select>
</div>
</div>
<div class="form-group">
<div class="input-group">
<td style="padding-bottom: .5em;" class="text-muted">Config File</td><br />
<textarea id="textareaEdit" rows="20" cols="46" ng-model="configFileContent"></textarea>
<input type="button" ng-click="updateConfig()" style="width: 90px;" value="Save"></button>
</div>
</div>
</div>
</div>
JS
$scope.update = (function(param) {
$scope.configFileContent = 'Loading...';
$scope.configFileContent = $api.request({
module: 'Radius',
action: 'getConfigFileContent',
method: 'POST',
data: $scope.configFileName
}, function(response) {
$timeout(function() {
console.log('got it');
$scope.configFileContent = response.confFileContent;
}, 2000);
});
});
$scope.updateConfig = (function(param) {
var data = [$scope.configFileName, $scope.configFileContent];
var json = JSON.stringify(data);
$scope.configFileContent = $api.request({
module: 'Radius',
action: 'saveConfigFileContent',
method: 'POST',
data: json
}, function(response) {
$timeout(function() {
console.log('Saved!');
$scope.update();
}, 2000);
});
});
<script>
var app = angular.module("myShoppingList", []);
app.controller("myCtrl", function($scope, $timeout) {
$scope.update = function() {
if ($scope.selectedData === '') {
$scope.someData = '';
return;
}
// do http response
var data = 'dummy file text from server';
$scope.xhr = false;
$scope.msg = 'loading...';
// simulating fetch request
$timeout(function() {
$scope.xhr = true;
$scope.content = data;
}, 3000);
}
});
</script>
<div ng-app="myShoppingList" ng-controller="myCtrl">
<select ng-model="selectedData" ng-change="update()">
<option selected="selected" value="">Select data</option>
<option value="foo">Fetch my data</option>
</select>
<br><br><br>
<textarea rows="5" cols="20" ng-model="someData" ng-value="xhr === false ? msg : content">
</textarea>
</div>
You can use a scope variable to detect the completion of promise request of xhr and simulate a loading... message.
As for save, i recommend not to use such approach of displaying message inside textarea and instead create another directive/component to detect the loading and saving request completion which is reusable and separates business logic keeping controller thin.

Using AngularJs Promise to Retrieve and Store an Entity

Following submission of a form to an AngularJS controller method, I need to take the value of a select menu (which in this case is the entity id) and retrieve that entity from my REST endpoint. I understand that when I use $http.get, the data is being retrieve asynchronously and so storing this data can be tricky.
I have been trying to follow a few articles which cover promises and deferring but I haven't quite gotten it right yet.
In the following example, I want to come away with a Room object. Right now, I'm just getting an empty object as seen here:
{"room":{},"user":{"userId":1,"userName":"tom","links":[{"rel":"self","href":"http://localhost:8080/libroomreserve/api/user/1"}]},"startTime":"2011-01-10 00:00:00","endTime":"2011-01-10 00:00:00","note":"weewwe"}
This is my controller method:
$scope.newReservation = function(){
var packagedUser = reservationService.fetchRoomObj($scope.reservation.room).then(function(roomObj){
return roomObj;
});
var finalReservation = {
room: packagedUser,
user: $scope.reservation.user,
startTime: $scope.reservation.startTime,
endTime: $scope.reservation.endTime,
note: $scope.reservation.note
};
reservationService.addReservation(
finalReservation,
function(data){
console.log("Success!");
$state.go("home");
},
function(data){
console.log("Failure!");
}
);
};
As you can see, I am trying to use the promise from my factory service's method (below) to retrieve the object.
reservations.fetchRoomObj = function(room){
return $http.get("/libroomreserve/api/room/" + room).then(function(response){
console.log("success http!");
console.log(response);
return response;
});
};
My console.log() calls return the object just fine so I know my only error is in properly storing my response data to a variable.
Here's the actual HTML form if that helps...
<form ng-submit="newReservation()">
<div class="form-group">
<label>Start </label>
<input type="text" ng-model="reservation.startTime" class="form-control">
</div>
<div class="form-group">
<label>End </label>
<input type="text" ng-model="reservation.endTime" class="form-control">
</div>
<!--<div class="form-group">
<label>User </label>
<input type="number" ng-model="reservation.user" class="form-control">
</div>-->
<div class="form-group">
<label>Room </label>
<select ng-model="reservation.room" class="form-control">
<option ng-repeat="room in roomsList" value="{{ room.roomId }}">{{ room.roomNumber }}</option>
</select>
</div>
<div class="form-group">
<label>Note</label>
<textarea ng-model="reservation.note" class="form-control"></textarea>
</div>
<button class="btn btn-success" type="submit">Reserve</button>
</form>
JSFiddle Here
You need to continue your code only after the call of then() method:
$scope.newReservation = function() {
reservationService.fetchRoomObj($scope.reservation.room).then(function(roomObj) {
var finalReservation = {
room: roomObj,
user: $scope.reservation.user,
startTime: $scope.reservation.startTime,
endTime: $scope.reservation.endTime,
note: $scope.reservation.note
};
reservationService.addReservation(
finalReservation,
function(data){
console.log("Success!");
$state.go("home");
},
function(data){
console.log("Failure!");
}
);
});
};
I used the roomObj directly to make the finalReservation object, and then, I call the addReservation() function.
The $http legacy promise method "success" have been deprecated. Use the standard "then" method instead.
return $http.get("/libroomreserve/api/room/" + room).then(function(response){
console.log("success http!");
console.log(response.data);
return response.data;
});

angular view not updating when scope changes in controller (after form submit)

I'm very new to angular so I may be going about this all wrong but here goes. I have a form
<form name="search_form" novalidate ng-submit="searchForm(search_form.$valid)" >
<div class="maincontainer">
<div class="formcontainer">What to eat?</div>
<div class="formcontainer"><input type="text" name="food_type" ng-model="food_type" placeholder="Enter a search term" required></div>
<div class="formcontainer">Where to look?</div>
<div class="formcontainer"> <input type="text" name="cityname" ng-model="trader.cityname" value="cityname" googleplace="" placeholder="Enter a location" required>
</div>
<div class="formcontainer">
<button type="submit" class="btn-main2" >Submit</button>
</div>
</form>
that when I submit I want to grab the results based on the location I get from google and display them in a new view
myControllers.controller('SearchCtrl',['$scope','Search','$location', function ($scope,Search,$location) {
$scope.setSearchLocation = function(place){
$scope.lat = place.geometry.location.lat();
$scope.lng = place.geometry.location.lng();
}
$scope.searchForm = function() {
// check to make sure the form is valid
if (!$scope.search_form.$valid) {
alert('Please fill out all fields');
}
else{
$scope.results = Search.do_search($scope.lat,$scope.lng);
$location.path('search-results');
}
};
}])
.directive('googleplace', function() {
return {
require : 'ngModel',
link : function(scope, element, attrs, model) {
var options = {
types : [],
};
scope.gPlace = new google.maps.places.Autocomplete(element[0],options);
google.maps.event.addListener(scope.gPlace, 'place_changed',function() {
var place = scope.gPlace.getPlace();
scope.setSearchLocation(place);
scope.$apply(function() {
model.$setViewValue(element.val());
});
});
},
};
});
everything works as expected except the view does not update in the results view. If I set the $scope.results out side the searchForm() function everything renders properly. I realize this is because it exists before the page renders, just saying that part works.
when I try $scope.$apply() it says already in progress
<div id="results-container" ng-repeat="result in results">
<div id="picbox"><img src="../images/test.jpg" alt="" "/></div>
<div id="addressinfo">
<h4>John's Restaurant </h4>
<p>123 York Street, Toronto ON <br>
<span id="type">#
Burgers, #Poutine</span></p>
</div>
<div id="location">4.2m<br>
<img src="../images/heart.png" width="86" height="76" alt=""/><br>
</div>
</div>
</div>
When you call $location.path(...), $scope object of controller is always initialized.
My suggestion is ...
write the element of div#results-container on the same template where form[name=search_form] exists.
remove $location.path('search-results');
I hope this could help you.

How to query parse.com users through factory Service in AngularJS?

Building an cross-platform hybrid app using Parse.com, AngularJS using the Ionic Framework. The user creation and querying works fine when using the simple parse.com code from the docs.
However I have been trying to put the query into a AngularJS service, so that it can be accesses and I can do a ng-repeat to display the returned results in a list.
The code put in place so far is this:
View (search.html):
<div class="row">
<div class="col col-75">
<label class="item item-input">
<i class="icon ion-search placeholder-icon"></i>
<input type="text" placeholder="Search" ng-model="search.queryvalue">
</label>
</div>
<div class="col">
<button class="button button-calm" ng-click="searchnow()">Search</button>
</div>
</div>
<ion-list>
<ion-item class="item-avatar" type="item-text-wrap" ng-repeat="user in users">
<img ng-src="{{userpic}}.png">
<h2>{{user.id}}</h2>
<p>{{user.get('location')}}</p>
</ion-item>
</ion-list>
Controller:
.controller('SearchCtrl', function($scope, $state, $rootScope, parseQueryFactory) {
$scope.search = {};
$scope.users = {};
$scope.searchnow = function () {
$scope.users = parseQueryFactory.searchUsers($scope.search.queryvalue);
};
})
Services:
.factory('parseQueryFactory', function($http) {
var query = new Parse.Query(Parse.User);
var people = {};
return {
searchUsers: function(searchVal){
query.startsWith("username", searchVal); // Search username
query.limit(20);
return query.find({
success: function(people) {
/*var objects = {};
for (var i = 0; i < people.length; i++) {
var object = people[i];
objects = objects + object;
}
console.log(people);
return objects;*/
},
error: function(error) {
return error;
}
});
}
}
})
I have tried a few ways to make this work (using sources like the ionic forum, stackoverflow and Google in general), but I am new to angular and not sure how to go about doing this.
The only thing that works is by putting the following code in the controller (but then I cannot use ng-repeat):
$scope.searchnow = function () {
var queryvalue = $scope.user.queryvalue;
userquery.startsWith("username", queryvalue); // Search username
userquery.limit(20);
userquery.find({
success: function(people) {
for (var i = 0; i < people.length; i++) {
var object = people[i];
console.log("ID: " + object.id + ', username: ' + object.get('username'));
}
}, error: function(error) {
console.log("error");
}
});
};
Has anyone implemented such a service for parse.com?
I have looked around and tried implementations from various people, but nothing seems to work in a way that returns a service like response from which ng-repeat comands can be done.
I believe this might help.
Services:
app.factory('Presentation', function($q) {
var Presentation = Parse.Object.extend(Parse.User, {
// Instance methods
}, {
// Class methods
listByUser : function() {
var defer = $q.defer();
var query = new Parse.Query(this);
query.startsWith("username", searchVal); // Search username
query.limit(20);
query.find({
success : function(aPresentations) {
defer.resolve(aPresentations);
},
error : function(aError) {
defer.reject(aError);
}
});
return defer.promise;
}
});
// Properties
Presentation.prototype.__defineGetter__("location", function() {
return this.get("location");
});
Presentation.prototype.__defineGetter__("pic", function() {
var pic = this.get("pic");
if (pic ==null){
pic = 'img/default.png';
return pic;
}
return pic.url();
});
return Presentation; });
Controller:
app.controller('userController', function( Presentation){
user = this;
Presentation.listByUser().then(function(aPresentations) {
user.list = aPresentations;
}, function(aError) {
// Something went wrong, handle the error
});
});
html:
<div class="row">
<div class="col col-75">
<label class="item item-input">
<i class="icon ion-search placeholder-icon"></i>
<input type="text" placeholder="Search" ng-model="search.queryvalue">
</label>
</div>
<div class="col">
<button class="button button-calm" ng- click="searchnow()">Search</button>
</div>
</div>
<ion-list>
<ion-item class="item-avatar" type="item-text-wrap" ng-repeat="user in users">
<img ng-src="{{user.pic}}">
<h2>{{user.id}}</h2>
<p>{{user.location}}</p>
</ion-item>
</ion-list>
For further reading you can check out 5 Tips for using parse with angularjs by slidebean.

Resources