I am having troubles with some parts of my code randomly.
This object is declared in a angular controller.
this.tData = {
'questions':[],
'typeQuestion':[],
'category':[],
'dName':this.dName,
'tCodigo':this.tCodigo}
Then I got some data from others functions and push it into respective fields,
this.tData.questions.push(this.idQuestion) // this come from frontend ng-model
this.tData.typeQuestion.push(this.typeQuest) // this come from frontend ng-model
this.tData.category.push(this.idCategory)// this come from frontend ng-model
This construct my object fine. Doing console.log(this.tData) show me the object completely fine. But then when I pass it to the backend in this function of the angular service.
this.updateStuff = function(codStuff,tData){
return $http.put('/updateStuff' + codStuff,tData)}
The object that backend get doing console.log(params) is
{
questions:['exampleId'],
typeQuestion:['exampleData'],
category:[], // HERE IS THE PROBLEM
dName:'exampleName',
tCodigo:'exampleCod'}
Like you see category:[] is empty but doing console.log(tData) in the service of angular before I send it I see the correct data there.
I miss data when I send it to the backend. This problem happend to me in 3 others cases like this.
Why some arrays are ok in backend and why others are not?
I tried a lot of things but ever 1 item of the object I send to the backend go empty.
If you need more specific code tell me in comments.
Updates
Code here I push category in the controller:
this.getCategoryByName = function(){
this.bName = document.getElementById('seCategory').value;
Category.getCategoryByName(this.bName).then((result)=>{
this.idCategory = result.data.data._id; // this give me id of category
this.tData.category.push(this.idCategory);
})
}
2
This is where I call in frontend my functions:
<button class="btn btn-primary" ng-click="ctController.getCategoryByName(); ctController.updateTest();" > up </button>
This is the code of updateTest() function:
this.updateTest = function(){
Test.updateTest(this.codTest,this.tData).then(result=>{})
}
Above method call the angular service updateStuff
SOLVED
Solved adding a chain promise in the method getCategoryByName and adding the updateTest() method nested in getCategoryByName() method more or less like #T.J. Crowder sugest so I give it the response.
Code here I push category in the controller:
this.getCategoryByName = function(){
this.bName = document.getElementById('seCategory').value;
Category.getCategoryByName(this.bName).then((result)=>{
this.idCategory = result.data.data._id; // this give me id of category
this.tData.category.push(this.idCategory);
})
}
That tells us that you're calling updateStuff before Category.getCategoryByName has finished its work, and so before this.tData.category.push is called. The reason console.log seems to show you things in this.tData.category is (as I mentioned in a comment) because of deferred evaluation in the console.
This also explains why it happens sometimes: You have a race between that Category.getCategoryByName operation and the operation calling updateStuff. Sometimes, Category.getCategoryByName wins and so updateStuff includes the pushed information, other times the code calling updateStuff wins and so updateStuff doesn't have the information in this.tDate.category (yet).
this.getCategoryByName should return the promise chain:
this.getCategoryByName = function(){
this.bName = document.getElementById('seCategory').value;
return Category.getCategoryByName(this.bName).then((result)=>{
// ^^^^^^
this.idCategory = result.data.data._id; // this give me id of category
this.tData.category.push(this.idCategory);
});
};
...and then you should make whatever is calling updateStuff dependent on the resolution of that promise.
(You'll also want to ensure that something handles the chain's rejection path. Your current getCategoryByName ignores errors, which will lead to "Unhandled rejection" errors in the console if Category.getCategoryByName fails.)
Related
I'm working in Angular (angularfire) trying to combine two firebaseArrays into a single object, but the asynchronous nature of the return calls is making it difficult. I would like to do something simple like:
$scope.itemStats = $firebaseStorage.getArray('item-stats');
$scope.stats = $firebaseStorage.getArray('stats');
$scope.newArray = $scope.itemStats.concat($scope.stats);
The code for the getArray function is:
getArray: function(key) {
var dbRef = ref.child(key);
return $firebaseArray(dbRef);
}
It's possible I'm querying the data wrong in the first place, and could grab both at the same time which would also solve my problem (aka, I'm asking the wrong question).
Which approach should I use, and how do I solve this?
You can use the $loaded which will return a promise resolved when loading the array is done.
An example from the documentation
var list = $firebaseArray(ref);
list.$loaded()
.then(function(x) {
// now "list" is loaded
})
Problem: I'm trying to get some data from a server to populate a cytoscape graph. When i access the scope from javascript its undefined.
Despite their being quite a few posts with this / similar issue. I'm having trouble finding an explanation about how to get round the problem of accessing the scope from a javascript file, once the data is ready (if that is the problem im having).
I read that http.get methods are asynchronous? so they don't block anything after from executing. I guess this is what is causing an undefined error when i'm accessing the scope because the data hasn't returned yet? I've found some info on this but can't figure out what to use to get round the problem. I can access the data in the usual way in HTML with the angular curly braces and it works. It also works when i just return some data from the controller.
If anyone can point me in the right direction i'd really appreciate it.
This is javascript file im trying to run which grabs the scope and adds nodes to a cytoscape graph.
<script type="text/javascript">
window.onload = function() {
nodes = angular.element(document.querySelector('[ng-controller="DataController"]')).scope().nodes;
alert(nodes);
cy.add(nodes);
}
</script>
2. which calls a factory method from the controller.
dataFactory.getNodes().then(function(data)
{
$scope.nodes = data;
});
3. http.get from the factory
_service.getNodes = function()
{
return $http.get(urlBase + "/nodes");
};
4. node.js returns some 'nodes' to add to the cytoscape graph
router.get('/api/data/nodes', function(req, res) {
console.log('sending some data');
var data = [
...
];
res.status(200).json(data);
});
I've seen "promises" mentioned so i guess i'll be heading in that direction next...
Thanks
You are trying to alert the value of nodes when the DOM is loaded (window.onload) and the data from the service is not yet returned.
Your controller is already uses a promise, and the data is binded to the scope when the promise is resolved.
.then(function(data){
...
});
If you specifically want to access the data from an external js script, you can simply call the function from the controller:
script
<script type="text/javascript">
function alertNodes() {
nodes = angular.element(document.querySelector('[ng-controller="DataController"]')).scope().nodes;
alert(nodes);
cy.add(nodes);
}
</script>
controller
dataFactory.getNodes().then(function(data){
//$scope.nodes = data;
alertNodes()
});
Struggling with Backbone..
I am amending an opensource project and have the following code
var tt = new Request();
tt.set("url", base);
tt.set("method","OPTIONS");
tt.send("text","display");
var resp = tt.get("response");
var text = resp.get("text");
console.log(resp);
console.log("text",text.length);
Use the chrome developer tools I can see "resp" model in detail so that looks good.
If I try and get the value of the "text" item from the model it returns an empty string, yet in the console it clearly has some text in it.
What am I doing wrong?
Make sure you try to console.log "text" in the success callback of the request. I am assuming that the request class would make an async call to some service and provide a way for you to hook into the success or error cases.
HTH!
Hi fellow Meteor friends!
Please note: I am using Tom's router!
So I'm trying to only display my template when the mongo collection is ready but for some reason it does not work! :(
I first followed this post: LINK
So I have my publish functions in the server.js and I subscribe to these functions inside my router, so no Deps.autorun() involved here (btw: is this the right approach? Deps.autorun() did not work for me properly):
So I have something like:
'/myroute': function(bar) {
Meteor.subscribe("myCollection", bar, function() {
Session.set('stuffLoaded', true);
});
return 'stuffPage';
}
In the template, where the data loaded from "myCollection" is displayed, I will have something like this:
<template name="stuffPage">
{{#if stuffLoaded}}
<!-- Show the stuff from the collection -->
{{else}}
<p>loading!</p>
{{/if}}
</template>
For some reason "loading!" is never displayed.
Also, for a couple of milliseconds, the "old data" from the last time the same template was displayed (but with another "bar" value provided to the publish function --> different data) is displayed.
This of course is not good at all because for a couple of ms the user can see the old data and suddenly the new data appears.
To avoid this "flash" I want to display "loading!" until the new data is loaded but again: this does not work for me! :-(
What am I doing wrong?
Thx in advance for your help!
EDIT:
Ok so the problem with the answer in the first post provided by #user728291 is the following:
For some reason the router stuff get's called AFTER the Deps.autorun() ... what is wrong here? :( (please note: eventsLoaded == stuffLoaded.)
Where do you guys put your Deps.autorun() for the subscriptions or in other words: What's your code mockup for this?
I actually really think that my code mockup is just plain wrong. So how do you make different subscriptions based on the route (or in other words: based on the template which is currently shown)?
AND: Where do you put the Deps.autorun()? Inside the router.add() function? Or just inside of (Meteor.isClient)?
I think #user728291's answer is pretty spot on, I'd just add that Meteor.subscribe returns a handle that you can use to check readiness:
Keep a reference to the handle
Deps.autorun(function() {
stuffHandle = Meteor.subscribe(Session.get('bar'));
});
Then check it in your template:
{{#if stuffHandle.ready}}
...
{{/if}}
Template.barTemplate.helpers({stuffHandle: stuffHandle});
And control it via the session:
'/myroute': function(bar) {
Session.set('bar', bar);
return 'barTemplate';
}
Better to put the subscription in a Deps.autorun and use Session variable to pass arguments from the router. Also, make sure you are setting stuffLoaded to false before the subscribe runs. Otherwise it just keeps its old value.
'/myroute': function(bar) {
if ( ! Session.equals( "bar", bar ) ) {
Session.set( "stuffLoaded", false); //subscription needs to be run
Session.set( "bar", bar ); // this change will trigger Dep.autorun
}
return 'stuffPage';
}
Deps.autorun ( function (){
Meteor.subscribe("myCollection", Session.get( "bar" ), function() {
Session.set("stuffLoaded", true);
});
});
You might need some initial default values for the Session variables if you are not getting what you want on the first time the page loads.
First off, you may be missing the actual function name for the callback as demonstrated in this post.
Meteor.subscribe("myCollection", bar, function onComplete() {
Session.set('stuffLoaded', true);
});
Which seems to be great practice. I don't usually miss a beat using this method.
Secondly, I'm not sure subscriptions inside routes work well? I'd rather do the following:
'/myroute': function(bar) {
Session.set("myCollectionParam", bar)
return 'stuffPage';
}
So then the subsciption finally looks like this:
Meteor.subscribe("myCollection", Session.get("myCollectionParam"), function onComplete() {
Session.set('stuffLoaded', true);
});
OR (not sure which works correctly for you, depending on your publish function):
Meteor.subscribe("myCollection", {bar: Session.get("myCollectionParam")}, function onComplete() {
Session.set('stuffLoaded', true);
});
Good luck!
EDIT
Just mentioning something about the publish function:
While Session.get("myCollectionParam") could return null, you can ensure the behaviour a bit more by using the following publish method:
Meteor.publish("myCollection", function(myCollectionParam) {
check(myCollectionParam, String);
return MyCollection.find({_id: myCollectionParam});
});
I've been at this for an hour and I need help. This is kind of baffling me. Consider this explicit setup of an object in my code:
WORKING CASE:
var terms={};
terms[0]={};
terms[1]={"label":"bag","cell_src":"images/bag.jpg","clue_type":"audio","clue_src":"/audio/bus.wav"};
terms[2]={"label":"crayon","cell_src":"images/crayon.jpg","clue_type":"audio","clue_src":"/audio/car.wav"};
terms[3]={"label":"pen","cell_src":"images/pen.jpg","clue_type":"audio","clue_src":"/audio/car.wav"};
terms[4]={"label":"pencil","cell_src":"images/pencil.jpg","clue_src":"/audio/boat.wav"};
terms[5]={"label":"pencil_case","cell_src":"images/pencil_case.jpg","clue_src":"/audio/train.wav"};
terms[6]={"label":"rubber","cell_src":"images/rubber.jpg","clue_src":"/audio/taxi.wav"};
terms[7]={"label":"ruler","cell_src":"images/ruler.jpg","clue_src":"/audio/plane.wav"};
terms[8]={"label":"sharpener","cell_src":"images/sharpener.jpg","clue_src":"/audio/taxi.wav"};
window.terms= terms;
window.terms= terms; // for using globaly
if I do a console.log(window.terms[1]); I get "bag". Thats what I want.
NOT WORKING CASE
If instead of explicitly defining the values of term{}, I read in the contents from a json file and assign them to each enumerated index like this:
var terms={};
terms[0]={};
$.getJSON('content.json', function(data){
$.each(data,function(i){
//terms[i]={"label":"bag","cell_src":"images/bag.jpg","clue_type":"audio","clue_src":"/audio/bus.wav"};
terms[i+1]={"label":data[i].headword,"cell_src":data[i].image,"clue_type":"audio","clue_src":data[i].audio};
});
window.terms=terms;
});
if I do a console.log(window.terms[1]); I get an error "Uncaught TypeError: Cannot read property '1' of undefined" Note that I have an alternate attempt commented out where I eliminate the possibility that theres something weird going on with the values I am trying to pull in and I explicitly assign the same static value to all the indexes. That produces the same error.
Any ideas how this could be??
$.getJSON does not block when performing an AJAX call. You have to keep the callback chain a live.
I think you want to define terms as an array of objects. Currently you have it defined as an object with properties 1, 2, 3, etc. Syntax like var terms = {} means terms is an object and when you assign terms[1] = {"label": "bag"} you're saying "the property named 1 of object terms is {"label": "bag"}. Just change your terms declaration to this:
var terms = [];
Also, if you want to see the label property of one of the objects the log statement would looks like this:
console.log(terms[2].label);
The $.getJSON() function is just a shorthand for a call to $.ajax() to load a JSON file. Since the AJAX call is asynchronous, the execution of $.getJSON() completes, and any code after it is executed, before the data has been loaded and stored in your variable.
If you want to work with terms do so inside the success callback function that you're passing to $.getJSON().
If your code looks like this:
var terms={};
terms[0]={};
$.getJSON('content.json', function(data){
$.each(data,function(i){
//terms[i]={"label":"bag","cell_src":"images/bag.jpg","clue_type":"audio","clue_src":"/audio/bus.wav"};
terms[i+1]={"label":data[i].headword,"cell_src":data[i].image,"clue_type":"audio","clue_src":data[i].audio};
});
window.terms=terms;
});
// use window.terms here
Then it won't work, because the // use window.terms here part executes before the AJAX call has finished. You'll need to move that to a separate function and call that from the success callback:
function workWithTerms() {
// use window.terms here
}
var terms={};
terms[0]={};
$.getJSON('content.json', function(data){
$.each(data,function(i){
//terms[i]={"label":"bag","cell_src":"images/bag.jpg","clue_type":"audio","clue_src":"/audio/bus.wav"};
terms[i+1]={"label":data[i].headword,"cell_src":data[i].image,"clue_type":"audio","clue_src":data[i].audio};
});
window.terms=terms;
workWithTerms();
});