I'm looking for _.after function just to play around with it.
var renderPoin = _.after(data.models.length, function() {
console.log( 'siap' );
require(["ftscroller"], function () {
$('.dedo').css('width', $li_width);
var containerElement, scroller;
containerElement = document.getElementById('poin-nav');
scroller = new FTScroller(containerElement, {
scrollbars: false,
scrollingY: false,
snapping: true,
scrollResponseBoundary: 8,
scrollBoundary: 0
//contentWidth: $li_width
});
});
});
_.each(data.models, function (poin, i) {
db.transaction(function(trans) {
trans.executeSql("insert into piezo_point(id, nama, lat, lng) values(?,?,?,?)", [poin.get('id'), poin.get('nama'), poin.get('lat'), poin.get('lng')]);
}, self.errorCB, function() {
self.viewList = new Poin({
model: poin,
vent: window.vent
});
self.$el.find('#poin-nav ul').append(self.viewList.render().el);
$li_width += parseInt(self.viewList.$el.css('width'));
if ( (i+1) === data.models.length){
renderPoin;
}
});
}, self);
however the renderPoin above not executed as expected. What am I doing wrong?
thanks in advance
I think that it's just a typo.
You're not calling the renderPoin function because you're missing the () brackets in this part of the code:
if ( (i+1) === data.models.length){
renderPoin;
}
update
I'm not even going to argue that your code is not even calling the desired function.
Take a look at the official docs:
(_.after)... Creates a version of the function that will only be run after first being called count times.
If you don't believe the docs either, try this running this simple test in your browser console:
var f = _.after(1, function(){});
alert(typeof f); // and be amazed
update #2
The whole purpose of the after function is to leave your callback triggering logic behind and just call the function on every loop. The _.after function decides when to actually call the function or not, so you might as well lose the if ( (i+1) === data.models.length){ and replace it with just the function call:
_.each(data.models, function (poin, i) {
db.transaction(function(trans) {
trans.executeSql("insert into piezo_point(id, nama, lat, lng) values(?,?,?,?)", [poin.get('id'), poin.get('nama'), poin.get('lat'), poin.get('lng')]);
}, self.errorCB, function() {
self.viewList = new Poin({
model: poin,
vent: window.vent
});
self.$el.find('#poin-nav ul').append(self.viewList.render().el);
$li_width += parseInt(self.viewList.$el.css('width'));
// just call it!
renderPoin();
});
}, self);
Related
If I create a function constructor in JavaScript like so:
function Emitter() {
this.events={}
}
and then I add a method to its prototype like so:
Emitter.prototype.on = function(type,listener) {
this.event[type] = this.events[type] | [];
this.event[type].push(listener);
}
When I call the method 'on' an instance of Emitter twice, why does it not just overwrite the original property called greet and assign it the second function? I guess I do not understand the stricture of what is happening in:
this.event[type] = this.events[type] | [];
var emtr = new Emitter();
emtr.on('greet',function(){
console.log('Hello once');
});
emtr.on('greet', function(){
console.log('Hello twice');
});
You should be using || for OR instead of |, this is invalid:
this.event[type] = this.events[type] | [];
Also, you are calling event instead of events. You should have:
this.events[type] = this.events[type] || [];
This way if this.events[type] is not undefined it will stay as is. However if it is undefined is will be assigned as an empty array: []
The code below will successfully add the two functions to emtr.events['greet'] (an array of functions):
function Emitter() {
this.events = {}
}
Emitter.prototype.on = function(type, listener) {
this.events[type] = this.events[type] || [];
this.events[type].push(listener);
}
var emtr = new Emitter();
emtr.on('greet', function() {
console.log('Hello once');
});
emtr.on('greet', function(){
console.log('Hello twice');
});
console.log(emtr.events['greet'])
So you can call them like so:
emtr.events['greet'][0]();
And
emtr.events['greet'][1]()
If instead, you would like to replace the listener then you shouldn't be using an array. Pointing to a function instead of an array of functions will suffice:
function Emitter() {
this.events = {}
}
Emitter.prototype.on = function(type, listener) {
this.events[type] = listener;
}
var emtr = new Emitter();
emtr.on('greet', function() {
console.log('Hello once');
});
emtr.on('greet', function(){
console.log('Hello twice');
});
emtr.events['greet'](); // notice how the first listener was replaced by the new one
This way you can call your listener with emtr.events['greet']().
While #Ivan is completely correct, he missed your main question of “why?”
The answer is that the logical OR operator in JS also functions as a null coalescing operator when used as part of an assignment statement.
Essentially, in the case
let x = y || “default”;
The right hand side will evaluate to the first “truthy” value and return that. In JS most things evaluate true in a logical operation except values like false, 0, null, and a few others not relevant to this question.
So in your syntax (as corrected by Ivan), you’re telling JS to assign to the events[type] property the first thing that’s true. Either itself (which will evaluate true if it’s not null, unassigned, 0, etc) or else an empty array.
The first time you add an event, it’ll be unassigned and therefore get an empty array added. Subsequently, arrays eval as true, so you’ll just keep re-assigning the property to itself which has no effect.
Make sense?
function Emitter() {
this.events = []
}
Emitter.prototype.on = function (type, listener) {
this.events[type] = this.events[type]||[]
this.events[type].push(listener);
}
Emitter.prototype.emit = function(type){
var listener = this.events[type].pop()
if(this.events[type].length>=0)
listener()
else
console.log('Nothing to emit')
}
var emtr = new Emitter();
emtr.on('greet',function(){
console.log('Hello once');
});
emtr.emit('greet')
This My Mark-up code
<button id="btnUploadFiles" class="col-lg-offset-1 col-lg-2 col-md-6 col-sm-4 btn primaryButton" data-ng-click="uploadFilesToPdm()">
<span class=" btn-circle"></span>
Upload
</button>
My controller code
$scope.uploadFilesToPdm = function () {
if (validateLoad()){do something further...}
}
function validateLoad() {
selectedItems = {
EnvironmentId: 52,
ApplicationId: 23,
UserId: ""
}
var data = fileUploadService.validateLoad.save({}, selectedItems);
data.$promise.then(function (response) {
if (!response.isValid) {
alert(warning.messageMandatoryFileLoad + response.requiredFileType);
}
return response.isValid;
}, function (error) {
});
}
My WebApi method returning true and is working fine.
and response is also gets caught in ValidateLoad() method.
However in uploadFilesToPdm if condition is not able to evaluate and function wont proceed to "do further something...". Please suggest, if am missing something.
You can handle it by returning the promise:
$scope.uploadFilesToPdm = function() {
validateLoad().then(function(response) {
if (!response.isValid) {
return alert(warning.messageMandatoryFileLoad + response.requiredFileType);
}
//do something further..
}, function(error) {});
}
function validateLoad() {
selectedItems = {
EnvironmentId: 52,
ApplicationId: 23,
UserId: ""
}
return fileUploadService.validateLoad.save({}, selectedItems).$promise;
}
Your validateLoad() function doesn't return anything, which means it effectively returns underfined, which is falsy.
You need to return a boolean from that function. But that's impossible, because you'll only know the boolean value long after the function has returned. The boolean will only be known once the file upload has complete. So what you can return is a promise of boolean. But then you can't check if the promise is true or false. You can only check if it's resolved as true or as false:
$scope.uploadFilesToPdm = function () {
validateLoad().then(function(valid) {
if (valid) {do something further...}
});
};
function validateLoad() {
selectedItems = {
EnvironmentId: 52,
ApplicationId: 23,
UserId: ""
}
var data = fileUploadService.validateLoad.save({}, selectedItems);
return data.$promise.then(function (response) {
if (!response.isValid) {
alert(warning.messageMandatoryFileLoad + response.requiredFileType);
}
return response.isValid;
});
}
Read the blog post I wrote about promises and how to avoid its traps.
As Lucas said in his comment: You need to "wait" for the async call to finish. The easiest way to achieve that in your case is to return the promise from the validateLoad() function like TJ's answer is showing it.
There's another way of doing it by using $q which is an antipattern but sometimes used for code separation.
function validateLoad() {
q = $q.defered();
selectedItems = {
EnvironmentId: 52,
ApplicationId: 23,
UserId: ""
}
var data = fileUploadService.validateLoad.save({}, selectedItems);
return data.$promise.then(function (response) {
if (!response.isValid) {
alert(warning.messageMandatoryFileLoad + response.requiredFileType);
q.reject(false);
}
q.resolve(response.isValid);
});
return q.promise;
}
In this case you create another promise that you return and then use in your $scope.uploadFilesToPdm function.
I'm trying to create a nightwatchJS custom assertion that will check if a file exists. The assertion appears to fire, but nightwatch exits as soon as the assertion command finishes.
I guess I'm not returning control to the nightwatch api, but if thats the case then how can I achieve that?
// filename = doesFileExist.js
exports.assertion = function(fname, msg) {
var fs = require('fs');
this.message = msg + 'FileExists: ' + fname ;
this.expected = true;
this.pass = function(value) {
return value == this.expected;
} ;
this.value = function(result) {
return result;
};
this.command = function(callback) {
return fs.exists(fname, callback);
};
};
and the test case (using nightwatch.json as an example) is ;
this.checkForFile = function() {
browser
.verify.doesFileExist('nightwatch.json', 'test1')
return browser;
};
I know this is an old question, but I found in writing my own custom assertions that you need to return 'this' in your command function. The callback is what sends your value to this.value which is used in the pass function.
So it would look like this
this.command = function(callback) {
fs.exists(fname, callback);
return this;
};
I needed to add an api.execute around my call to make the function halt.
this.command = function (callback) {
var someValue = evaluateSomeValueHere;
return this.api.execute(
function (someValue) {
return someValue;
}, [someValue],
function (result) {
callback(result.value);
}
)
};
In my Service i have the vars i want to display and the getters for it:
var docsLoaded = 0;
var docsToLoad = null;
pouchService.getDocsLoaded = function () {
return docsLoaded;
};
pouchService.getDocsToLoad = function () {
return docsToLoad;
};
While the service is syncing, i want to count the synced docs
pouchService.syncNow = function () {
var foundLastSeq = false;
docsLoaded = 0;
docsToLoad = null;
remoteDB.info().then(function (remoteInfo) {
function findOutDiff(localPosition) {
docsToLoad = (remoteInfo.update_seq - localPosition) + 1;
console.log("docs to load: " + docsToLoad);
}
// start Sync progress
sync = localDB.sync(remoteDB, {live: false})
.on('change', function (info) {
console.log('AI change: ');
console.log(info);
if (info.direction === 'pull') {
if (foundLastSeq === false) {
foundLastSeq = true;
findOutDiff(info.change.last_seq);
}
}
console.log(docsLoaded + " from " + docsToLoad);
docsLoaded++;
})
In my HTML i want to display the progress like this:
{{pouchService.getDocsLoaded()}} from {{pouchService.getDocsToLoad()}}
Now i get sometimes a value from getDocsLoaded, but mostly its zero. When I cancel the Syncprogress i get the value where it's stopped.
So i get the value before it really starts and when it's over, but i want it during the sync progress. (on the console my my progressinfos are working as expected)
Any ideas?
The problem is in applying scope. Jim wrote a nice article about this problem:
jimhoskins.com/2012/12/17/angularjs-and-apply.html
Solved it:
$rootScope.$apply(function () {
docsLoaded++;
});
I'm having trouble decorate the objects in my list returned by $asArray in angularfire with a new method (not decorating the array itself).
The angularfire documentation seems to suggest that the right way to do this is to override the $$added method in the factory for $FirebaseArray, returning a new object that either encapsulates or extends the snapshot that gets passed in to that method. From the documentation:
// an object to return in our JokeFactory
app.factory("Joke", function($firebaseUtils) {
function Joke(snapshot) {
this.$id = snapshot.name();
this.update(snapshot);
}
Joke.prototype = {
update: function(snapshot) {
// apply changes to this.data instead of directly on `this`
this.data = snapshot.val();
},
makeJoke: function() {
alert("Why did the " + this.animal + " cross the " + this.obstacle + "?");
},
toJSON: function() {
// since we didn't store our data directly on `this`, we need to return
// it in parsed format. We can use the util function to remove $ variables
// and get it ready to ship
return $firebaseUtils.toJSON(this.data);
}
};
return Joke;
});
app.factory("JokeFactory", function($FirebaseArray, Joke) {
return $FirebaseArray.$extendFactory({
// change the added behavior to return Joke objects
$$added: function(snap) {
return new Joke(snap);
},
// override the update behavior to call Joke.update()
$$updated: function(snap) {
this.$getRecord(snap.name()).update(snap);
}
});
});
However, when I do this in my code, nothing ever gets added to the array, although I can see from outputting to the console that it is getting called.
var printMessageObjConstructor = function(snap) {
this.$id = snap.name();
this.snapshot = snap;
this.$update = function(snap) {
this.snapshot = snap;
};
this.printMessage = function() {
return this.author + "'s question is: " + this.body;
};
};
var ref = new Firebase("https://danculley-test.firebaseio.com/questions");
//What Am I Doing Wrong Here?
var arrayFactory = $FirebaseArray.$extendFactory({
$$added: function(snap, prevChild) {
var x = new printMessageObjConstructor(snap);
console.log("I am being called from FirebaseDecoratedCtlOverloadAddedinNewObj.");
return x;
},
$createObject: function(snap) {
return new printMessageObjConstructor(snap);
},
$$updated: function(snap) {
var i = this.$indexFor(snap.name());
var q = this.$list[i];
q.$update(snap);
}
});
var sync = $firebase(ref, {arrayFactory:arrayFactory});
var list = sync.$asArray();
list.$loaded(function(list) {
$scope.questions = list;
});
I've set up a new plunk stripped down to show the issue with a couple other use cases that I've tried. (The actual method I'm adding is more complex and isn't related to the view, but I wanted to do something simple to reproduce the issue.)
I think the issue is that I don't quite understand what exactly $$added is supposed to return, or what additional behavior beside returning the value to be stored $$added is supposed to have. There also doesn't really seem to be an $$added on the prototype or on $FirebaseArray to call as a super to get the default behavior. Can someone point me in the right direction?
UPDATE
For the benefit of others, after reviewing the like that Kato posted, I was able to solve the issue by adding the following, almost all copied directly from the source except for the commented line below.
$$added: function(snap, prevChild) {
var i = this.$indexFor(snap.name());
if( i === -1 ) {
var rec = snap.val();
if( !angular.isObject(rec) ) {
rec = { $value: rec };
}
rec.$id = snap.name();
rec.$priority = snap.getPriority();
$firebaseUtils.applyDefaults(rec, this.$$defaults);
//This is the line that I added to what I copied from the source
angular.extend(rec, printMessageObj);
this._process('child_added', rec, prevChild);
}
}
For the benefit of others, after reviewing the link that Kato posted, I was able to solve the issue by adding the following, almost all copied directly from the source except for the commented line below.
$$added: function(snap, prevChild) {
var i = this.$indexFor(snap.name());
if( i === -1 ) {
var rec = snap.val();
if( !angular.isObject(rec) ) {
rec = { $value: rec };
}
rec.$id = snap.name();
rec.$priority = snap.getPriority();
$firebaseUtils.applyDefaults(rec, this.$$defaults);
//This is the line that I added to what I copied from the source
angular.extend(rec, printMessageObj);
this._process('child_added', rec, prevChild);
}
}