How to use Parse JS SDK in Angular.js service? - angularjs

I'll explain my problem with a quick example.
Parse.initialize("nlxy5xYYZQ1fLfFkzcyLHOifkie1dOU0ZSxoxw1w", "IRBJO7nyd1vQquhMvnyMd298ZVJ0qWg1AjxBY5nr");
var People = Parse.Object.extend("People");
var app = angular.module('app', []);
app.controller("MyCtrl", ["$scope", "PeopleService", function($scope, PeopleService){
$scope.people = PeopleService.getPeople();
}]);
app.service("PeopleService", function(){
var people = null;
return {
getPeople: function(){
people = [];
var queryObject = new Parse.Query(People);
queryObject.find({
success: function (results) {
for (var i = 0; i < results.length; i++) {
var result = results[i];
people.push(result.get("name"));
}
return people;
},
error: function (error) {
console.error("Error: " + error.code + " " + error.message);
}
});
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js">
</script>
<script src="https://www.parsecdn.com/js/parse-1.3.2.min.js"></script>
<div ng-app="app">
<ul ng-controller="MyCtrl">
<li ng-repeat="person in people">{{person}}</li>
</ul>
</div>
Now, of course, the scope in the controller doesn't update when the data is returned by Parse.
How can I make this service work properly?

As #james stated before, using promises in $q service is what you are looking for. Here's how it could go with your example.
getPeople: function(){
var deferred = $q.defer();
people = [];
var queryObject = new Parse.Query(People);
queryObject.find({
success: function (results) {
for (var i = 0; i < results.length; i++) {
var result = results[i];
people.push(result.get("name"));
}
deferred.resolve(people);
},
error: function (error) {
deferred.reject(error);
}
});
return deferred.promise;
}
This returns a promise which you can use like this
$scope.people = PeopleService.getPeople();
or
$scope.people = PeopleService.getPeople()
.then(function(people) {
//modify data as necessary
return people
}, function(reason) {
alert('Failed: ' + reason);
}
);

Related

No output with ng-repeat

i have a problem with this ng-repeat statement.
<div ng-repeat="m in mbct.messages track by $index" ng-click="mbct.message_click($index)" ng-class="mbct.set_class(m.gelesen)">
<div>ABS: <span ng-bind="m.abs"></span> </div>
<div>Betreff: <span ng-bind="m.betreff"></span> </div>
<div>Wann: <span ng-bind="m.timestamp"></span> </div>
<hr>
</div>
Controller:
var self = this;
self.messages = [];
self.massages_length = "";
//initialize variables when app starts
self.init = function(){
alert("Init-runs");
MessageService.get_messanges().then(
function(response){
self.messages = response.data;
self.messages_length = self.messages.length;
console.log(self.messages_length);
}
)
};
self.get_message_loop = $interval(function(){
MessageService.get_messanges().then(
function(response) {
if(self.messages_length < response.data.length){
console.log("new message");
self.messages = response.data;
self.messages_length = self.messages.length;
console.log("self.messages_length:" + self.messages_length);
}
else{
console.log("no new message");
}
}
);
},500);
And my Service:
gsgmain.factory('MessageService',['$http',function($http){
return{
get_messanges : function(){
var email = $("#php_username").html();
var data = {user_email: email, methode: "get_all_msg"};
var json_data = angular.toJson(data);
return $http.post("php/server.php", json_data);
},
send_message : function(abs,emp,message_text,betreff){
var data = {abs:abs, emp: emp, message: message_text,betreff:betreff,methode:"send_message"};
var json_data = angular.toJson(data);
return $http.post("php/server.php",json_data);
},
set_read: function(msg_id){
var data = {msg_id: msg_id,methode: "set_read"};
var json_data = angular.toJson(data);
return $http.post("php/server.php",json_data);
}
}
}]);
The ng-repeat-statement has no output, since i added the if-else-statement in "get_message_loop" to check if there are new messages.
Before the get_message_loop function looked like this:
self.get_message_loop = $interval(function(){
MessageService.get_messanges().then(
function(response) {
self.messages = response.data;
}
);
},500);
and erverything works fine.
Could you tell me why?
Thank you in advance.
me again :D,
I solved the Problem. I defined the controller twice. One time in the html-tag and the other time in my routeprovider. So the code did everything twice.
And always in the second run, the variable self.messages_length was undefined.
I removed one definition and now everything works fine ;)

Another way to inject in Angular

I have a template and I'm changing the way to Inject dipendecies. It has the 'classic' way to inject. I want to replace it with the compact form. I can't do these line of code. I've tried but I can't understand the struscture. Because I am accustomed in the other form. Can somebody help me?
(function() {
'use strict';
angular.module('app.data')
.factory('postResource', postResource)
.factory('postsUtils', postsUtils);
postResource.$inject = ['$resource'];
function postResource($resource) {
return $resource('/api/posts/:id', {id: '#id'}, {
update: {
method: 'PUT'
}
});
}
postsUtils.$inject = ['postResource'];
function postsUtils(postResource) {
function postsDuringInterval(posts, days) {
var today = new Date();
var interval = 86400000 * days;
var postsDuringInterval = [];
posts.forEach(function(post) {
var postDate = new Date(post.date);
today - postDate < interval && postsDuringInterval.push(post);
});
return postsDuringInterval;
}
function recent(posts, postsNum) {
posts.sort(function(a, b) {
if (a.date < b.date) return 1;
else if (a.date == b.date) return 0;
else return -1;
});
return posts.slice(0, postsNum || 1);
}
function lastEdited(posts) {
var lastEdited = posts[0];
posts.forEach(function(post) {
lastEdited = lastEdited.date < post.date ? lastEdited : post;
});
return lastEdited;
}
return {
postsDuringInterval: postsDuringInterval,
lastEdited: lastEdited,
recent: recent
}
}
})();
Here is an example of how you would inject dependencies.
var app = angular.module('myApp',
['ngRoute', 'ngSanitize', 'ui.bootstrap', 'angular-flexslider',
'ng-backstretch', 'angular-parallax', 'fitVids', 'wu.masonry', 'timer',
'uiGmapgoogle-maps', 'ngProgress']);
An example of a controller that has a service injected into it.
app.controller('ContactController',['$scope','contactService',
function($scope, contactService) {
var self = this;
self.contact = {id:null, name:"",lastName:"",email:"",subject:"",message:""};
this.submit = function(){
contactService.submit(self.contact);
self.contact = {id:null, name:"",lastName:"",email:"",subject:"",message:""};
};
}]);
The factory:
app.factory('contactService',['$http','$q', function($http,$q){
return {
submit: function (contact) {
return $http.post('/sendForm/', contact)
.then(
function (response) {
return response;
},
function (errResponse) {
console.error("Error while submitting form" + errResponse);
return $q.reject(errResponse);
}
)
}
}
}]);
I think this is the way you were referring too. Hope this helps.

angular, try to display object in ng-repeat fails

i'm writing an mobile application in javascript with angularJS and ionicframework (last beta v.11), i create dinamically an object and want to display all objects inside in a ng-repeat. Why nr-repeat don't display anything?
This is screen from my object:
I use this code for put values in scope:
$scope.distanceSuppliers = myCar;
And this is the code in html:
<ion-item ng-repeat="(id, supplier) in distanceSuppliers">
<div class="items item-button-right" ng-click="openDetails(id)">
{{supplier.name}}<br />
{{supplier.address}}<br />
</div>
</ion-item>
This is my complete code for JS:
.controller('suppliers', function($scope, cw_db, $ionicPopup, $ionicActionSheet, appdelegate, $rootScope, $firebase, $location, $ionicLoading, cw_position) {
$ionicLoading.show({
template: 'Updating data..'
});
var geocoder;
var tot = 0;
var done = 0;
geocoder = new google.maps.Geocoder();
cw_db.getData(cw_db.getSuppliers(), "", function(suppliers) {
cw_position.getPosition(function (error, position) {
suppliers.on('value', function(supp) {
$scope.distanceSuppliers = {};
tot = 0;
done = 0;
supp.forEach(function(childSnapshot) {
tot++;
var childData = childSnapshot.val();
if (childData.address) {
calculateDistance(childData, position.coords.latitude, position.coords.longitude);
}
});
});
$ionicLoading.hide();
});
});
function calculateDistance(childData, usrLat, usrLon) {
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix(
{
origins: [new google.maps.LatLng(usrLat, usrLon)],
destinations: [childData.address],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, function(response, status) {
if (status != google.maps.DistanceMatrixStatus.OK) {
alert('Error was: ' + status);
} else {
done++;
var results = response.rows[0].elements;
childData.distance = results[0].distance.value;
$scope.distanceSuppliers.push(childData);
if (done == tot) {
console.log($scope.distanceSuppliers);
}
}
});
}
$scope.openDetails = function(index) {
//appdelegate.setCallId(index);
//$location.path("/app/supplierDetails");
}
})
what's wrong?
Not sure, but I believe you have a data binding update problem.
Try using the $timeout to force the render:
w_position.getPosition(function (error, position) {
$timeout(function() {
suppliers.on('value', function(supp) {
$scope.distanceSuppliers = {};
tot = 0;
done = 0;
supp.forEach(function(childSnapshot) {
tot++;
var childData = childSnapshot.val();
if (childData.address) {
calculateDistance(childData, position.coords.latitude, position.coords.longitude);
}
});
});
$ionicLoading.hide();
});
});
And don't forget to add the $timeout parameter to the controller:
.controller('suppliers', function($scope, ...<other parameters here>..., $timeout) {
I found the problem! Fix using $scope.$apply();
The problem was that i was writing in a different $scope using this code:
cw_position.getPosition(function (error, position) {
suppliers.on('value', function(supp) {
tot = 0;
done = 0;
supp.forEach(function(childSnapshot) {
tot++;
var childData = childSnapshot.val();
if (childData.address) {
calculateDistance(childData, position.coords.latitude, position.coords.longitude);
}
});
});
$ionicLoading.hide();
});
where cw_position.getPosition call a js with this code:
angular.module('cw_position', [])
.service('cw_position', function() {
this.getPosition = function(callback) {
navigator.geolocation.getCurrentPosition(
function (position) {
return callback(null, position);
},
function(error) {
return callback(error, null);
}
);
}
// Built google maps map option
//
this.getGoogleMapOptions = function (lat, lon, zoom, type) {
if (type == null) {
type = google.maps.MapTypeId.ROADMAP;
}
var mapOptions = {
center: new google.maps.LatLng(lat, lon),
zoom: zoom,
mapTypeId: type
};
return mapOptions;
}
});
navigator.geolocation.getCurrentPosition causes the 'problem'
Thx to all for your help

angularjs share data config between controllers

I'm wondering what could be a good way to share directive
between controller.
I've got ie two directives to use in different controller
with different configuration the first think I thought of
using like:
//html
<body data-ng-controller="MainCtrl">
<div class="container">
<div data-ui-view></div>
</div>
</body>
//js
.controller('MainCtrl', function ($scope,$upload) {
/*File upload config*/
$scope.onFileSelect = function($files) {
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
$scope.upload = $upload.upload({
url: 'server/upload/url',
method: 'POST',
data: {myObj: $scope.myModelObj},
file: file,
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
console.log(data);
});
}
};
/* Datepicker config */
$scope.showWeeks = true;
$scope.minDate = new Date();
$scope.open = function($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.opened = true;
};
$scope.dateOptions = {
'year-format': "'yy'",
'starting-day': 1
};
$scope.format = 'MMM d, yyyy';
})
.controller('IndexCtrl', function ($scope) {
})
doing so I can use all the functions in my children controller
but I don't like very much because of collision problems.
Since you cannot use a service (you can't use $scope in a service) the other alternatives could be make an other directive or put the code in a run block
but it's quite the same using a parent controller so
what do you think about ?
UPDATE
what do you think about this approach ?
//outside of angular stauff
function MyTest(){
this.testScope = function(){
console.log('It works');
}
}
//inside a controller
$scope.ns = new MyTest();
//in the view
<p ng-click="ns.testScope()">ppp</p>
RIUPDATE
this seems the best option :)
MyTest.call($scope);
Consider the method described by this post: Extending AngularJS Controllers Using the Mixin Pattern
Instead of copying your methods out of a service, create a base controller that contains those methods, and then call extend on your derived controllers to mix them in. The example from the post:
function AnimalController($scope, vocalization, color, runSpeed) {
var _this = this;
// Mixin instance properties.
this.vocalization = vocalization;
this.runSpeed = runSpeed;
// Mixin instance methods.
this.vocalize = function () {
console.log(this.vocalization);
};
// Mixin scope properties.
$scope.color = color;
// Mixin scope methods.
$scope.run = function(){
console.log("run speed: " + _this.runSpeed );
};
}
Now we can mixin AnimalController into DogController:
function DogController($scope) {
var _this = this;
// Mixin Animal functionality into Dog.
angular.extend(this, new AnimalController($scope, 'BARK BARK!', 'solid black', '35mph'));
$scope.bark = function () {
_this.vocalize(); // inherited from mixin.
}
}
And then use DogController in our template:
<section ng-controller="DogController">
<p>Dog</p>
<!-- Scope property mixin, displays: 'color: solid black' -->
<p ng-bind-template="color: {{ color }}"></p>
<!-- Calls an instance method mixin, outputs: 'BARK BARK!' -->
<button class="btn" ng-click="bark()">Bark Dog</button>
<!-- Scope method mixin, outputs: 'run speed: 35mph' -->
<button class="btn" ng-click="run()">Run Dog</button>
</section>
The controllers in this example are all in the global space and are included in the markup as follows.
<script type="text/javascript" src="lib/jquery.js"></script>
<script type="text/javascript" src="lib/angular.js"></script>
<script type="text/javascript" src="app/controllers/animal-controller.js"></script>
<script type="text/javascript" src="app/controllers/dog-controller.js"></script>
<script type="text/javascript" src="app/controllers/cat-controller.js"></script>
<script type="text/javascript" src="app/app.js"></script>
I haven't tested it, but I don't see why the following wouldn't work:
var myApp = angular.module('myApp', [])
.controller('AnimalController', ['$scope', 'vocalization', 'color', 'runSpeed', function ($scope, vocalization, color, runSpeed) { /* controller code here */}]);
.controller('DogController', ['$scope', '$controller', function($scope, $controller) {
var _this = this;
// Mixin Animal functionality into Dog.
angular.extend(this, $controller('AnimalController', {
$scope: scope,
vocalization: 'BARK BARK!',
color: 'solid black',
runSpeed:'35mph'
}));
$scope.bark = function () {
_this.vocalize(); // inherited from mixin.
}
}]);
see: docs for $controller service
What you want is terrible.
You wouldn't want your controllers to know anything about each other, let alone, one having access to the function of the other. You can just use a Service to achieve that. As for using directives, not sure what exactly you want to happen.
As for your second thing, you can as easily do this
.service('MyTestService', function(){
return {
testScope: function(){
console.log('It works');
}
};
})
.controller('MyController', ['$scope', 'MyTestService', function($scope, MyTestService){
$scope.testScope = MyTestService.testScope;
}])
and in your view:
<p ng-click="testScope()">ppp</p>
I ended up with:
//service
.service('PostUploader',function($upload){
var that = this;
var fileReaderSupported = window.FileReader !== null;
this.notify = null;
this.success = null;
this.showAlert = false;
this.avatar = '';
this.onFileSelect = function($files) {
var $file = $files[0];
var filename = $file.name;
this.avatar = filename;
var isImage = /\.(jpeg|jpg|gif|png)$/i.test(filename);
if(!isImage){
this.showAlert = true;
return;
}
this.showAlert = false;
if (fileReaderSupported && $file.type.indexOf('image') > -1) {
var fileReader = new FileReader();
fileReader.readAsDataURL($file);
fileReader.onload = that.notify;
}
$upload.upload({
url :'/api/post/upload',
method: 'POST',
headers: {'x-ng-file-upload': 'nodeblog'},
data :null,
file: $file,
fileFormDataName: 'avatar'
})
.success(that.success)
.progress(function(evt) {
})
.error(function(data, status, headers, config) {
throw new Error('Upload error status: '+status);
})
};
this.closeAlert = function() {
this.showAlert = false;
};
})
//controller
/* Uploader post */
$scope.dataUrl = null;
$scope.avatar = PostUploader.avatar;
$scope.showAlert = PostUploader.showAlert;
$scope.onFileSelect = PostUploader.onFileSelect;
$scope.closeAlert = PostUploader.closeAlert;
PostUploader.notify = function(e){
$timeout(function() {
$scope.dataUrl = e.target.result;
});
};
PostUploader.success = function(data, status, headers, config) {
$timeout(function() {
$scope.post.avatar = data.url;
});
}
$scope.$watch('avatar',function(newVal, oldVal){
if(newVal) {
$scope.avatar = newVal;
}
});
$scope.$watch('showAlert',function(newVal, oldVal){
$scope.showAlert = newVal;
$scope.dataUrl = null;
});
I did so because I've to do the same thing in create post and edit post but all in all
I've got quite the same repeated code ! :)
The only good thing is the code has got less logic.
obvious but brilliant solution (may be)
(function(window, angular, undefined) {
'use strict';
angular.module('ctrl.parent', [])
.run(function ($rootScope) {
$rootScope.test = 'My test'
$rootScope.myTest = function(){
alert('It works');
}
});
})(window, angular);
angular.module('app',['ctrl.parent'])
.controller('ChildCtrl', function($scope){
});
It's easy and clean and don't see any drawback(it's not global)
UPDATE
'use strict';
(function(window, angular, undefined) {
'use strict';
angular.module('ctrl.parent', [])
.controller('ParentController',function (scope) {
scope.vocalization = '';
scope.vocalize = function () {
console.log(scope.vocalization);
};
});
})(window, angular);
angular.module('app',['ctrl.parent'])
.controller('ChildCtrl', function($scope,$controller){
angular.extend($scope, new $controller('ParentController', {scope:$scope}));
$scope.vocalization = 'CIP CIP';
});
just a little neater and it works CIP CIP :)

AngularJS Chart Directive - Data loaded in async service not updating chart

I am having one chart directive created, and I am bootstrpping the app after loading google api. In following code, a simple data table is working fine. But when I load data from server in async manner, chart is not being displayed.
Controller
'use strict';
myNetaInfoApp.controller('allCandidatesController', [
'$scope','allCandidates2009Svc', '$timeout',
function ($scope, allCandidates2009Svc, $timeout) {
$scope.data1 = {};
$scope.data1.dataTable = new google.visualization.DataTable();
$scope.data1.dataTable.addColumn("string", "Party");
$scope.data1.dataTable.addColumn("number", "qty");
$scope.data1.dataTable.title = "ASDF";
$timeout( function (oldval, newval) {
allCandidates2009Svc.GetPartyCriminalCount().then(function(netasParty) {
var i = 0;
for (var key in netasParty) {
$scope.data1.dataTable.addRow([key.toString(), netasParty[key]]);
i++;
if (i > 20) break;
}
});
});
$scope.dataAll = $scope.data1;
//sample data
$scope.data2 = {};
$scope.data2.dataTable = new google.visualization.DataTable();
$scope.data2.dataTable.addColumn("string", "Name");
$scope.data2.dataTable.addColumn("number", "Qty");
$scope.data2.dataTable.addRow(["Test", 1]);
$scope.data2.dataTable.addRow(["Test2", 2]);
$scope.data2.dataTable.addRow(["Test3", 3]);
}
]);
Service
'use strict';
myNetaInfoApp.factory('allCandidates2009Svc', ['$http', '$q',
function ($http, $q) {
var netas;
return {
GetPartyCriminalCount: function () {
var deferred = $q.defer();
$http.get('../../data/AllCandidates2009.json')
.then(function (res) {
netas = res;
if (netas) {
var finalObj = {};
_.each(netas.data, function(neta) {
finalObj[neta.pty] = finalObj[neta.pty] ? finalObj[neta.pty] + 1 : 1;
});
deferred.resolve(finalObj);
}
});
return deferred.promise;
}
};
}]);
Directive
"use strict";
var googleChart = googleChart || angular.module("googleChart", []);
googleChart.directive("googleChart", function () {
return {
restrict: "A",
link: function ($scope, $elem, $attr) {
var dt = $scope[$attr.ngModel].dataTable;
var options = {};
if ($scope[$attr.ngModel].title)
options.title = $scope[$attr.ngModel].title;
var googleChart = new google.visualization[$attr.googleChart]($elem[0]);
$scope.$watch($attr.ngModel, function (oldval, newval) {
googleChart.draw(dt, options);
});
}
};
});
HTML
<div ng-controller="allCandidatesController">
<div class="col-lg-6">
<h2>Parties and Candidates with Criminal Charges</h2>
<div google-chart="PieChart" ng-model="dataAll" class="bigGraph"></div>
<!--<p><a class="btn btn-primary" href="#" role="button">View details ยป</a></p>-->
</div>
<div class="col-lg-6">
<h2>Heading</h2>
<div google-chart="BarChart" ng-model="data2" class="bigGraph"></div>
</div>
</div>
I think you need to wrap your function body in allCandidates2009Svc factory with scope.$apply(). But the return deferred.resolve() will be outside scope.$apply().
function asyncGreet(name) {
var deferred = $q.defer();
setTimeout(function() {
// since this fn executes async in a future turn of the event loop, we need to wrap
// our code into an $apply call so that the model changes are properly observed.
scope.$apply(function() {
deferred.notify('About to greet ' + name + '.');
if (okToGreet(name)) {
deferred.resolve('Hello, ' + name + '!');
} else {
deferred.reject('Greeting ' + name + ' is not allowed.');
}
});
}, 1000);
return deferred.promise;
}
Read the docs here
http://docs.angularjs.org/api/ng.$q

Resources