Trigger $digest when variable changed by node.js function - javascript

I want to make an application with node-webkit using AngularJS.
No problem so far but when I want Angular to watch over a "legacy variable" by using the method described in this answer I just get an Error: [$rootScope:inprog].
I wrapped my node.js function into $scope.$apply() like this:
$scope.$apply(myNodeModule("test", function(err, res) {
if (err) {return console.log(err)}
myLegacyVar = res;
}));
And watching over the (global) myLegacyVar variable like described in the answer mentioned before:
function () {
return $window.myLegacyVar
}, function(n,o){
console.log("changed ",n);
$scope.data = n;
}
);
All of the code resides within my Angular controller function.
The function is async, so I guess it could have to do with that?
Or is this function inception I have going on in $apply maybe causing my error?
I'm just clueless right now, I have just recently started using AngularJS, hours of googleing haven't gotten my very far.

This answer helped. The safe $apply function there did the trick.
I added that function to my controller and added this snipped to my node.js function:
var scope = angular.element($("#angularDOMElement")).scope();
scope.safeApply(function(){
myLegacyVar = res;
})
I don't know if thats the proper way to do it but its working.

Related

JavaScript SDK return data form nested function

I'm writing Telerik platform hybrid app. I want to write function which returns a number from Everlive cloud. Here's my code.
function getSector(){
var result = 0;
var data = el.data('csector');
data.get()
.then(function (data) {
var obj = JSON.parse(JSON.stringify(data));
//alert(obj.result[0].current);
result = obj.result[0].current;
},
function (error) {
alert(JSON.stringify(error));
});
return result;}
Problem is that I cant't return value. It always return 0. Seems like result can't be changed from nested function? Alert works great. Please help.
Documentation
If you are calling your function from somewhere in JS and other than in your markup directly (data binding), then you may need to implement promises through jQuery, angular or some other library. Here is jQuery's implementation: https://api.jquery.com/promise/
If you are calling the function through data binding then your function should modify a property on your model and then your markup should be bound to that property. Without more information about your project it is hard to answer definitively.

angular for each with closure scope variable

I have the following code
$scope.getBooksByUser = function (user) {
var books = [];
ProductFactory.getBooks.query({ id: user.id }).$promise.then(function (result) {
angular.forEach(result, function(i) {
books.push(i.name);
debugger;
})
});
};
As you can see, I am trying to fill a simple javascript array in angular for each. When this function is executed, an array books is still empty.
I am little confused with this behavior, cause when i try to debug my code i can see that on each iteration value is set in array. Actually chrome debugger shows me that an array books is in closure scope and i guess that is a reason but i still cant find an explanation and how i can fix it.
Actually if i was using $scope.books = [] it would be work fine, but i dont need $scope, but i need just a javascript array.
Thanks in advance.
I guess it is happening because of asynchronous call of AJAX. In your case compiler will not wait for your ajax response. You should do it like
ProductFactory.getBooks.query({ id: user.id }).$promise.success(function (result) {
angular.forEach(result, function(i) {
books.push(i.name);
debugger;
})
});
Hope it helps :-)

How to update $scope immediately when dealing with non angular code?

I have to deal with an non angular library and need to create a comunication between them.
<div id="MoipWidget" data-token="{{personaltoken}}" callback-method-success="successCB" callback-method-error="errorCB"></div>
Every time that the page is loaded, I have to get a token from my server.
$http.post('https://example.org', obj)
.success(function(data){
$scope.personaltoken = data.token;
//Here I call the non angular library and I get and error telling me that the token is undefined.
//If I run the code from inside a $timeout works as I need...
})
.error(function(data){
alert('error');
});
I've also tried to run inside $scope.$apply but I get an error telling that $digest already in progress
The non angularjs library that I have to call is simple is just two lines.
var settings = {}
LibraryCall(settings);
How can I update the model immediately?
I've tried to use $scope.$evalAsync as #Kjell suggested but did not work.
After reading more about $scope, I've found what I needed.
$scope.$applyAsync(function(){
var settings = {}
LibraryCall(settings);
});
$scope.$applyAsync will schedule the invocation of $apply to occur at a later time.
https://docs.angularjs.org/api/ng/type/$rootScope.Scope
I removed the error callback for brevity, don't do it in your code :)
I suppose the code you call is asynchronous, if it's not, you should not have any $scope updating problem (because all angular promises call $apply already)...
This should work:
$http.post('https://example.org', obj).success(function(data){
$scope.personaltoken = data.token;
otherLibrary.doSomething(data.token, function(error, result) {
$scope.changeSomething = 'toHey';
$scope.$apply();
});
});
This shoud also work:
$http.post('https://example.org', obj).success(function(data){
$scope.personaltoken = data.token;
otherLibrary.doSomething(data.token, function(error, result) {
$scope.$apply(function() {
$scope.changeSomething = 'toHey';
});
});
})
This shoud raise the $digest already in progress error, because $http does wrap the promise callback on a $apply call already.
$http.post('https://example.org', obj).success(function(data){
$scope.personaltoken = data.token;
$scope.$apply(function() {
otherLibrary.doSomething(data.token, function(error, result) {
$scope.changeSomething = 'toHey';
});
});
})
Try using either $scope.$evalAsync() or $scope.$applyAsync().
They are made for stuff like this. It will execute the code later in time. Not that different from $timeout, but potentially faster.
$scope.$evalAsync(function(){
var settings = {}
LibraryCall(settings);
})
Edit: Just to quote Ben Nadel on the difference between $timeout and $evalAsync, from this post:
So, in essence, $scope.$evalAsync() combines the best of both worlds:
When it can (which is most of the time), it will evaluate your
expression in the same tick; otherwise, it will evaluate your
expression in a later tick, which is exactly what $timeout() is doing.

