AngularJS: Retrieve mysql data in electron and publish it to AngularJS scope - angularjs

I'm trying to retrieve a list of data from mysql database by using electron and bind it to a list in the controllers scope. I'm using mysql2. Here is my controller:
$scope.carList = [];
mysql.execute("SELECT * FROM cars").spread(function(results){
$scope.carList = results;
console.log(results);
})
I do get the results back, but the in the view carList remains empty. How can I solve this problem?
I just added a button to my view and bound it to a check function like this:
$scope.check = function(){
console.log($scope.carList);
}
After I click on the button, my list in the views gets populated. Now my question would be how can I have my list populated on the start of the controller rather than wait for an event ro make it happen?

I think mysql.execute("").spread(fn) promise is not a part of the AngularJS digest cycle. You did not provide enough code to fully reproduce your problem but I think by triggering a new digest cycle it should work for you. E.g. try it with $timeout which triggers a new digest cycle.
$scope.carList = [];
mysql.execute("SELECT * FROM cars").spread(function(results){
$timeout(function () {
$scope.carList = results;
});
})
I would prefer to create a AngularJS service which handles your electron mysql in a nice way. You could globally apply your $scopes in it, right after finishing your mysql procedures which are not a part of your digest cycle.
Approach by using AngularJS promises
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function($scope, $q) {
$scope.carList = [];
getCars.then(function(cars) {
$scope.carList = cars;
});
function getCars() {
var deferred = $q.defer();
mysql.execute("SELECT * FROM cars").spread(function(results) {
deferred.resolve(results);
});
return deferred.promise;
}
});

Related

AngularJs, master-details update master when details change

I have an AngularJs app with a master details that I have changed to use
ui-router.
Previously, I had a SelectionService with a plain JavaScript observer that was
used by the master to notify selection and by the details to update himself. The
selection was just a technical identifier, so that the DetailsController has
to get the item from my BackendService which is basically a local cache.
Now with ui-router when an item is selected into the master, I go the details
state and the same flow remains (use the technical id to get details from backend).
My problem is that into the previous version all updates made on the details
where automagically updated on the master. But that is broken with the ui-router
version, probably because the MasterController and DetailsController don't
share the same scope.
So, my question is : How do you ensure that a list of items is updated when one
item is changed. Do you rely on some AngularJs functionalities (then how) or do
you use a classic events mechanism with $scope.$broadcast and $scope.$on ?
Edit, after more investigations
I have read some articles that are clearly against the usage of AngularJs events ($scope.$broadcast, $scope.$emit and $scope.$on) and recommand a custom event bus/aggregator.
But I would like to avoid that and thus rely on the AngularJs digest lifecycle. However what is suggest by #Kashif ali below is what I have but my master is not updated when the details changes.
angular
.module('masterDetails')
.service('BackendService', function($q){
var cache = undefined;
var Service = {}
Service.GetImages = function() {
var deferred = $q.defer();
var promise = deferred.promise;
if ( !cache ) {
// HTTP GET from server .then(function(response){
cache = JSON.parse(response.data);
deferred.resolve(cache);
});
} else {
deferred.resolve(cache);
}
return promise;
}
Service.GetImage = function(key) {
return GetImages().then(function(images){
return images[key];
});
}
Service.SetImage = function(key, updated) {
GetImages().then(function(images){
images[key] = updated;
// TODO; HTTP PUT to server
});
}
})
.controller('MasterController', function(BackendService){
var vm = this;
vm.items = [];
vm.onSelect = onSelect;
init();
function init() {
BackendService.GetImages().then(function(images){
// images is a map of { key:String -> Image:Object }
vm.items = Object.keys(images).map(function(key){
return images[key];
});
});
}
function onSelect(image) {
$state.go('state.to.details', {key: image.key});
}
})
.controller('DetailsController', function(BackendService, $stateParams){
var vm = this;
vm.image = undefined;
init();
function init() {
BackendService.GetImage($stateParams.key).then(function(image){
vm.image = image;
}).then(function(){
// required to trigger update on the BackendService
$scope.$watch('[vm.image.title, vm.image.tags]', function(newVal, oldVal){
BackendService.SetImage(vm.image.key, vm.image);
}, true);
});
}
});
Edit, this is due to the states
So, when I open the app on #/images the images state start. Then I select one image to go to the images.edit state and everything works well, the master is updated when details changes.
However if I start on #/images/:key which is the images.edit state, then the master ignore all changes mades on the master.
You can rely on both the solution you have mentioned
1.You can achieve this using factories in angularjs
Factories/services are the singleton objects that is shared along the the app:
example:
angular.module("app",[]).factory('myfactory',function(){
var data;
{
getData:getData,
setData:setData
};
function setData(data1)
{
data=data1;
}
function getData()
{
return data;
}
}
).controller('myclrl1',function($scope,myfactory){
}).controller('myclrl2',function($scope,myfactory){
});
you can inject these controller in different views and can access singleton factory(all controller will share the same object) "myfactory" in both controller using getter and setter
you can use $scope.$broadcast and $scope.$on to make nested contollers to communicate with each other
you can find the detailed Example over here.
$scope.$broadcast and $scope.$on
hope that would be helpful
Regards

