Meteor reactive variable timing - animation

So I'm in a bit over my head, even after as much research as I can I'm not sure that my question will even make that much sense.
I'm making a card game with Meteor and have a deck that is collected, shuffled, and dealt into the players hand arrays, then the database is updated. Then, in order to give animation to each card so that it looks smooth, each card is given a 'top' and 'left' property using template helpers modifying the CSS (as opposed to using templates to make things just appear and disappear).
My issue is - When I call the 'deal' function, only about half the cards go where they are supposed to. HOWEVER, if I refresh the page everything is right where it should be.
So the variables are correct, but the reactive-ness isn't quite working right? Like it seems like the reactive variable is triggering too early, before all the parameters are loaded?
Any suggestions for how to either clean up the animation system, or just trigger a manual refresh after all the dealing is done?
Again, I apologize for the uncertainty about everything.
There are 45 cards in the deck all trying to execute at once. The template is set up overall like this:
<template name = 'card'>
<div class = 'card' style = '
top:{{positionTop}}px;
left:{{positionLeft}}px'>
</div>
</template>
With helpers like this:
Template.card.helpers({
'positionLeft':function(){
if ('partner' === this.location){
return this.position*50
} else if ('hand' === this.location){
return this.position*25
}
},
'positionTop':function(){
if ('player' === this.owner){
return 200
} else if ('opponent' === this.owner){
return 400
}
}
});

Related

AJAX and Leaflet: Inspect feature properties before styling/adding to map

I'm using leaflet-ajax to load geoJSON on demand. I want to find the maximum theProperty value so I can use that to scale the feature's fill colors before I add them to the map.
Here's my general approach:
function maxBinPropertyValue(theProperty) {
var theGeoJson = null;
var maxPropertyValue = 0;
var propertyValue = null;
var theGeoJson = new L.GeoJSON.AJAX(binsFileName());
theGeoJson.on('data:loaded', function() {
console.log('The data is loaded');
theGeoJson.eachLayer(function(layer) {
console.log('Looping through the layers');
propertyValue = feature.properties[theProperty];
if (propertyValue > maxPropertyValue) {
maxPropertyValue = propertyValue;
console.log('Max so far: ' + maxPropertyValue);
};
});
});
theGeoJson = null;
console.log('The final maximum value: ' + maxPropertyValue);
return maxPropertyValue;
};
I'm trying to wait for the data:loaded event, then loop through all the features to find the maximum value of theProperty, which is returned to the calling routine.
Except it doesn't work. The first console.log says 'The data is loaded'. The second and third console.logs are never reached, and the fourth and final one reports a value of 0 for maxPropertyValue.
How can I examine all the features in a featureset before styling them, in a way guaranteed to not have asynchronous problems?
PS: I'm pretty sure I can't use onEachFeature: instead of the above approach, because I need to examine every feature's property to determine the maximum value in the set before I can style any of the features.
As for your issue about inspecting your data and retrieving the maximum value, you are indeed facing the classic asynchronous concept of JavaScript.
See How do I return the response from an asynchronous call?
Asynchronism is a problem if not dealt with properly, but an advantage if correctly handled.
To put the concept shortly, you do not manage asynchronism in a "standard" sequential way, but you should rather consider parts of code (callbacks) that are executed at a later time based on events.
Whenever you provide a function as an argument, it is certainly a callback that will be executed at a later time, but very probably much later than the next instructions.
So in your case, your 2nd and 3rd console.log are within a callback, and will be executed once your data is loaded, which will happen much later than your 4th console.log.
As for your next step (styling and adding to map), you actually do not need to perform an extra AJAX call, since you already have all data available in theGeoJson variable. You simply need to refactor / restyle it properly.
It is a good approach to break your problem in small steps indeed.
Good luck!
PS: that being said, ES7 provides async and await functionalities that will emulate a sequential execution for asynchronous functions. But to be able to use those, you need latest browser versions or transpilation, which is probably more work to learn and configure as of today for a beginner than understanding how to work with async JS.
I also had this problem and had to wrap my head around this, so giving an explicit example for solution here;
// make a request with your "url"
var geojsonLayer = new L.GeoJSON.AJAX("url");
// define your functions to interact with data
function thingToDoBeforeLoadingStarts () {
// do stuff
}
function thingToDoForEachFileDownloaded () {
// do stuff
}
function thingToDoAfterAllDownloadEnds () {
// do stuff
}
// attach listeners
geojsonlayer.on("data:loading",thingToDoBeforeLoadingStarts);
geojsonLayer.on("data:progress",thingToDoForEachFileDownloaded)
geojsonLayer.on("data:loaded",thingToDoAfterAllDownloadEnds);

Getting DOM attributes of elements selected in CasperJS