Ember Data's store promises don't work in quint tests

Update
A bit of context into some quirks of the illustrative code below. StoreProxy exists as a model, created by the ApplicationRouter, that has a reference to the store. This lets other objects access the store directly (for singletons, tests, etc). Example:
MyApp.StoreProxy = DS.Model.extend();
MyApp.ApplicationRoute = U.Route.extend({
model: function () {
return this.store.createRecord('storeProxy');
}
});
Before the route is executed, StoreProxy doesn't have a store property. After, it does. I can only assume this is because of some ember-data magic.
I very well realize your reaction to this may be "Ugh! No! You're doing it wrong!". Noted. We'll move to do it the right way from here over time. That said, this is where the code is now. So, given that, and given this method for getting a reference to the current store, why doesn't the code below call its accept or rejection handlers?
Original question
I'm writing a qUnit unit test for ember. I'm using fixture data. The findAll call on the store isn't resolving or rejecting the promise.
test('Find all in store', function() {
expect(1);
var findPromise;
findPromise = MyApp.StoreProxy.store.findAll('rule');
findPromise.then(function(result) {
console.log('yes');
ok(true);
}, function(error) {
console.log('no');
});
});
I tried using async tests mentioned in this question:
testing ember fixture data with quint but the resolve and reject are never called, so the test hangs indefinitely.
I've also tried placing Ember.run calls around my code, in case it's a weird run loop thing. But to no avail.
asyncTest('Find all in store', 1, function() {
var findPromise;
Ember.run(function() {
findPromise = MyApp.StoreProxy.store.findAll('rule');
findPromise.then(function(result) {
console.log('yes');
ok(true);
start();
}, function(error) {
console.log('no');
start();
});
});
});
The code I'm testing runs fine when I run the application normally (fixture adapter or no), so it feels like something with the test environment.
Any thoughts on what to try? I'm stumped.
The way that you're writing your asynchronous tests is incorrect. Check out QUnit's page on async testing. Your test should look something like this:
asyncTest('Find all in store', function() {
var findPromise = ...;
findPromise.then(function(result) {
start();
ok(result);
}, function() {
start();
ok(false);
});
});
Specifically:
You put an extra parameter in the asyncTest function, which likely causes the test to not run at all.
You're using Ember.Application.store, which is not how you should access your store (and probably isn't even a valid store). I'm not sure what your context is, but you should be getting your store from elsewhere.
You're putting the start() calls after your assertions when they should be before.

unit testing in javascript: how do you mock? - a (hard for me) example

I just rewrote backbone-mongodb to be really compatible with backbone. The original solution had nice vows for testing, and I would like my code to get tested as well, but simply have no idea how to do it.
Here is an example, I would like to test:
update: function(callback) {
var model = this.model;
this._withCollection(function(err, collection) {
if (err) callback(err);
else {
var attributes = _.clone(model.attributes);
delete attributes['_id'];
collection.update({ _id: new ObjectID(model.id) }, {$set: attributes}, {safe:true, upsert:false}, function(err) {
model.fetch();
callback(null, model.toJSON());
});
}
});
},
This code has nothing special in it. It uses the node-mongodb-native driver, and updates a record in the database. AFAIK, proper testing would mean to at least check that (1) collection.update was called with the given arguments, (2) callback is called when and how it should be, (3) model contains the new data.
With vows I can check (2), but have no idea at all how to check (1). Actually, the same holds for every unit testing framework I know about, qUnit, Jasmine. I'm sure that this can be done somehow, and I'm decided to learn at least one of them, but it's hard to make a choice when you got stuck at the beginning. :)
I know about sinon.js and think that everyting can be tested using mocking all the objects I have until I end up having the collection mocked as well, but this seems to be extremely clumsy. Could someone help me in writing the above tests please, and I'll happy to write out a tutorial of it?
I will use Jasmine for that purpose; I don't how familiar are you using that library, but they have a plugin to use jQuery for writing spec tests, you can load fixtures/templates and run tests on it.
for your particular case, assuming that function is part of MyObj "class", I will write something like:
describe("My object tests", function() {
it("Should update my obj", function () {
var noError, flag = false;
MyObj.update(function (err, model){
flag=true;
noError= err==null;
// or you can do other checks on the result
})
// we wait for 5 sec until get a response (flag==true)
waitsFor(function (){ return flag}, "Timeout on update", 5000);
// check if there are no errors
expect(noError).toBeTruthy();
});
});

Categories