Populate an array and then add a $watch to refresh list view angularjs

I have a controller that looks something like this:
(function () {
"use strict";
angular.module("app")
.controller("postsController", postsController);
function postsController($scope, $http, $uibModal, PostService) {
var vm = this;
$scope.posts = [];
PostService.getPosts()
.then(function (p) {
$scope.posts = p.data;
}, function (error) {
vm.errorMessage = "Failed to load data: " + error;
});
$scope.$watchCollection('posts', function(newPosts, oldPosts) {
PostService.getPosts();
});
}
})();
The original data is pulled from the service and the posts array is populated properly. However, I am have trouble having the view refreshed if data in the array has changed. Either nothing happens on the view, or I am seeing an infinite loop with the $watch.
Any help is greatly appreciated.
Why get infinite loop:
$scope.$watchCollection('posts',function(){...}) watched your post array, when you refresh your view and post changed, watch listener triggered.
Then listener do PostService.getPosts();, resolve with $scope.posts = p.data;, this also change the post ( more: post been set back to data from backend, that's why nothing changed in your view), then watch listener triggered again. Then do PostService.getPosts(); again, then again and again.
So you get a infinite loop.

angular display model on view after getting data from firebase

I am working on displaying collection that I got from DB in angular with firebase DB. I have those controller and service setup. in the html, I use search.users expecting it will hold all the data that I got from the DB but it won't show up. I can't figure out why. I tried few things like angular.copy or $broadcast with no luck. Can anyone help advise on this? Appreciated in advance.
.controller('SearchController', function ($scope, SearchService, logout, $location){
var search = this;
search.users = SearchService.users;
//$scope.$on('evtputUsers', function () {
// search.users = SearchService.users;
//});
})
//service for SearchService
.factory('SearchService', function ($http, $rootScope){
var userRef = new Firebase("app url");
var broadcastUsers = function () {
$rootScope.$broadcast('evtputUsers');
};
//get the user info
//insert the data to the db.
//retrieving the data
var dbUsers;
userRef.child('users').on('value', function(snapshot){
dbUsers = snapshot.val();
// angular.copy(snapshot.val(), dbUsers);
console.log('usersinDB:',dbUsers);
broadcastUsers();
}, function(err){
console.error('an error occured>>>', err);
});
return {
users: dbUsers
};
})
Rather than using $broadcast() and $on() you should use the AngularFire module.
AngularFire provides you with a set of bindings to synchronizing data in Angular.
angular.module('app', ['firebase']) // 1
.controller('SearchCtrl', SearchCtrl);
function SearchCtrl($scope, $firebaseArray) {
var userRef = new Firebase("app url")
$scope.users = $firebaseArray(userRef); // 2
console.log($scope.users.length); // 3
}
There are three important things to take note of:
You need to include AngularFire as firebase in the dependency array.
The $firebaseArray() function will automagically synchronize your user ref data into an array. When the array is updated remotely it will trigger the $digest() loop for you and keep the page refreshed.
This array is asynchronous. It won't log anything until data has populated it. So if you're logs don't show anything initially, this is because the data is still downloading over the network.

Why is angular $apply required here?

Please consider the following angularjs code for a controller:
(function (app) {
var controller = function ($scope, $state, datacontext) {
$scope.$parent.manageTitle = "Account Management";
$scope.accounts = [];
var init = function () {
getRecords();
};
var getRecords = function () {
return datacontext.getAccounts().then(function (data) {
$scope.$apply(function () {
$scope.accounts = data;
});
});
};
init();
};
app.controller("accountsCtrl", ["$scope", "$state", "datacontext", controller]);
})(angular.module("app"));
Removing the $scope.$apply wrapper and leaving just the "$scope.accounts = data" in the getRecords method breaks the code. The data is retrieved but the ng-repeat directive in the html is not automatically updated. I'm trying to get my arms around the entire $apply/$digest model, but it sure seems to be that the $apply should NOT be required in this case.
Am I doing something wrong?
Thanks.
<------------------------------------------ EDIT ---------------------------------------->
Ok, thanks for the responses. Here is the datacontext. It uses Breeze. I still can't figure out what the problem is - - I just don't see why $apply is required in the code, above.
(function (app) {
var datacontext = function () {
'use strict';
breeze.config.initializeAdapterInstance('modelLibrary', 'backingStore', true);
breeze.config.initializeAdapterInstance("ajax", "angular", true);
breeze.NamingConvention.camelCase.setAsDefault();
var service;
var manager = new breeze.EntityManager('api/ProximityApi');
var entityQuery = breeze.EntityQuery;
var queryFailed = function (error) {
};
var querySuccess = function (data) {
return data.results;
};
var getAccounts = function () {
var orderBy = 'accountName';
return entityQuery.from('Accounts')
.select('id, accountName')
.orderBy(orderBy)
.using(manager)
.execute()
.then(querySuccess, queryFailed);
};
service = {
getAccounts: getAccounts
};
return service;
};
app.factory('datacontext', [datacontext]);
})(angular.module('app'));
Thanks again!
Thanks for your answers. Jared - you're right on the money. By default, Breeze does not use angular $q promises, but uses third-party Q.js promises instead. Therefore, I needed $apply to synchronize the VM to the view. Recently however, the Breeze folks created angular.breeze.js, which allows the Breeze code to use angular promises, instead. By including the angular.breeze module in the application, all Breeze code will use native angular promises and $http instead.
This solved my problem and I could remove the $apply call.
See: http://www.breezejs.com/documentation/breeze-angular-service
The reason that you need to use the $apply function is the result of using Breeze to to return the data. the $apply function is used to get angular to run a digest on all the internal watches and update the scope accordingly. This is not needed when all changes occur in the angular scope as it does this digest automatically. In your code, because you are using Breeze the changes are taking place outside the angular scope, thus you will need to get angular to manually run the digest, and this is true for anything that takes place out side of angular (jQuery, other frameworks ect...). It is true that Breeze is using promises to update the data, however Angular does not know how to handle the changes after the promise returns because it is out side the scope. If you were using an angular service with promises then the view would be updated automatically. If your code is working correctly as is then it would be the correct way to use $apply in this way.
The only thing I might suggest is to change the way you are calling the apply to make sure that it will only run if another digest is not currently in progress as this can cause digest errors. I suggest you call the function as such:
if(!$scope.$$phase){$scope.$apply(function () {
$scope.accounts = data;
});
Or the other option would be to write a custom wrapper around the $apply function like this SafeApply

Data holding and watching in service to update view in AngularJS

I use AngularJS on the client side to display some informations that'll get pushed through a NodeJS-Server. The data will be held in a service factory.
I want to watch this data through the angular watch function.
The problem i run into, is the following. watch runs through the factory only once and stops.
And if the server pushes changed data it won't update the view?
Here is some code snippet.
.factory('clientStorage', ['socket', function (socket){
var that = {};
//basis data
that.basisData = {};
that.getBasisData = function () {
socket.on('sendBasis', function (basis) {
console.log('ich hoffe doch');
that.basisData = basis;
console.log(that.basisData);
});
console.log(that.basisData);
return that.basisData;
};
return that;
}]);
I don't know why this happens?
Anybody there who may help?
Greetings
Henrik
Try this in your controller:
$scope.$watch( function() { return clientStorage.basisData; }, function(clientData) {
$scope.data = clientData;
});
Where $scope.data is the data you are trying to update.

Resources