I would like to use the name of a meteor template from inside:
<template name="blaModal">
<div class="modal fade" id="{{templateName}}">
</div>
</template>
How can I do this?
You can create a global helper, that resolves the current Template's instance and returns it's view-name (with Template. prefix removed):
/imports/startup/client/helpers.js
Template.registerHelper('templateName', function () {
const instance = Template.instance()
const { view } = instance
const { name } = view
return name.replace('Template.', '')
})
Related
Following code, is a very simple Firebase - VueJS app, (codeSandBox demo)
app.vue
<template>
<div class="container">
<!-- Adding Quote -->
<add-quote/>
<!-- Display Quotes -->
<quote-list/>
</div>
</template>
<script>
import addQuote from "./components/AddQuote.vue";
import quoteList from "./components/QuoteList.vue";
export default {
components: {
addQuote,
quoteList
},
methods: {
get_allQuotes: function() {
// var vm = this;
var localArr = [];
quotesRef
.once("value", function(snapshot) {
snapshot.forEach(function(snap) {
localArr.push({
key: snap.key,
category: snap.val().category,
quoteTxt: snap.val().quoteTxt
});
});
})
.then(data => {
this.$store.commit("set_allQuotes", localArr);
});
}
},
mounted() {
this.get_allQuotes();
console.log("App: mounted fired");
}
};
</script>
store.js(vuex store)
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
quotesList: []
},
getters: {
get_quotesList(state) {
return state.quotesList;
}
},
mutations: {
set_allQuotes(state, value) {
state.quotesList = value;
}
}
});
AddQuote.vue
<template>
<div class="row quote-edit-wrapper">
<div class="col-xs-6">
<textarea v-model.lazy="newQuoteTxt"
rows="4"
cols="50"></textarea>
<button #click="addQuote">Add Quote</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
newQuoteTxt: '',
}
},
computed: {
allQuotes() {
return this.$store.getters.get_quotesList;
},
newQuoteIdx() {
var localArr = [...this.allQuotes]
if(localArr.length > 0) {
var highestKEY, currKEY
localArr.forEach((element, idx) => {
currKEY = parseInt(element.key)
if(idx == 0) {
highestKEY = currKEY
} else {
if(highestKEY < currKEY) {
highestKEY = currKEY
}
}
})
return highestKEY + 1
} else {
return 1
}
}
},
methods: {
// ADD new Quote in DB
addQuote: function() {
var vm = this
var localArr = [...this.allQuotes]
//1. First attach 'value' event listener,
// Snapshot will contain data from that ref
// when any child node is added/updated/delete
quotesRef.on('value', function (snapshot) {
snapshot.forEach(function(snap) {
var itemExists = localArr.some(function (item, idx) {
return item.key == snap.key
})
// If newly added item doesn't yet exists then add to local array
if (!(itemExists)) {
localArr.push({
key: snap.key,
category: snap.val().category,
quoteTxt: snap.val().quoteTxt })
vm.$store.commit('set_allQuotes', localArr)
}
})
})
//2. Second set/create a new quotes in Firebase,
// When this quote gets added in Firebase,
// value event (attached earlier) gets fired
// with
var newQuoteRef = quotesRef.child(this.newQuoteIdx)
newQuoteRef.set({
category: 'motivation',
quoteTxt: this.newQuoteTxt
})
}
}
}
</script>
quoteList.vue
<template>
<div class="row">
<div class="col-xs-12 quotes-list-wrapper">
<template v-for="(quote,idx) in allQuotes">
<!-- Quote block -->
<div class="quote-block-item">
<p class="quote-txt"> {{quote.quoteTxt}} </p>
</div>
</template>
</div>
</div>
</template>
<script>
export default {
computed: {
allQuotes() {
return this.$store.getters.get_quotesList;
}
}
}
</script>
Note: The main code of concern is of addQuote.vue
User enter newQuoteTxt that gets added to Firebase (addQuote()) as a quote item under quotesRef. As soon as quote is added (on firebase), Firebase client side SDK's value event fires, and adds the new quote (via callback) to localArray (allQuotes). VueJS then updates the DOM with newly added Quote.
The addQuote() method works in the following manner:
First, attach a callback/listener to 'value' event on quotesRef
quotesRef.on('value', function (snapshot) {
....
})
Next, A firebase ref (child of quotesRef) is created with a ID this.newQuoteIdx
var newQuoteRef = quotesRef.child(this.newQuoteIdx)
Then set() is called (on this newly created Ref) adding newquote to firebase RealTime DB.
value event gets triggered (attached from step 1) and listener /callback is called.
The callback looks for this new quote's key in existing list of items by matching keys of localArr and snap.key, if not found, adds the newly quote to localArr. localArr commits to a vuex store.
`vm.$store.commit('set_allQuotes', localArr)`
VueX then updates all subscriber component of this array. VueJS then adds the new quote to the existing list of quotes (updates the DOM)
While debugging the addQuote method, the problem I notice, the execution/flow of script (via F8 in chrome debugger) first steps into the listener/callback attached to value event before the code newQuoteRef.set({ ... }) that adds new quote (on firebase), which in turn will cause 'value' event to trigger.
I am not sure why this occurs. Can anybuddy explain why the listener/callback is called before the quotes is created.
Are child nodes (of QuotesRef) are cached at clientside such that 'value' fires even before new quote is added.
Thanks
If I correctly understand your question (Your code is not extremely easy to follow! :-)) it is the normal behaviour. As explained in the documentation:
The value event will trigger once with the initial data stored at
this location, and then trigger again each time the data
changes.
Your sandbox demo does not actually shows how the app works, but normally you should not set-up the listener in the method that saves a new node to the database. These two things should be decoupled.
One common approach is to set the listener in the created hook of a component (see https://v2.vuejs.org/v2/guide/instance.html#Instance-Lifecycle-Hooks and https://v2.vuejs.org/v2/api/#created) and then in your addQuote method you just write to the database. As soon as you write, the listener will be fired.
How can I use reactive template variables (from Template.data) in an anonymous function within the template rendered function? (I want to keep it reactive).
Template.templateName.rendered = function() {
function testFunction(){
//Log the field 'title' associated with the current template
console.log(this.data.title);
}
});
Not sure exactly what you are trying to do (like printing this.data.title whenever it changes?), but you should:
use a Reactive variable (add reactive-var package, then create a var myVar = new ReactiveVar()
If necessary, wrap your function with Tracker.autorun (or this.autorun in a template creation / rendered event).
So you could have like:
Parent template HTML:
<template name="parentTemplateName">
{{> templateName title=myReactiveVar}}
</template>
Parent template JS:
Template.parentTemplateName.helpers({
myReactiveVar: function () {
return new ReactiveVar("My Title!");
}
});
Template JS:
Template.templateName.onRendered(function() {
// Re-run whenever a ReactiveVar in the function block changes.
this.autorun(function () {
//Print the field 'title' associated with the current template
console.log(getValue(this.data.title));
});
});
function getValue(variable) {
return (variable instanceof ReactiveVar) ? variable.get() : variable;
}
What worked for me was simple using autorun() AND using Template.currentData() to grab the values from within autorun():
let title;
Template.templateName.rendered = function() {
this.autorun(function(){
title = Template.currentData().title;
});
function testFunction(){
console.log(title);
}
});
Template.templateName.onRendered(function(){
console.log(this.data.title);
});
I'm using Flow Router and Blaze Renderer for a simple website (think blog / brochureware).
I'm using FlowRouter.path() to create links on my menu elements. The url changes as expected when these links are clicked and the action() method on the route is fired. However the templates don't seem to be refreshed and the template helpers aren't fired.
The route in my /lib/route.js file is
const about = FlowRouter.group({
prefix: '/about'
});
about.route('/:pageSlug/:subSectionSlug', {
action: (params) => {
console.log('action() fired :' + params.pageSlug + ' :' + params.subSectionSlug);
BlazeLayout.render( 'applicationLayout', {
main: 'basicPage',
});
},
name: 'pageSubsection',
});
Then my templates looks like -
<template name="applicationLayout">
{{> Template.dynamic template=main}}
</template>
<template name="basicPage">
<div id="pageWrapper">
...
<aside class="leftBar subSectionMenu">
{{> sidebar }}
</aside>
...
</div>
</template>
<template name="sidebar">
<ul class="pageMenu">
{{#each subSections }}
{{> sidebarItem}}
{{/each}}
</ul>
</template>
<template name="sidebarItem">
<a class="sidebarItemAnchor" href="{{ href }}">
<li class="sidebarItem .hoverItem {{#if isSelected }} selected {{/if}}">
{{title}}
<span class="sidebarArrow"></span>
</li>
</a>
</template>
With a simple helper to add the selected class to the li element -
Template.sidebarItem.helpers({
href() {
const subSection = this;
const params = {
pageSlug: subSection.pageSlug,
subSectionSlug: subSection.slug,
}
return FlowRouter.path('pageSubsection', params, {});
},
isSelected() {
const slug = FlowRouter.current().params.subSectionSlug;
console.log('running isSelected with ' + slug);
if (this.slug === slug) {
return true;
} else {
return false;
}
}
});
I think I must be misunderstanding how (and when) templates are rendered.
What do I need to do to re-render these templates when the route changes?
Flow Router was designed to work like this. It doesn't automatically re-render.
A simple fix is to add FlowRouter.watchPathChange(); into all Template helpers that depend on the params of a route.
So in this case update the sidebar.js -
Template.sidebarItem.helpers({
isSelected() {
FlowRouter.watchPathChange();
const slug = FlowRouter.current().params.subSectionSlug;
if (this.slug === slug) {
return true;
} else {
return false;
}
},
});
When that helper is used in the sidebarItem template it is now updated whenever the path changes.
I'm new in meteor.js and i'm practising building a simple and little app. My question is how to use the template data get from mongodb in templates inside. This is the structure:
router.js
Router.route('/point/:_id', {name: 'pointDetail', data: function() {
return InterPoints.findOne(this.params._id);
} });
template pointDetail.html
<template name="pointDetail">
<some tags>
{{datathree}}
</some tags>
{{> map}}
</template>
template map.html
<template name="map">
<div class="map-container">
{{> googleMap name="exampleMap" options=exampleMapOptions}}
</div>
</template>
map.js
Template.map.helpers({
exampleMapOptions: function() {
// Make sure the maps API has loaded
if (GoogleMaps.loaded()) {
// Map initialization options
return {
center: new google.maps.LatLng(coordX, coordY),
zoom: 8
};
}
}
});
I want to fill coorsx and coordy in map.js using the data I'm retrieving from the router.
Someting like coordx = {{coordsx}}
It is possible?
Thanks a lot.
You can retrieve a parent template's data context through Blaze.currentView.parentView.templateInstance(). Assuming your Interpoints document has properties coordx and coordy:
Template.map.helpers({
exampleMapOptions: function() {
// Make sure the maps API has loaded
if (GoogleMaps.loaded()) {
// Map initialization options
var parentTemplate = Blaze.currentView.parentView.templateInstance();
var coordX = parentTemplate.data.coordx;
var coordY = parentTemplate.data.coordy;
return {
center: new google.maps.LatLng(coordX, coordY),
zoom: 8
};
}
}
});
I have a Meteor template in HTML-file:
<template name='main'>
</template>
I rendered it using Iron router:
Router.route('/', function () {
this.render('main');
});
Now I want to render another template to replace 'main' template. How to do it?
Obviously you do not want another route?
If not you can use a reactive var in the router. When you change the variable it will run again and render your other template.
See http://eventedmind.github.io/iron-router/#hooks
var OnBeforeActions;
OnBeforeActions = {
whichMain: function() {
if (reactiveVar) {
this.render('otherMain');
}
else
this.next() ;
}
};
Router.onBeforeAction(OnBeforeActions.whichMain, {
only: ['Main']
});
Alternatively use a dynamic template inside your main router.
https://www.discovermeteor.com/blog/blaze-dynamic-template-includes/