Im struggling with an issue using Meteor JS.
I call an api wich return me a Json array wich look like the one returned on this url (I don't put the whole array here cause of the size): https://blockchain.info/address/12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX?format=json&offset=0
I call it server side like :
if (Meteor.isServer) {
Meteor.methods({
getWalletPreviousTx: function() {
var url = "https://blockchain.info/address/12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX?format=json&offset=0";
var result = Meteor.http.get(url);
if(result.statusCode==200) {
var tx = JSON.parse(result.content);
return tx;
} else {
console.log("Response issue: ", result.statusCode);
var errorJson = JSON.parse(result.content);
throwError("Couldn't fetch wallet balance from Blockchain, try again later !");
}
}
});
}
And i retrieve it to my view via an helper in a specific template :
Template.wallet.helpers({
addrTxs: function () {
Meteor.call('getWalletPreviousTx', function(err, tx) {
console.log(tx);
return [tx];
});
}
});
The console.log in the helper actually log my Json array wich mean it have access to it.
Now the part im struggling with is to retrieve this Json to my view, i've tried a lot of way and none of them works, actually i have this in my view :
<template name="wallet">
<table>
{{#each addrTxs}}
<ul>
{{> addrTx}}
</ul>
{{/each }}
</table>
</template>
The part of the Json I want to display is the "addr" and "value" of each transactions :
"inputs":[
{
"sequence":4294967295,
"prev_out":{
"spent":true,
"tx_index":97744124,
"type":0,
"addr":"1AWAsn8rhT555RmbMDXXqzrCscPJ5is5ja",
"value":50000,
"n":0,
"script":"76a914683d704735fd591ba9f9aebef27c6ef00cbd857188ac"
}
}
]
Fact is, i never managed to display anything from this Json array in my view, even puting directly this in my view doesn't show anything :
{{addrTxs}}
What am I doing wrong ? Can anyone help with this ?
Thanks for reading.
----------------------- Edit ---------------------
I think the problem is more that my helper and template are loaded before the api call is finished (because the console.log appear in my console like 3seconds after my page is rendered). How can i make my helper wait until the api call is finished before rendering it in the view ? I use iron router.
I have tried to add a waitOn action on my route in order to wait until my api call is finished :
Router.route('/wallet', {
name: 'wallet',
template: 'wallet',
loadingTemplate: 'loading',
waitOn: function () {
Meteor.call('getWalletPreviousTx', function(error, result) {
if(!error) {
Ready.set(result)
}
});
return [
function () { return Ready.get(); }
];
},
action: function () {
if (this.ready())
this.render();
else
this.render('loading');
}
});
The above code with the waitOn action seems to work (i have no errors) but i don't know the way to display in my view the specific result from :
if(!error) {
Ready.set(result)
}
Transactions are contained in tx.txs, iterates through that.
Template.wallet.helpers({
addrTxs: function () {
Meteor.call('getWalletPreviousTx', function(err, tx) {
console.log(tx);
return tx.txs;
});
}
});
You're right, you need to use the sessions variables with async call.
First, call method on created :
Template.wallet.created = function () {
Meteor.call('getWalletPreviousTx', function(err, tx) {
console.log(tx.txs);
Session.set('tx', tx.txs);
});
};
Helper should look like this :
Template.wallet.helpers({
addrTxs: function () {
return Session.get('tx');
}
});
Related
I am really struggling with waiting on a subscription to load for a specific route before returning the data to the template. I can see on from the publish on the server that a document is found, but on the client there is no document.
If I do a find().count() on the publish, it shows 1 document found, which is correct, but when I do the count on the subscription, it shows 0 documents.
I have tried a number of different methods, like using subscriptions:function() instead of waitOn:function(), but nothing works.
Collections.js lib:
SinglePackage = new Mongo.Collection("SinglePackage");
SinglePackage.allow({
insert: function(){
return true;
},
update: function(){
return true;
},
remove: function(){
return true;
}
});
Publications.js server:
Meteor.publish("SinglePackage", function(pack_id) {
return Packages.find({shortId: pack_id});
});
Iron Router:
Router.route('/package/:id', {
name: 'package.show',
template: 'Package_page',
layoutTemplate: 'Landing_layout',
waitOn: function() {
return Meteor.subscribe('SinglePackage', this.params.id);
},
data: function() {
return SinglePackage.find();
},
action: function () {
if (this.ready()) {
this.render();
} else {
this.render('Loading');
}
}
});
Am I doing something very wrong, or is this just a complicated thing to achieve? One would think that waitOn would make the rest of the function wait until the subscription is ready.
Any help would be highly appreciated.
It appears that the data function is running before the subscription is ready. Even if the data function did run after the subscription was ready, it wouldn't be a reactive data source rendering the pub/sub here pointless. Here's a great article on reactive data sources.
Referring to the example from the Iron Router Docs for subscriptions, you would do something like this:
Router.route('/package/:id', {
subscriptions: function() {
// returning a subscription handle or an array of subscription handles
// adds them to the wait list.
return Meteor.subscribe('SinglePackage', this.params.id);
},
action: function () {
if (this.ready()) {
this.render();
} else {
this.render('Loading');
}
}
});
Then in your template.js:
Template.Package_page.helpers({
singlePackage() {
// This is now a reactive data source and will automatically update whenever SinglePackage changes in Mongo.
return Package.find().fetch();
}
});
In your template.html you can now use singlePackage:
<template name="Package_page">
{#with singlePackage} <!-- Use #each if you're singlePackage is an array -->
ID: {_id}
{/with}
</template>
I am using ember. I intercept one component's button click in controller. The click is to trigger a new report request. When a new report request is made, I want the newly made request to appear on the list of requests that I currently show. How do I make ember refresh the page without obvious flicker?
Here is my sendAction code:
actions: {
sendData: function () {
this.set('showLoading', true);
let data = {
startTime: date.normalizeTimestamp(this.get('startTimestamp')),
endTime: date.normalizeTimestamp(this.get('endTimestamp')),
type: constants.ENTERPRISE.REPORTING_PAYMENT_TYPE
};
api.ajaxPost(`${api.buildV3EnterpriseUrl('reports')}`, data).then(response => {
this.set('showLoading', false);
return response.report;
}).catch(error => {
this.set('showLoading', false);
if (error.status === constants.HTTP_STATUS.GATEWAY_TIMEOUT) {
this.notify.error(this.translate('reports.report_timedout'),
this.translate('reports.report_timedout_desc'));
} else {
this.send('error', error);
}
});
}
There are few think you should consider. Generaly you want to have variable that holds an array which you are render in template in loop. For example: you fetch your initial set of data in route and pass it on as model variable.
// route.js
model() { return []; }
// controller
actions: {
sendData() {
foo().then(payload => {
// important is to use pushObjects method.
// Plain push will work but wont update the template.
this.get('model').pushObjects(payload);
});
}
}
This will automatically update template and add additional items on the list.
Boilerplate for showLoading
You can easily refactor your code and use ember-concurency. Check their docs, afair there is example fitting your usecase.
Hello and sorry for my broken English.
I have a method "updateCounterState" for incrementing and updating a number every second in my mongodb-Collection and showing this number in my template in my html-file. And it seems to work, but I get every time I use this function two errors. For three days I am trying to figure out how to fix these errors. I believe I have to use this code block with a Meteor.bindEnvironment-Wrapper because of my asynchronous updates. However, I don't know how to use this to fix these errors. Or maybe I am completely wrong and these errors have another cause.
EDIT #2:
client/main.html
<head>
<title>test-timer</title>
</head>
<body>
{{> timeTrackerTemplate}}
</body>
<template name="timeTrackerTemplate">
{{#each showCounterState}}
<p class="counter-state">{{state}}</p>
<button class="start-counting">Start</button>
{{/each}}
</template>
client/main.js
import { Template } from 'meteor/templating';
Template.timeTrackerTemplate.events({
'click .start-counting': function(e) {
Meteor.call('updateCounterState', this._id);
}
});
server/main.js
import { Meteor } from 'meteor/meteor';
Meteor.startup(() => {
// code to run on server at startup
});
methods.js (root folder)
Meteor.methods({
'updateCounterState': function(id) {
Meteor.setInterval(function() {
TimeTracker.update(
{_id: id},
{
$inc: {state: 1},
},
);
}, 1000);
}
});
ttcollection.js (root folder)
TimeTracker = new Mongo.Collection('testtracker');
if (Meteor.isClient) {
Template.timeTrackerTemplate.helpers({
showCounterState: function () {
return TimeTracker.find();
}
});
}
meteor:PRIMARY> db.testtracker.find({})
{ "_id" : ObjectId("57ee677227a0af6b59dc12ce"), "state" : 147 }
{ "_id" : ObjectId("57ee677a27a0af6b59dc12cf"), "state" : 148 }
{ "_id" : ObjectId("57ee6e6027a0af6b59dc12d0"), "state" : 73 }
Error every time I press a button:
Exception while simulating the effect of invoking 'updateCounterState' Error: Can't set timers inside simulations
at withoutInvocation (http://localhost:3000/packages/meteor.js?hash=ae8b8affa9680bf9720bd8f7fa112f13a62f71c3:463:13)
at bindAndCatch (http://localhost:3000/packages/meteor.js?hash=ae8b8affa9680bf9720bd8f7fa112f13a62f71c3:471:33)
at Object.setInterval (http://localhost:3000/packages/meteor.js?hash=ae8b8affa9680bf9720bd8f7fa112f13a62f71c3:498:24)
at updateCounterState (http://localhost:3000/app/app.js?hash=f641538433c68c8f8b820f0e05cebb12531cb357:66:20)
at http://localhost:3000/packages/ddp-client.js?hash=27502404fad7fc072e57e8b0b6719f40d92709c7:3973:25
at withValue (http://localhost:3000/packages/meteor.js?hash=ae8b8affa9680bf9720bd8f7fa112f13a62f71c3:1077:17)
at Connection.apply (http://localhost:3000/packages/ddp-client.js?hash=27502404fad7fc072e57e8b0b6719f40d92709c7:3964:54)
at Connection.call (http://localhost:3000/packages/ddp-client.js?hash=27502404fad7fc072e57e8b0b6719f40d92709c7:3840:17)
at Object.clickStartCounting (http://localhost:3000/app/app.js?hash=f641538433c68c8f8b820f0e05cebb12531cb357:47:20)
at http://localhost:3000/packages/blaze.js?hash=a9372ce320c26570a2e4ec2588d1a6aea57de9c1:3718:20 Error: Can't set timers inside simulations
at withoutInvocation (http://localhost:3000/packages/meteor.js?hash=ae8b8affa9680bf9720bd8f7fa112f13a62f71c3:463:13)
at bindAndCatch (http://localhost:3000/packages/meteor.js?hash=ae8b8affa9680bf9720bd8f7fa112f13a62f71c3:471:33)
at Object.setInterval (http://localhost:3000/packages/meteor.js?hash=ae8b8affa9680bf9720bd8f7fa112f13a62f71c3:498:24)
at updateCounterState (http://localhost:3000/app/app.js?hash=f641538433c68c8f8b820f0e05cebb12531cb357:66:20)
at http://localhost:3000/packages/ddp-client.js?hash=27502404fad7fc072e57e8b0b6719f40d92709c7:3973:25
at withValue (http://localhost:3000/packages/meteor.js?hash=ae8b8affa9680bf9720bd8f7fa112f13a62f71c3:1077:17)
at Connection.apply (http://localhost:3000/packages/ddp-client.js?hash=27502404fad7fc072e57e8b0b6719f40d92709c7:3964:54)
at Connection.call (http://localhost:3000/packages/ddp-client.js?hash=27502404fad7fc072e57e8b0b6719f40d92709c7:3840:17)
at Object.clickStartCounting (http://localhost:3000/app/app.js?hash=f641538433c68c8f8b820f0e05cebb12531cb357:47:20)
at http://localhost:3000/packages/blaze.js?hash=a9372ce320c26570a2e4ec2588d1a6aea57de9c1:3718:20
Where is the updateCounterState function defined? Is it a helper on a Template? What are the allow/deny statuses on the TimeTracker collection?
EDIT (after question updated)
First, your method takes as a single parameter the id of the TimeTracker, but when you call your method, you don't pass anything. You should either create a TimeTracker object from the client and then pass its _id, or take no arguments in your function and create it on the server (in this case, your method should return the _id to keep track of it.
Then, putting code in the root folder is not the best practice, you should use the /imports folder and then import { foo } from 'foo.js', or put your code in /server or /client.
Your Template helpers returns a Mongo cursor which is of no use. If you want to return a single TimeTracker, use TimeTracker.findOne({ _id: id }) which will return the object. Basically, you should probably have something like:
Template.timeTrackerTemplate.onCreated(function () {
this.trackerId = null;
});
Template.timeTrackerTemplate.helpers({
showCounterState: function() {
const trackerId = Template.instance().trackerId;
return trackerId ? TimeTracker.findOne({ _id: trackerId }).state : '';
}
});
Template.timeTrackerTemplate.events({
'click .start-counting': function(e, instance) {
instance.trackerId = TimeTracker.insert({ state: 0 });
Meteor.call('updateCounterState', instance.trackerId);
}
});
I solved my errors. It is important to call methods with collections on server side only - especially without "insecure" package.
if (Meteor.isServer) {
//Meteor.methods()
}
I have a "box" route/controller as below;
export default Ember.Controller.extend({
initialized: false,
type: 'P',
status: 'done',
layouts: null,
toggleFltr: null,
gridVals: Ember.computed.alias('model.gridParas'),
gridParas: Ember.computed('myServerPars', function() {
this.set('gridVals.serverParas', this.get('myServerPars'));
this.filterCols();
if (!this.get('initialized')) {
this.toggleProperty('initialized');
} else {
Ember.run.scheduleOnce('afterRender', this, this.refreshBox);
}
return this.get('gridVals');
}),
filterCols: function()
{
this.set('gridVals.layout', this.get('layouts')[this.get('type')]);
},
myServerPars: function() {
// Code to set serverParas
return serverParas;
}.property('type', 'status', 'toggleFltr'),
refreshBox: function(){
// Code to trigger refresh grid
}
});
My route looks like;
export default Ember.Route.extend({
selectedRows: '',
selectedCount: 0,
rawResponse: {},
model: function() {
var compObj = {};
compObj.gridParas = this.get('gridParas');
return compObj;
},
activate: function() {
var self = this;
self.layouts = {};
var someData = {attr1:"I"};
var promise = this.doPost(someData, '/myService1', false); // Sync request (Is there some way I can make this work using "async")
promise.then(function(response) {
// Code to use response & set self.layouts
self.controllerFor(self.routeName).set('layouts', self.layouts);
});
},
gridParas: function() {
var self = this;
var returnObj = {};
returnObj.url = '/myService2';
returnObj.beforeLoadComplete = function(records) {
// Code to use response & set records
return records;
};
return returnObj;
}.property(),
actions: {
}
});
My template looks like
{{my-grid params=this.gridParas elementId='myGrid'}}
My doPost method looks like below;
doPost: function(postData, requestUrl, isAsync){
requestUrl = this.getURL(requestUrl);
isAsync = (isAsync == undefined) ? true : isAsync;
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
return $.ajax({
// settings
}).success(resolve).error(reject);
});
return promise;
}
Given the above setup, I wanted to understand the flow/sequence of execution (i.e. for the different hooks).
I was trying to debug and it kept hopping from one class to another.
Also, 2 specific questions;
I was expecting the "activate" hook to be fired initially, but found out that is not the case. It first executes the "gridParas" hook
i.e. before the "activate" hook. Is it because of "gridParas"
specified in the template ?
When I do this.doPost() for /myService1, it has to be a "sync" request, else the flow of execution changes and I get an error.
Actually I want the code inside filterCols() controller i.e.
this.set('gridVals.layout', this.get('layouts')[this.get('type')]) to
be executed only after the response has been received from
/myService1. However, as of now, I have to use a "sync" request to do
that, otherwise with "async", the execution moves to filterCols() and
since I do not have the response yet, it throws an error.
Just to add, I am using Ember v 2.0
activate() on the route is triggered after the beforeModel, model and afterModel hooks... because those 3 hooks are considered the "validation phase" (which determines if the route will resolve at all). To be clear, this route hook has nothing to do with using gridParas in your template... it has everything to do with callling get('gridParas') within your model hook.
It is not clear to me where doPost() is connected to the rest of your code... however because it is returning a promise object you can tack on a then() which will allow you to essentially wait for the promise response and then use it in the rest of your code.
Simple Example:
this.doPost().then((theResponse) => {
this.doSomethingWith(theResponse);
});
If you can simplify your question to be more clear and concise, i may be able to provide more info
Generally at this level you should explain what you want to archive, and not just ask how it works, because I think you fight a lot against the framework!
But I take this out of your comment.
First, you don't need your doPost method! jQuerys $.ajax returns a thenable, that can be resolved to a Promise with Ember.RSVP.resolve!
Next: If you want to fetch data before actually rendering anything you should do this in the model hook!
I'm not sure if you want to fetch /service1, and then with the response you build a request to /service2, or if you can fetch both services independently and then show your data (your grid?) with the data of both services. So here are both ways:
If you can fetch both services independently do this in your routes model hook:
return Ember.RSVP.hash({
service1: Ember.RSVP.resolve($.ajax(/*your request to /service1 with all data and params, may use query-params!*/).then(data => {
return data; // extract the data you need, may transform the response, etc.
},
service2: Ember.RSVP.resolve($.ajax(/*your request to /service2 with all data and params, may use query-params!*/).then(data => {
return data; // extract the data you need, may transform the response, etc.
},
});
If you need the response of /service1 to fetch /service2 just do this in your model hook:
return Ember.RSVP.resolve($.ajax(/*/service1*/)).then(service1 => {
return Ember.RSVP.resolve($.ajax(/*/service2*/)).then(service2 => {
return {
service1,
service2
}; // this object will then be available as `model` on your controller
});
});
If this does not help you (and I really think this should fix your problems) please describe your Problem.
I am working on a project to pull in twitter timeline's for selected teams/players. When I am on the team/_id page, how can I grab an attribute to path through a method?
Below is my client side javascript, as well as the relevant route from iron router. When I type in something like "patriots" to the "????" section, I get a result. I would like to do this dynamically, I currently have the twitter handles stored under the twitter attribute.
Template.tweets.helpers({
teams: function() {
return Teams.find();
},
});
Template.tweets.onRendered(function () {
var twitterHandle = "???";
Meteor.call('getTimeline', twitterHandle, function(err,results){
if (err) {
console.log("error", error);
};
console.log(results);
Session.set("twitter", JSON.parse(results.content));
})
return Session.get("twitter");
});
Router.route('/teams/:_id', {
name: 'teamView',
template: 'teamView',
data: function(){
var currentTeam = this.params._id;
return Teams.findOne({ _id: currentTeam });
var twitterHandle = this.params.twitter;
return Teams.findOne({twitter: twitterHandle});
}
});
<template name="tweets">
<h3>Tweets</h3>
<div class="container">
{{twitter}}
</div>
</template>
You should be able to access all information from the current route using the Router.current() object. In your case you can use Router.current().params._id to get the _id param:
var twitterHandle = Router.current().params._id;
Edits based on your comments below
I did not notice that you were calling the Teams.findOne function twice in your route's data function. Form the looks of it you're already storing the twitter handle in the Teams collection, so you merely need to access the data that's returned by the route.
Template.tweets.helpers({
twitterData: function() {
//return the data stored in the callback function of the Meteor method call in the onRendered event
return Session.get('twitter');
}
});
Template.tweets.onRendered(function () {
//clear any previously stored data making the call
Session.set('twitter', null);
//property of the team document returned by the data function in the route.
var twitterHandle = this.data.twitter;
Meteor.call('getTimeline', twitterHandle, function(err,results){
if (err) {
console.log("error", error);
} else {
Session.set("twitter", JSON.parse(results.content));
}
});
});
Router.route('/teams/:_id', {
name: 'teamView',
template: 'teamView',
data: function(){
var currentTeam = this.params._id;
return Teams.findOne({ _id: currentTeam });
}
});
<template name="tweets">
<h3>Tweets</h3>
<div class="container">
<!-- twitterData will be an object, so you'll need to figure out what properties to display and use dot notation//-->
{{twitterData}}
</div>
</template>