I have problem with the Scope of JS functions and/or Angularjs promise and $q. I looked up some examples but I did not get it at all.
In my app I have some data saved in indexeddb of my browser. In Frontend there is an input field with autocomplete function. The value of the input field goes to my angularjs Controller and calls a function. The function should return the found values. The logic of finding values from indexeddb is working fine.
The problem is that I return my array before the values are pushed into the array. So I am not sure if I need to change some stuff in JavaScript scope or to use Angularjs stuff.
Here is my Controller:
var AutocompleteController = function($scope, $http, $q) {
$scope.getCodeFromDB = function(val) {
var codes = [];
var openDbRequest = indexedDB.open('testDb', 1);
openDbRequest.onsuccess = function (e) {
var db = e.target.result;
var transaction = db.transaction("codeobjekt");
transaction.objectStore("codeobjekt")
.openCursor(IDBKeyRange.bound(val, val + '\uffff'))
.onsuccess = function (e) {
var cursor = e.target.result;
if (cursor) {
codes.push(cursor.value.code); //gets filled
console.log(cursor.value.code);
cursor.continue();
}
};
};
console.log(codes); //is empty
return codes;
};
};
Thanks in advance.
in your code you try to use an array filled after the return.
Try using the $q service provides by angular like that.
var AutocompleteController = function($scope, $http, $q) {
$scope.getCodeFromDB = function(val) {
var def= $q.defer(),
codes = [],
openDbRequest = indexedDB.open('testDb', 1);
openDbRequest.onsuccess = function (e) {
var db = e.target.result;
var transaction = db.transaction("codeobjekt");
transaction.objectStore("codeobjekt")
.openCursor(IDBKeyRange.bound(val, val + '\uffff'))
.onsuccess = function (e) {
var cursor = e.target.result;
if (cursor) {
codes.push(cursor.value.code); //gets filled
console.log(cursor.value.code);
cursor.continue();
}
else {
//cursor end
def.resolve(codes);
}
};
};
return def.promise;
};
};
And the usage
//usage
$scope.getCodeFromDB("value")
.then(function(codes) {
//codes filled
});
Related
I am using a factory to get list of folders and display it in the front end. Also in the front end i have form where i can add new folder to the existing list. After adding a folder i want to refresh my factory instance and display the updated folder list.
// factory
angular.module('myapp').factory('archiveService', ['$http', 'ApiUrl', function($http, ApiUrl) {
var archiveService = function() {
this.repos = [];
this.busy = false;
this.page = 0;
};
archiveService.prototype.nextPage = function() {
if (this.busy) return;
this.busy = true;
var url = ApiUrl.url + '/folders/?page=' + this.page;
$http.get(url).then(function(res) {
console.log(res);
this.repos = res.data;
if (items.repos == 0) {
return;
}
this.page += 1
this.busy = false;
}.bind(this)).catch(function(data) {
}.bind(this));
};
return {
archiveService: archiveService,
}
}]);
// this is my controller
angular.module('myapp').controller('archiveModalController', ['$rootScope', '$scope','archiveService', function($rootScope, $scope, archiveService) {
// I want to refresh this and show new data on update
$scope.archivelist = new archiveService.archiveService();
}])
I would like to know how can i refresh so i can get the new updated data
$scope.archivelist = new archiveService.archiveService();
Angular services follow the singleton pattern meaning the instantiation of a class is restricted to a single object.
Also, since you are using a factory:
angular.module('myapp').factory('archiveService', [<dependencies>, function () {
function nextPage() {
//code here
);
return {
nextPage: nextPage
}
}]);
Then in your controller you simply:
archiveService.nextPage();
Seeing the variables in place I believe nextPage could simply receive the page as a parameter and since repos is an array I guess you meant to add the new fetched data to that array?
Which would be:
this.repos.push(res.data;) instead of this.repos = res.data;
Point is, each time you want to request new data you should call the correct service/factory method from the controller.
So at your controller init, you only need:
(function init() {
$scope.archivelist = archiveService.nextPage();
})();
Although like I stated, you should probably have an initial value like nextPage(1) and from there send the desired page you want to the factory method to be handled correctly.
I'm sending a data ...
....
// upload on file select or drop
$scope.upload = function (file, id) {
id = typeof id !== 'undefined' ? id : null;
Upload.base64DataUrl(file).then(function(base64){
//auth
var fbAuth = FirebaseURL.getAuth();
//Ref
var ref = FirebaseURL.child("users_photos");
ref.push({'image': base64,'removed': true, 'user_id': fbAuth.uid, 'dt_created':Firebase.ServerValue.TIMESTAMP ,'dt_updated':Firebase.ServerValue.TIMESTAMP}, function(error){
if (error) {
alert('Error');
} else {
var newID = ref.key();
//I would like display data insert here?
console.log(DATA RESULT INSERT);
}
});
});
I would like display data inserted.
It is possible to display the last inserted object without query by the key?
Use AngularFire for synchronized collections.
Create a query using limitToLast(1) to always sync the last inserted object.
angular.module('app', ['firebase'])
.constant('FirebaseUrl', '<my-firebase-app>')
.service('rootRef', ['FirebaseUrl', Firebase)
.factory('userItems', UserItems)
.controller('MyCtrl', MyController);
function UserItems($firebaseArray, rootRef) {
return function userItems(uid) {
var itemRef = rootRef.child('items');
var query = itemRef.orderyByChild('uid').equalTo(uid);
return $firebaseArray(query);
}
}
function MyController($scope, userItems, rootRef) {
$scope.items = userItems(rootRef.getAuth().uid);
$scope.addItem = function addItem(item) {
$scope.items.$add(item).then(function(ref) {
var record = $scope.items.$getRecord(ref.key());
// save the data to the other structure
});
};
}
See the section on Complex queries for more info.
I need to use $q to wait until my async function has completed and then do something.
However I have tried injecting $q into my angular module as well as my angular functions and I am getting the message $q is undefined.
Can someone tell me how I can go about being able to use this in my code?
Here is the code for the module and the function I want to use $q in respectively
Module
var droidSync = angular.module('droidSync', ['ionic', 'ngRoute', 'ui.router']);
Controller and FunctionIn this case I want to wait for the results.forEach to finish then I want to hide my loading screen using $ionicLoading.hide()
droidSync.controller('mainController', function ($scope, $ionicLoading) {
$scope.syncContacts = function () {
//Display a loading screen while sync is in execution
$ionicLoading.show({
template: '<p>Syncing Contacts...</p><ion-spinner class="spinner-calm" icon="crescent"/>'
});
var table = AzureService.getTable('contact');
table.read().done(function (results) {
results.forEach(function (result) { //THIS NEEDS TO BE COMPLETE BEFORE HIDING LOAD SCREEN
console.log('result is', result);
// If the contact is flagged as deleted check if its on the device and delete it
if (result.isdeleted == true) {
var options = new ContactFindOptions();
options.filter = result.id;
options.multiple = false;
var fields = ["*"];
navigator.contacts.find(fields, findSuccess, findError, options);
function findSuccess(contact) {
if (contact.length > 0) {
console.log("inside the delete area:", contact);
var contactToDelete = navigator.contacts.create();
//It is safe to use contact[0] as there will only ever be one returned as AzureID is unique
contactToDelete.id = contact[0].id;
contactToDelete.rawId = contact[0].id;
console.log('we want to delete this', contactToDelete);
contactToDelete.remove();
console.log('Contact Deleted');
}
else {
console.log('Contact to delete not present on device. Checking next contact');
}
}
function findError() {
console.log('Contact search failed: Deleted Contact Search');
}
}
else {
//create a contact object to save or update
var emails = [];
var phoneNumbers = [];
var name = new ContactName();
var contactToUpdate = navigator.contacts.create();
contactToUpdate.note = result.id;
name.givenName = result.firstname;
name.familyName = result.lastname;
phoneNumbers[0] = new ContactField('mobile', result.mobilephone, true);
phoneNumbers[1] = new ContactField('home', result.homephone, false);
emails[0] = new ContactField('work', result.email, true);
contactToUpdate.name = name;
contactToUpdate.phoneNumbers = phoneNumbers;
contactToUpdate.emails = emails;
//Search for the contact on the device
var options = new ContactFindOptions();
options.filter = result.id;
options.multiple = false;
var fields = ["*"];
navigator.contacts.find(fields, foundSuccess, foundError, options);
function foundSuccess(contact) {
if (contact.length > 0) {
//The contact has been found on the device. Pass in ids for contact, emails and phone numbers to update.
console.log('object to update is object is', contact);
console.log('contact array length is ', contact.length);
contactToUpdate.id = contact[0].id;
contactToUpdate.rawId = contact[0].rawId;
contactToUpdate.phoneNumbers[0].id = contact[0].phoneNumbers[0].id;
contactToUpdate.phoneNumbers[1].id = contact[0].phoneNumbers[1].id;
contactToUpdate.emails[0].id = contact[0].emails[0].id;
console.log('about to save this', contactToUpdate);
contactToUpdate.save(upSuccess, upError);
function upSuccess() {
console.log('updated a contact!');
}
function upError(ContactError) {
console.log('error updating a contact!');
}
}
else {
//The contact does not exist on the device. Just save it.
console.log('non existent contact: ', contactToUpdate);
contactToUpdate.save(saveSuccess, SaveError);
function saveSuccess() {
console.log('saved a contact!');
}
function SaveError() {
console.log('error saving a contact!');
}
}
}
function foundError() {
console.log('Contact search failed: Undeleted Contact Search');
}
} // end else
})) // end forEach
}) // table.read()
}; // scope.syncContacts()
});
So i'd probably do something like this
This is completely untested code so take that for what you will
$q.all is what your going to want to look into
droidSync.controller('mainController', ["$scope", "$q", "$ionicLoading",
function ($scope, $q, $ionicLoading) {
var loop = function(result){
var deferred = $q.defer();
deferred.resolve(// your loop stuff);
return deferred.promise;
};
var loopingFunction = function(results){
var promises = [];
results.forEach(function(result){
promises.push(loop(result));
});
return $q.all(promises);
};
$scope.syncContacts = function () {
//Display a loading screen while sync is in execution
$ionicLoading.show({
template: '<p>Syncing Contacts...</p><ion-spinner class="spinner-calm" icon="crescent"/>'
});
var table = AzureService.getTable('contact');
table.read().done(function (results) {
loopingFunction(results).then(function(){
// do something after it finishes
$ionicLoading.hide()
});
});
};
}]);
I am really new to angular.js. This is my first Service that I wrote. It takes a json object and updates the scope of the controller. I'm a little confused on it though...I think I am supposed to wrap the inner code of sseListener and return it as a function, but I am not sure how exactly I would write that and why I need to return it as a function.
Also, if I inject this service to several controllers, would that multiply the event listeners? I only want to have one event listener.
one#demo ~/cloudimageshare-monitoring/project/app/scripts $ cat services/sse_listen.js
angular.module('monitorApp')
.factory('sseListener', function () {
var source = new EventSource('/subscribe');
source.addEventListener('message', function(e) {
var result = JSON.parse(e.data);
event = Object.keys(result)[0];
switch(event) {
case "cpuResult":
cpuUpdate(result);
break;
}
});
}
one#demo ~/cloudimageshare-monitoring/project/app/scripts $ cat controllers/main.js
'use strict';
angular.module('monitorApp')
.controller('homeCtrl', function($scope, $location, $document) {
console.log("s");
});
angular.module('monitorApp')
.controller('cpuCtrl', function($scope) {
$scope.apiTimeStamp = "";
$scope.infoReceived = "";
$scope.last15 = "";
$scope.last5 = "";
$scope.lastMinute = "";
var cpuUpdate = function (result) {
$scope.$apply(function () {
$scope.apiTimeStamp = result.cpuResult.timestamp;
$scope.infoReceived = new Date();
$scope.last15 = result.cpuResult.metrics['1m'].data
$scope.last5 = result.cpuResult.metrics['5m'].data
$scope.lastMinute = result.cpuResult.metrics['15'].data
});
}
});
Instead of calling cpuUpdate directly (it's not clear to me how your factory gets a reference to this function), it would be better to use $rootScope.$broadcast(eventName, data), and react to the event in your controller. Also, you should return an object from factories, but since you don't need to inject this anywhere, it's best to put it in an app.run. Here's how I think your code should with the changes I mentioned:
angular.module('monitorApp').
run(function ($rootScope) { //Inject the $rootScope
var source = new EventSource('/subscribe');
source.addEventListener('message', function(e) {
var result = JSON.parse(e.data);
event = Object.keys(result)[0];
switch(event) {
case "cpuResult":
// Broadcast the event with data
$rootScope.$broadcast('$cpuResult', result);
break;
}
});
}).
controller('cpuCtrl', function($scope)){
$scope.apiTimeStamp = "";
$scope.infoReceived = "";
$scope.last15 = "";
$scope.last5 = "";
$scope.lastMinute = "";
// Need to pass the event to cpuUpdate,
var cpuUpdate = function (e, result) {
$scope.$apply(function(){
$scope.apiTimeStamp = result.cpuResult.timestamp;
$scope.infoReceived = new Date();
$scope.last15 = result.cpuResult.metrics['1m'].data
$scope.last5 = result.cpuResult.metrics['5m'].data
$scope.lastMinute = result.cpuResult.metrics['15'].data
});
};
//Listen for the event, call cpuUpdate when caught
$scope.$on('$cpuResult', cpuUpdate);
});
I'm building an offline HTML page using Angular and using ydn-db for offline storage.
I have a database service like so,
demoApp.SericeFactory.database = function database() {
var database = {
dataStore: null,
admins: [],
students: [],
errors: [],
getadmindata: function(username) {
self = null, that = this
database.dataStore.get('admins', username).done(function(record) {
that.self = record;
return record;
}).fail(function(e) {
console.log(e);
database.errors.push(e);
});
return self; //This does not change.
}
};
database.dataStore = new ydn.db.Storage('DemoApp');
angular.forEach(INITSTUDENTS, function(student) {
database.dataStore.put('students', student, student.matricno);
database.students.push(student);
});
angular.forEach(INITADMINS, function(admin) {
database.dataStore.put('admins', admin, admin.username);
database.admins.push(admin);
});
return database;
I also have a controller that attempts to use the database;
function AppCntl ($scope, database) {
var user = database.getadmindata('user'); //I get nothing here.
}
What I have tried,
I have tried making changing self to var self
I have tried splitting the function like so
rq = database.dataStore.get('admins', 'user');
rq.done(function(record), {
self = record;
alert(self.name) //Works.
});
alert(self) //Doesn't work.
I have gone through questions like this o StackOverflow but nothings seems to be working for me or maybe I have just been looking in the wrong place.
Database request are asynchronous and hence it executes later after end of execution of the codes.
So when the last alert execute, self is still undefined. Secound alert execute after db request completion and it is usual right design pattern.
EDIT:
I have success with following code:
// Database service
angular.module('myApp.services', [])
.factory('database', function() {
return new ydn.db.Storage('feature-matrix', schema);
}
});
// controller using database service
angular.module('myApp.controllers', [])
.controller('HomeCtrl', ['$scope', 'utils', 'database', function($scope, utils, db) {
var index_name = 'platform, browser';
var key_range = null;
var limit = 200;
var offset = 0;
var reverse = false;
var unique = true;
db.keys('ydn-db-meta', index_name, key_range, limit, offset, reverse, unique)
.then(function(keys) {
var req = db.values('ydn-db', keys);
req.then(function(json) {
$scope.results = utils.processResult(json);
$scope.$apply();
}, function(e) {
throw e;
}, this);
});
}])
Complete app is available at https://github.com/yathit/feature-matrix
Running demo app is here: http://dev.yathit.com/demo/feature-matrix/index.html