Why Am I Unable to Pass Data to Iron-Router Route Template? - javascript

I just updated to Meteor 1.0 and the latest version of Iron-Router.
For whatever reason, when I pass data to a template that includes only a single document, it works. But when I try to pass multiple documents to the template, I get a blank screen.
//galleryRoute.js
Router.route('/:section', function() {
this.layout('Gallery', {
data: function() {
var data = { photos: photos.find({ path: { $regex: '/'+this.params.section +'/' } }) };
return data;
}
});
});
<template name="Gallery">
<div class="container">
{{#each photos}}
<div class="section-photo" style="background:url({{path}}) no-repeat center center; width:175px; height:175px; background-size:cover;"></div>
{{/each}}
</div>
</template>
Wondering if anyone has any ideas about why this would be?

In your data function, you're accessing this.params.section. However, this is refers to your current scope, which is not the route, therefore this.params will be undefined. Try doing something like this:
//galleryRoute.js
Router.route('/:section', function() {
var route = this;
this.layout('Gallery', {
data: function() {
var data = { photos: photos.find({ path: { $regex: '/'+route.params.section +'/' } }) };
return data;
}
});
});

Related

Vuejs: Passing SAVE function into CRUD component

I am struggeling with a proper solution which requires an advanced parent-child communication in vuejs. There can be many different parent components which has a logic how to save data. From the other side there will be only one child component which has a list of elements and a form to create new elements but it doesn't know how to save the data.
The question is: Is there any other way (better approach) to have the same functionality but to get rid of this.$refs.child links. For example I am wondering if I can just pass a function (SaveParent1(...) or SaveParent2(...)) to the child component. But the problem is the function contains some parent's variables which won't be available in child context and those variables could be changed during the runtime.
Just few clarifications:
The methods SaveParent1 and SaveParent2 in real life return
Promise (axios).
The child-component is like a CRUD which is used
everywhere else.
At the moment the communication looks like that: CHILD -event-> PARENT -ref-> CHILD.
Bellow is the example:
<div id="app">
<h2>😀Advanced Parent-Child Communication:</h2>
<parent-component1 param1="ABC"></parent-component1>
<parent-component2 param2="XYZ"></parent-component2>
</div>
Vue.component('parent-component1', {
props: { param1: { type: String, required: true } },
methods: {
onChildSubmit(p) {
// Here will be some logic to save the param. Many different parents might have different logic and all of them use the same child component. So child-component contains list, form and validation message but does not know how to save the param to the database.
var error = SaveParent1({ form: { p: p, param1: this.param1 } });
if (error)
this.$refs.child.paramFailed(error);
else
this.$refs.child.paramAdded(p);
}
},
template: `<div class="parent"><p>Here is parent ONE:</p><child-component ref="child" #submit="onChildSubmit"></child-component></div>`
});
Vue.component('parent-component2', {
props: { param2: { type: String, required: true } },
methods: {
onChildSubmit(p) {
// Here is a different logic to save the param. In prictice it is gonna be different requests to the server.
var error = SaveParent2({ form: { p: p, param2: this.param2 } });
if (error)
this.$refs.child.paramFailed(error);
else
this.$refs.child.paramAdded(p);
}
},
template: `<div class="parent"><p>Here is parent TWO:</p><child-component ref="child" #submit="onChildSubmit"></child-component></div>`
});
Vue.component('child-component', {
data() {
return {
currentParam: "",
allParams: [],
errorMessage: ""
}
},
methods: {
submit() {
this.errorMessage = "";
this.$emit('submit', this.currentParam);
},
paramAdded(p) {
this.currentParam = "";
this.allParams.push(p);
},
paramFailed(msg) {
this.errorMessage = msg;
}
},
template: `<div><ol><li v-for="p in allParams">{{p}}</li></ol><label>Add Param: <input v-model="currentParam"></label><button #click="submit" :disabled="!currentParam">Submit</button><p class="error">{{errorMessage}}</p></div>`
});
function SaveParent1(data) {
// Axios API to save data. Bellow is a simulation.
if (Math.random() > 0.5)
return null;
else
return 'Parent1: You are not lucky today';
}
function SaveParent2(data) {
// Axios API to save data. Bellow is a simulation.
if (Math.random() > 0.5)
return null;
else
return 'Parent2: You are not lucky today';
}
new Vue({
el: "#app"
});
There is also a live demo available: https://jsfiddle.net/FairKing/novdmcxp/
Architecturally I recommend having a service that is completely abstract from the component hierarchy and that you can inject and use in each of the components. With this kind of component hierarchy and architecture it is easy to run into these issues. It is important to abstract as much functionality and business logic from the components as possible. I think of components in these modern frameworks just merely as HTML templates on steroids, which should at most act as controllers, keeping them as dumb and as thin as possible so that you don't run into these situations. I do not know vue.js so I cannot give you the technical solution but hope this indication helps
I think I have found a solution. So no two ways communication. I can just pass a method and the child will do everything without communicating with parent. I am happy with that I am marking it as an answer. Thanks everyone for your help.
Let me please know what do you think guys.
Bellow is my solution:
<div id="app">
<h2>😀Advanced Parent-Child Communication:</h2>
<parent-component1 param1="ABC"></parent-component1>
<parent-component2 param2="XYZ"></parent-component2>
</div>
Vue.component('parent-component1', {
props: { param1: { type: String, required: true } },
computed: {
saveFunc() {
return function(p) { SaveParent1({ form: { p: p, param1: this.param1 } }); }.bind(this);
}
},
template: `<div class="parent"><p>Here is parent ONE:</p><child-component :saveFunc="saveFunc"></child-component></div>`
});
Vue.component('parent-component2', {
props: { param2: { type: String, required: true } },
computed: {
saveFunc() {
return function(p) { SaveParent2({ form: { p: p, param2: this.param2 } }); }.bind(this);
}
},
template: `<div class="parent"><p>Here is parent TWO:</p><child-component :saveFunc="saveFunc"></child-component></div>`
});
Vue.component('child-component', {
props: {
saveFunc: { type: Function, required: true }, // This is gonna be a Promise in real life.
},
data() {
return {
currentParam: "",
allParams: [],
errorMessage: ""
}
},
methods: {
submit() {
this.errorMessage = "";
var error = this.saveFunc(this.currentParam);
if (error)
this.paramFailed(error);
else
this.paramAdded(this.currentParam);
},
paramAdded(p) {
this.currentParam = "";
this.allParams.push(p);
},
paramFailed(msg) {
this.errorMessage = msg;
}
},
template: `<div><ol><li v-for="p in allParams">{{p}}</li></ol><label>Add Param: <input v-model="currentParam"></label><button #click="submit" :disabled="!currentParam">Submit</button><p class="error">{{errorMessage}}</p></div>`
});
function SaveParent1(data) {
console.log(data);
// Axios API to save data
if (Math.random() > 0.5)
return null;
else
return 'Parent1: You are not lucky today';
}
function SaveParent2(data) {
console.log(data);
// Axios API to save data
if (Math.random() > 0.5)
return null;
else
return 'Parent2: You are not lucky today';
}
new Vue({
el: "#app"
});
The demo link: https://jsfiddle.net/FairKing/novdmcxp/126/

Using params on template helpers from Iron Router in Meteor

I'm using a router with this specific route:
Router.route('/organizations/:id', function () {
this.render('organizationDetail', {id: this.params.id});
});
This renders 'organizationDetail' template.
I have this helpers for that template:
if (Meteor.isClient) {
Template.organizationDetail.helpers({
organization: function() {
this.params.id????
return FindById(id);
}
});
}
How can I access id variable to ask for the correct object?
You need to put that parameter into your data context:
Router.route('/organizations/:id', function () {
this.render('organizationDetail', {
data: function() {
return {id: this.params.id};
}
});
});
Then you can use it straight from the this object, which holds the data context in helpers:
if (Meteor.isClient) {
Template.organizationDetail.helpers({
organization: function() {
return FindById(this.id);
}
});
}

Meteor Router.go not passing params

Snippet of code client-side:
Template.projectEditButton.events({
"click .edit": function() {
Router.go('projectForm', {prjId: this._id});
}
});
At this point, this._id is correct. In router file:
Router.route('/projects/form', {
name: 'projectForm',
data: function() {
return Projects.findOne(this.params.prjId);
}
});
this.params is empty. What am I missing?
You need to add the parameter to your route definition in order for the mapping to work:
Router.route('/projects/form/:prjId', {
name: 'projectForm',
data: function() {
return Projects.findOne(this.params.prjId);
}
});

Retrieving a display results from a search

I'm relatively new to meteor.js and I'm trying to get a search form to work. So far I'm not even trying to get the params to work, but it will come later.
I'm basically trying to get a bunch of lifts to display.
lib/router.js
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound',
waitOn: function() {
return Meteor.subscribe('lifts');
}
});
Router.route('/', { name: 'liftsList' });
Router.route('/lifts/search/:from-:to-:when', {
name: 'liftsSearch',
waitOn: function() {
return Meteor.subscribe('liftsSearch');
}
});
server/publications.js
Meteor.publish('liftsSearch', function() {
var query = { fromLoc: { $near : {
$geometry: {
type : "Point" ,
coordinates: [ 6.11667, 45.9 ]
} },
$maxDistance : 50
}};
return Lifts.find(query);
});
If I try to display the results with Lifts.find(query).fetch(), it returns actual results.
client/lifts_search.html
<template name="liftsSearch">
<div class="container">
<h3>Lifts search results {{hi}}</h3>
<div class="lifts">
{{#each lifts}}
hi
{{> liftItem}}
{{/each}}
</div>
</div>
</template>
Here I simply got no lifts displaying, not even the little "hi" string.
Thanks
Unless there's code that you haven't included, {{#each lifts}} isn't rendering because you're not defining lifts anywhere. Just because you're populating the Lifts collection, the template doesn't automatically known that lifts refers to it (largely because that would be totally arbitrary - what exact query would it refer to?).
So, you need to define lifts in either a router data function:
Router.route('/lifts/search/:from-:to-:when', {
name: 'liftsSearch',
waitOn: function() {
return Meteor.subscribe('liftsSearch');
},
data: function() {
return {
lifts: Lifts.find() // add a query if you want
}
}
});
Or in a template helper:
Template.liftsSearch.helpers({
lifts: function() {
return Lifts.find(); // add a query if you want
}
});

How can I repeat a resource in Ember.js

I have a page resource that uses the page title in the url.
App.Router.map(function () {
this.resource('page', {
path: '/:page_id'
});
});
App.PageRoute = Ember.Route.extend({
serialize: function(model) {
return { page_id: model.title};
}
});
That is working fine in this jsbin. However, I would like to have subpages nested in the url like this:
localhost/#/main_page/sub_page
I tried to make a sub resource (jsbin), but I'm not sure if it is the right approach.
App.Router.map(function () {
this.resource('page', {path: '/:page_id'},
this.resource('subpage', {path: '/:page_id/:subpage_id'}));
});
There are two main problems in my attempt: I have to repeat my page view and it doesn't retain the parent page in the url. I'm getting:
localhost/#/undefined/sub_page
Am I heading in the right direction? Can this be accomplished with just one resource?
Thanks in advance!
Have you considered nesting resources?
App.Router.map(function () {
this.resource('page', {path: '/:page_id'}, function(){
this.resource('subpage', {path: '/:subpage_id'});
 });
});
This would enable at least the URL structure you asked for, but i am not really sure about your requirements.
Most of the modifications I've made are in your html and template. Please, don't mash up the Handlebars' link-to helper with classic anchors () and don't change the link-to tagname attribute, or atleast think twice before doing so.
First - move the testData to a globally accessible object, so that you can use it in the menu:
Javascript:
App.CustomRoutes = [{
id: 1,
title: 'single'
}, {
id: 2,
title: 'subpages',
pages: [{
id: 3,
title: 'subpage1'
}, {
id: 4,
title: 'subpage2'
}, {
id: 5,
title: 'subpage3'
}]
}];
Html:
<ul class="navbar-nav nav">
{{#each page in App.CustomRoutes}}
<li class='menu-item'>
{{#link-to 'page' page.title}}
{{page.title}}
{{#if page.pages}}
<b class="caret"></b>
{{/if}}
{{/link-to}}
<span class='subpages'>
<ul>
{{#each subpage in page.pages}}
<li>
{{#link-to 'page.subpage' page.title subpage.title}}
{{subpage.title}}
{{/link-to}}
</li>
{{/each}}
</ul>
</span>
</li>
{{/each}}
</ul>
Then I fixed your router declaration. When you define nested routes, the third parameter to this.resource() is a function, much alike the function you pass to App.Router.map().
App.Router.map(function () {
this.resource('page', {path: '/:page_id'},
function() { this.route('subpage', {path: '/:subpage_id'});});
});
Finally, here are the definitions of your routes. Because Subpage is nested in Page, the route must be called PageSubpageRoute. If it was nested in Foo, it would have been FooSubpageRoute.
NOTE: The this.render() call inside a router, has the following parameters: (, ).
App.PageSubpageRoute = Ember.Route.extend({
model: function(params) {
var parentModel = this.modelFor('page');
var res = $.grep(parentModel.pages, function (e) {
if (e.title == params.subpage_id) {
return e;
}
});
return res[0];
},
serialize: function(model) {
return { subpage_id: model.title};
},
renderTemplate: function() {
this.render('subpage');
}
});
App.PageRoute = Ember.Route.extend({
serialize: function(model) {
return { page_id: model.title};
},
model: function (params) {
return $.grep(App.CustomRoutes, function (e) {
if (e.title == params.page_id) {
return e;
}
})[0];
}
});
Here is the code: jsbin

Categories

Resources