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