I'm building a proof of concept test with CasperJS and I'm trying to wrap my head around when things are available and when they are not.
I'm a bit fuzzy on why evaluate() is required and why one cannot just get a page and go to town with document.querySelectorAll(), but that ends up giving me a nodeList with a length of zero... even after casper.echoing out the HTML and SEEING plenty of matching selectors.
Right now, I'm trying this:
var tile;
var pid;
casper.waitForSelector('.m-product-tile', function() {
tile = this.evaluate(function() {
return __utils__.findOne('.m-product-tile');
});
pid = this.evaluate(function()
return __utils__.findOne('.m-product-tile').getAttribute('data-pid');
});
//do cools stuff like click() on tile and make sure the URL has the pid in it
});
But this feels wrong as I am not sure __utils__.findOne() will always return the same element.
I'd love to be able to do this instead:
pid = tile.getAttribute('data-pid');
But when I attempt that, I get an error:
uncaughtError: TypeError: 'undefined' is not a function (evaluating 'tile.getAttribute('data-pid')')
Did tile somehow cease to be a DOMNode with a getAttributes method? Using typeof tile, it still thinks it's an object. I echoed out the DOMNode and it has a getAttributes key.
The code in the first example DOES work with getAttribute... so why does tile morph into something that no longer has a getAttributes method?
http://casperjs.readthedocs.org/en/latest/modules/casper.html#evaluate provides a pretty good answer... but still, I'd like to know why, if what the page is passing back, is a DOMNode, why can't I treat it like one?

the .click event not working ONLY for the last button (events defined in a for-loop)

I made a function that sets .click for the number of buttons passed to it.
The function is called when another Jquery detects the number of buttons on the page
...
var n = $(".button").length + 1;
...
set_navig(n);
...
function set_navig(n){
for(i=1 ; i<n ; i++){
var btn = "#pb" + i;
$(document).ready(function(){
$(btn).click(function(){
alert('Working!');
});
});
}
}
I have tried removing or adding buttons on the page - correct (n) is passed to the function, but ALWAYS only the last one doesn't work at all.
ANY IDEAS?
Thnx to EVERYBODY for so much good stuff available here. You where the major source of Jquery knowledge when I started learning it.
Well, I originally thought you should use i<=n, but you pass length + 1 (that's unusual by the way). You could just pass the button array instead of the length. Then you can iterate through each one.
I don't see a specific error with the code other than style, so perhaps you are not getting as many .button objects as you think or perhaps you have a typo in the last ID. You should paste a sample of the html code or just debug it yourself using alert() to see what values are being passed.
For future reference, common loops in many languages are either: for(i=0;i<n;i++){} or for(i=1;i<=n;i++){} I would say passing length + 1 to your function is very poor practice.

How to code against logic that depends on the current time in AngularJS?

I have some code and the result of it depends on the current time. Say,
Shop.prototype.isOpen = function() {
var now = new Date();
var today = now.getDay();
return this.openTime(today) <= now && now <= this.closeTime(today);
};
And then in the view, we display whether a shop is open:
<span ng-show="shop.isOpen()">Open now!</span>
The isOpen method is called once and doesn't get updated after that.
I have lots of complex application logic that depends on the isOpen and similar "time-bound" data.
What are the general approaches to keep the isOpen data fresh and have application logic/view be constantly in sync with that?
I think one solution would be to have an intermediate object whose value gets updated in frequent intervals, but I'm not sure if this is the right approach.
The angular documentation on directives has an example of time being updated.
http://docs.angularjs.org/guide/directive
But basically, have your controller set a $scope (or $rootScope, depending on how you want to access it) property that gets updated via a setTimeout loop.

Bbone render behaviour / toggling

Some extremely simple problem for those experienced with backbone,
but still, an answer here would very helpeful. Not looking for an functional answer, but more about what's really happening on this specific example.
With the following code (some simple add/remove from favorite)
render: function() {
$(this.el).html(this.model.get('name'));
$(this.el).append("<span class='unfav'>remove</span>");
$(this.el).append("<span class='fav'>add</span>");
if( this.model.get("selected") == true ){
$(this.el).addClass("selected");
} // Should we really need to have an 'else' conditions here that removes the clas :( ? sound weird to me.
return this;
}
Full code here http://jsfiddle.net/eHAfY/3/
(thanks to #cymen for the codebase)
After adding an element,
Don't get why the item gets changed when I click on 'Add', and does not when I click on remove : if there is a condition that have effect when true, why it is the class' still here when false ?
Your render method wipes its inner HTML with $(this.el).html(...); which is what render methods are usually expected to do.
With $(this.el).addClass("selected"); you modify the external container only when model.selected is true, and your view has no way to know that it should remove this class when the condition is false.

Resources