Multiple Data Contexts in router - Meteor - meteor

I'm building a meteor app and on one route I'm adding multiple data context like so -
this.route('orgPage', {
path: '/org/:title',
data: {
orgs: function () {Orgs.findOne(this.params._id)},
projects: function() {Meteor.subscribe('projects', this.params._id)}
}
The only problem is that when I try to access this data in my templates js file, I can't access the _id or any of the attributes of orgs.
I've tried several approaches, but it always returns undefined. If I use a single data context, it works perfectly. Here is the function that doesn't function properly -
Template.orgPage.events({
'click #newProject': function(e) {
$('#npModal').modal();
},
'submit #npModal form': function(e, template) {
e.preventDefault();
if(!$(e.target).find('[name=newTitle]').val()) {
var projectTitle = 'Untitled'
} else {
var projectTitle = $(e.target).find('[name=newTitle]').val()
}
var theid = this._id;
var newProject = {
title: projectTitle,
organization: theid
}
Meteor.call('project', newProject, function(error, id) {
if (error)
return alert(error.reason);
$('#npModal').modal('hide');
$('#npModal').on('hidden.bs.modal', function (e) {
Router.go('newFields', {});
})
});
});
Anyone have any ideas? Thanks!!

You have missed a return statement. function () {Orgs.findOne(this.params._id)} should be function () {return Orgs.findOne(this.params._id)}. Further more, this inside this function won't refer to what you want, so you can't use this.params. And why do you subscribe to a subscription as a data context property? Do it in the waitOn function instead.

Related

Meteor pub/sub issues

This below is my collection code
Competitions = new Mongo.Collection("competitions");
var CompetitionsSchema = new SimpleSchema({
year: {
type: String
},
division: {
type : String,
allowedValues: ['Elite', '1st','2nd','3rd','4th','Intro']
},
teams:{
type : [TeamSchema],
allowedValues: (function () {
return Teams.find().fetch().map(function (doc) {
return doc.name;
});
}()) //here we wrap the function as expression and invoke it
}
});
In the allowedValues function
Teams.find is empty.
In the router I am subscribing to the publication as follows
this.route('competitions', {
path: '/admin/competitions',
layoutTemplate: 'adminLayout',
waitOn: function () {
return [
Meteor.subscribe('teams')
];
}
});
This is my publish function
Meteor.publish('teams', function() {
return Teams.find({},{sort: {
points: -1,
netRunRate : -1
}});
});
Do I have to do subscription some where else as well?
Your problem is in this piece of code:
allowedValues: (function () {
return Teams.find().fetch().map(function (doc) {
return doc.name;
});
}()) //here we wrap the function as expression and invoke it
This gets called when the page loads. At that point the Teams collection will still be empty on the client side. You need to wait until the data is ready. Since you are using waitOn in iron-router, it might be enough to just move this code to the onRendered callback.

Why isn't this template reactive?

Why isn't this reactive? And more importantly how can it be made reactive?
I'd like the data to be saved in Mongo and used in the template. I could use a ReactiveVar or ReactiveDict. Do I need two copies of the data?
Doesn't Suspects.findOne('bruce') return a reactive object already? I tried putting the human answer directly on Bruce, but it didn't trigger an update.
The events fire, log(this) shows bruce's answer was changed, but the template doesn't re-render. What's the good way to do this?
http://meteorpad.com/pad/KoH5Qu7Fg3osMQ79e/Classification
It's Meteor 1.2 with iron:router added:
<head>
<title>test</title>
</head>
<template name="question">
{{#unless isAnswered 'human'}} <!-- :-< I'm not reacting here -->
<div>Sir, are you classified as human?</div>
<button id="no">No, I am a meat popsicle</button>
<button id="smokeYou">Smoke you</button>
{{else}}
<div> Classified as human? <b>{{answers.human}}</b></div>
{{/unless}}
</template>
And the JavaScript:
// Why isn't this reactive?
if (Meteor.isClient) {
Template.question.helpers({
isAnswered: function (question) { // :-< I'm not reactive
var suspect = Template.instance().data;
return (typeof suspect.answers[question] !== 'undefined');
}
});
Template.question.events({
'click #no': function () {
this.answers.human = "No"; // :-< I'm not reactive
console.log(this);
},
'click #smokeYou': function() {
this.answers.human = "Ouch"; // :-< I'm not reactive
console.log(this);
}
});
}
// Collection
Suspects = new Meteor.Collection('suspects');
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
Suspects.upsert('bruce', { quest: 'for some elements', answers: {}});
});
Meteor.publish('suspects', function() {
return Suspects.find({});
});
}
// Iron Router
Router.route('/', {
template: 'question',
waitOn: function() {
return Meteor.subscribe('suspects');
},
data: function() {
return Suspects.findOne('bruce');
}
});
Thanks :-)
The events are not actually updating the reactive data source (the db record). Instead of doing:
Template.question.events({
'click #no': function () {
this.answers.human = "No";
}
});
The event needs to perform a database action, either through a direct update or through a Meteor.call() to a Meteor.method. For example:
'click #no': function(){
Suspects.update('bruce', {'answers': {'human': 'no'}});
}
If you use this pattern, you will also need to set the correct allow and deny rules to permit the update from client code. http://docs.meteor.com/#/full/allow. Methods generally end up being a better pattern for bigger projects.
Also, I'm not sure off the top of my head that Template.instance().data in your helper is going to be reactive. I would use Template.currentData() instead just to be sure. http://docs.meteor.com/#/full/template_currentdata
Very close you just need to use ReactiveVar by the sound of it it pretty much explains what it's :) http://docs.meteor.com/#/full/reactivevar
and here's how to use it
if (Meteor.isClient) {
Template.question.onCreated(function () {
this.human = new ReactiveVar();
});
Template.question.helpers({
isAnswered: function (question) {
return Template.instance().human.get();
}
});
Template.question.events({
'click #no': function (e, t) {
t.human.set('No');
console.log(t.human.get());
},
'click #smokeYou': function(e, t) {
t.human.set('Ouch');
console.log(t.human.get());
}
});
}
UPDATE: if you're using a cursor I usually like to keep it on the template level not on iron router:
if (Meteor.isClient) {
Template.question.helpers({
isAnswered: function (question) {
return Suspects.findOne('bruce');
}
});
Template.question.events({
'click #no': function (e, t) {
Suspects.update({_id: ''}, {$set: {human: 'No'}});
},
'click #smokeYou': function(e, t) {
Suspects.update({_id: ''}, {$set: {human: 'Ouch'}});
}
});
}

Meteor helper as temp storage

I'm trying to use a helper as a reactive data source to store something until I insert it into the database. I'm trying to do this:
Template.test.helpers({
tempStorage: []
})
Template.test.events({
'click .something': function() {
tempStorage.push('value');
}
})
And then in my jade I want to be able to do:
+each tempStorage
+someTemplateTheUsesTheData
However I can't seem to push to tempStorage from my events JavaScript
First off, helpers should be functions (although apparently values can be assigned, thanks #saimeunt), and you cannot use them as storage.
What you can do is install the reactive variables package and attach your tempStorage to your template, like so:
Template.test.onCreated(function () {
this.tempStorage = new ReactiveVar();
this.tempStorage.set([]);
});
Template.test.events({
'click .something': function(event, template) {
var tempStorage = template.tempStorage.get();
tempStorage.push('value');
template.tempStorage.set(tempStorage);
}
});
Template.test.helpers({
tempStorage: function () {
return Template.instance().tempStorage.get();
}
});
If you want to, you can use Session instead of a reactive variable, but your tempStorage will persist accross templates (not so temporary anymore!):
Template.test.onRendered(function () {
Session.set('tempStorage', []);
});
Template.test.events({
'click .something': function() {
var tempStorage = Session.get('tempStorage');
tempStorage.push('value');
Session.set('tempStorage', tempStorage);
}
});
Template.test.helpers({
tempStorage: function () {
return Session.get('tempStorage');
}
});

Meteor JS Iron Router subscribe to data

I am using Meteor 0.8 and iron-router.
I have a single route that I use to subscribe to a number of collections. What I would like to do is create a document when you arrive on the route and then subscribe to it so I can access it.
This is my route:
this.route('product_collection', {
path: '/collection/:collection',
template: 'product_collection',
layoutTemplate: 'layout-new',
waitOn: function() { return [ Meteor.subscribe('specific_product_collection', this.params.collection), Meteor.subscribe('product_types')] },
data: function () { return ProductCollections.findOne({ url: this.params.collection }) },
onBeforeAction: function(pause){
//Prevent double rendering
if (this.ready()) {
var productType = ProductTypes.findOne({url: this.params.collection })
console.log(productType)
if(productType){
var product = {
productType: productType._id
};
Session.set("productTypeId", productType._id);
Products.insert(product, function(error, id) {
if (error) {
console.error(error);
set_notification('Oops, something went wrong.', 'Please try again later.');
} else {
Session.set("productId", id);
Session.set("productName", productType.name );
//Not working
Meteor.subscribe('specific-product', id);
console.log('should be subscribed')
}
});
}
} else {
console.log('loading')
this.render('loading');
pause();
}
},
});
Ok,
I found out the ID was different on the server vs client side location. So ended up writing a meteor method call on the server.
See reference: Retrieve _id after insert in a Meteor.method call

Backbone _.each collection.model empty

I'm trying to simply return what I request in PHP to JSON.
My problem is that each Stock is not yet completed.
Indeed, it is the "render" but "this.collection.models" is not yet completed because the request is not yet finished.
What should I do to correct this problem, wait until the request is finished so that the loop is done correctly.
Thank you in advance
var Article = Backbone.Model.extend({});
var Articles = Backbone.Collection.extend({
model:Article,
url: function() {
return _BASE_URL+'/sync/getLastArticles';
},
initialize:function () {
this.fetch();
}
});
var ArticlesView = Backbone.View.extend({
template:$('#articles').html(),
initialize:function () {
this.collection = new Articles();
this.render();
},
render:function () {
console.log(this.collection);
var that = this;
_.each(this.collection.models, function (item) {
console.log(item);
}, this);
},
renderArticle:function () {
;
}
});
You render before the fetch is done. What you want to do, is to wait for the fetch to complete and then render. Now how would you get notification of when the fetch is done? You have 2 options:
The success function (Not recommended by me)
// ArticlesView
initialize: function() {
_.bindAll(this); // Don't forget to BIND
this.collection = new Articles();
this.collection.fetch({
success: this.render
});
}
Now when the fetch has been successful, render is called. This however can cause scoping problems and Backbone.js offers a much nicer alternative to callback functions: events.
Event callback (prefer this)
// ArticlesView
initialize: function() {
_.bindAll(this);
this.collection = new Articles();
this.collection.on('reset', this.render); // bind the reset event to render
this.collection.fetch();
}

Resources