Meteor Template.myTemplate.helpers not rendering output - javascript

I am new to meteor and i am wondering why is it myTemplate.helper is not rendering its supposed output when i started putting these files in my /client/template directory. These are the following files:
/clinent/template/body.html:
<body>
<div class="row">
<div class="col-xs-12">
<div class="list-group">
{{#each projects}}
{{> projectList}}
{{/each}}
</div>
</div>
</div>
</body>
/client/template/body.js:
Project = new Mongo.Collection("project");
if (Meteor.isClient) {
Template.body.helpers({
projects: function(){
var projects = Project.find({});
return projects;
}
});
};
/client/template/templates.html:
<template name="projectList">
<a href="#" id="{{id}}" class="list-group-item {{#if active}} active {{/if}}">
{{name}}
<i class="glyphicon glyphicon-trash pull-right del"></i>
</a>
</template>
However it is rendering the ouptut properly when i put body.html and body.js at the root / directory.

I think I know what the problem is.
Project = new Mongo.Collection("project");
Should be available to both client and server, when you move body.js to client folder it is automatically served only to client, and it breaks the app.
Try following structure:
/client/template/body.js:
Template.body.helpers({
projects: function(){
var projects = Project.find({});
return projects;
}
});
/collections.js
Project = new Mongo.Collection("project");
Note that you don't need if(Meteor.isClient) when creating files inside client folder.

I think it will work if you use
Template.projectList.helpers
instead of
Template.body.helpers
When the helper is for a template then you have to put the template name and not body.

Related

Filter Events with collections

I have projects and workflows with separate collections.
collections:
Project = new Meteor.Collection("project");
Workflow = new Meteor.Collection("workflow");
After creating project, I am selecting project in the form and displaying Work flow card.
HTML:
<!-- Workflow Card -->
<div id="wCard">
{{#each workflow}}
**<div class="pheader">
<h2>{{project}}</h2>
<hr width="100%">
</div>**
<br>
<div class="workflowcard">
<div class="module-card">
<div class="card-header wfmodule">{{workflowTitle}}</div>
<div class="casting">
<div class="assigned-tag">Assigned To:</div>
<div class="assigned-to">{{team}}<hr></div>
<div class="actions">No Actions Created</div>
</div>
<div class="due">
Due on:
<div>
<div class="day-stamp" >{{weekday d_date}}</div>
<div class="date-stamp">{{date d_date}}</div>
<div class="month-stamp">{{month d_date}}</div>
</div>
</div>
</div>
<div class="btn-box showmuloption">
<button type="edit" class="editw" style="display:none">Edit Workflow</button>
<button type="hide" class="hidew" style="display:none">Hide Actions</button>
<div class="actionBtn"><button class="btn-wf stage-blue-wf button-x-small-wf" id="newAction">New Action</button></div>
</div>
</div>
{{/each}}
</div>
.JS:
Template.workflow.helpers({
getWorkflow: function(){
return Workflow.find();
},
user: function(){
return Meteor.users.find({});
},
getNewaction: function(){
return Newaction.find();
},
});
Now the workflow cards are displaying in a list. I want to display workflows according to projects. If I select a project, the workflow should go to that project, and I create another workflow with same project, it should display in that project. And if I select another project, the workflow should display in that related project.
Please help through this.
I use reactive varrible for this.
At first you need add meteor package:
$ meteor add reactive-var
Your form with project selecting might look like this:
<select id="project" value="{{getProject}}">
{{#each getProjectsList}}
<option id='{{_id}}' value="{{_id}}">{{projectname}}</option>
{{/each}}
</select>
Don't forget create helpers getProject and getProjectsList.
Now it's time for reactive varrible, you can initialise this in onCreated event.
Template.workflow.onCreated(function() {
var instance = this;
instance.projectId = new ReactiveVar(null);
});
When you change selected project, you should change you reactive varrible.
Template.workflow.events({
'change #project': function(){
Template.instance().projectId.set($("#project").val());
});
Every workflow need to have field with project id. You get workflows list like this
{{#each getWorkflows}}
And there is helper for getting workflow's list:
Template.workflow.helpers({
getWorkflows: function() {
var id = Template.instance().projectId.get();
return Workflow.find({projectId: id});
},
...// another helpers
})
So, when you will change project in form, list of workflow also will change, because it depends on reactive var. Also you can sibscribe only needed workflow object, and not all collection.
I hope this will helpfull to someone.

meteor display image with variable source

I'm using meteor.js and playing around with a simple application. I have a form on my page and once submitted the data is inserted into a database. I have a 'feed' of data on the left side of my page which updates when a new entry in the database is submitted, displaying the data. This is basic data at present about countries and populations etc. My question is how to mix a js variable to vary the source of an image file thats loaded in this 'feed' - hopefully explained below.
So, in code, I have this:
<template name="mainLeftCol">
<div class="col-sm-5">
{{> form}}
</div>
</template>
<template name="mainBody">
{{> mainLeftCol}}
<div class="col-sm-7">
{{#each dbEntry}}
{{> formItem}}
{{/each}}
</div>
</template>
and in a js file I have the event code for when the form is submitted:
Template.mainLeftCol.events({
'submit form': function(e) {
e.preventDefault()
var country = $(e.target).find('[id=country]').val()
prefix = country.slice(0,3);
if (prefix === 'Eng') {
console.log("Eng is for England");
}
var spot = {
country: country,
continent: $(e.target).find('[id=continent]').val(),
};
spot._id = Spots.insert(spot);
}
});
The formItems are being displayed using the following template which outputs the 'variables' that were input in the form:
<template name="formItem">
{{#Animate}}
<div class="panel panel-default spot animate">
<div class="panel-heading">
<h5 class="pull-right">{{country}}</h5>
<h4><mark><b>{{continent}}</b></mark></h4>
</div>
<div class="panel-body">
<img src="Eng.png" class="img-circle pull-right">
</div>
</div>
{{/Animate}}
</template>
As you can see in this template I have hardcoded "Eng.png" as the source for the image. What I would like to do is based on the prefix variable which slices the country field is to have the template display a different image (they're flags) based on the country on the template.
How can I mix a JS variable from my events code into the source of the image file in my template code?
Hope this makes sense!
I don't know if i got i right, but if you want to change the image (flag) according to the country try something like this:
Build a Template helper "prefix" which outputs the current country prefix.
Prefix helper:
Template.formItem.helpers({
countryPrefix: function () {
var country = $(e.target).find('[id=country]').val()
return country.slice(0,3);
}
});
In your Template you can now:
<div class="panel-body">
<img src="{{countryPrefix}}.png" class="img-circle pull-right">
</div>

How do I make {{#each}} work with these templates?

I have been reading these tutorials on Spacebars.
Understanding Spacebars by David Burles
Meteor's Spacebars README on Github
Discover Meteor's Spacebars secrets
I think I can understand pretty good, but I am having this problem
i have the main.html template like this
<template name="main">
<div class="container">
<section id="bl-work-section">
<div class="bl-box">
<h4 class="sin">Próximo</h4>
<h2 id="titulo1" class="sin"></h2>
<h4 class="sin" id="sub1"></h4>
</div>
<div class="bl-content">
<h2>Lunes 25 de Noviembre del 2014</h2>
{{#each mostrarLunes}}
{{> Lunes}}
{{/each}}
<a></a>
</div>
<span class="bl-icon bl-icon-close"></span>
</section>
<!-- Panel items for the works -->
<div class="bl-panel-items" id="bl-panel-work-items">
{{#each mostrarLunes}}
{{showPromos}}
{{/each}}
</div>
</div>
</div><!-- /container -->
</template>
So like you guys see im calling 2 templates inside main template and that 2 templates look like this
Lunes.html Template
<template name="Lunes">
<ul id="bl-work-items">
<li data-panel="panel">
<div class="oferta">
<h3>Promocion: {{metadata.nombrePromo}} </h3><br><small>Descripcion:{{metadata.descripcionPromo}}</small>
</div></li><br><br>
</ul>
</template>
showPromos.html Template
<template name="showPromos">
<div data-panel="panel">
<div>
<h1>Estoy viendo esto en la segunda pagina </h1>
<h3>Nombre Promo {{metadata.nombrePromo}}</h3>
<p>Descripcion promo.{{metadata.descripcionPromo}}</p>
</div>
</div>
<nav>
<span class="bl-icon bl-icon-close"></span>
</nav>
</template>
So what is the problem? well if we look at the template Lunes and showPromos, i have a Data-Panel="panel", but its seems like that data panel don't work when i wrap that value on the {{each}} tags, because if i use, that {{each}} tags outside the data-panel selector it works, so its seems like its not working seems like they don't have connection.
so im asking if there is a way to connect that value? already try helper attributes like the 3rd link says, and don't work, the attributes helpers look like this
Template.main.helpers({
attributes: function () {
return {
dataPanel: "prueba",
}
}
});
Helper mostrarLunes
Template.main.helpers({
'mostrarLunes' : function(){
return Promociones.find({'metadata.diaOferta' : { $in: ['Lunes'] } });
}
});
Looks like the data context isn't set. Try creating a template helper for your other templates with the data (I don't understand Spanish so I'm assuming this is the MongoDB cursor you need...
Templates.lunes.helpers({
data: function() {
return Promociones.find({'metadata.diaOferta' : { $in: ['Lunes'] } });
}
});
Templates.showPromos.helpers({
data: function() {
return Promociones.find({'metadata.diaOferta' : { $in: ['Lunes'] } });
}
});

Why can't I change this emberjs model without an error?

I'm writing an ember app that has this settings model that I'm using to bypass routing because I want a single url. However, I get this error:
Error: Cannot perform operations on a Metamorph that is not in the DOM.
when I change any of the data in the model. I'm relatively new to ember, but from what I read of what's out there, you should be able to change a model just fine. Here's my html file:
<script type="text/x-handlebars" data-template-name="index">
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="#"><img src="./img/MySpending.png"></a>
<div class="nav-collapse">
<ul id="navWrapper" class="nav">
{{#if model.isIndex}}
{{partial "indexNav"}}
{{/if}}
{{#if model.isMonthly}}
{{partial "monthlyNav"}}
{{/if}}
{{#if model.isYearly}}
{{partial "yearlyNav"}}
{{/if}}
</ul>
</div>
<div class='navbar_form pull-right'></div>
</div>
</div>
</div>
<div id="bodyWrapper" class="container" style="padding-top:60px;">
{{#if model.isIndex}}
{{partial "indexPage"}}
{{/if}}
{{#if model.isMonthly}}
{{partial "monthlyPage"}}
{{/if}}
{{#if model.isYearly}}
{{partial "yearlyPage"}}
{{/if}}
</div>
{{outlet}}
And each partial works fine initially.
It's when I change it in the three regular functions in my app.js that I have a problem. Here's my app.js:
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.settings=Ember.Object.extend({
isIndex: true,
isMonthly: false,
isYearly: false
});
Settings=App.settings.create();
//App.SettingsController = Ember.ObjectController.extend(Settings); Not sure whether to put this in or not
App.IndexRoute = Ember.Route.extend({
model: function() {
return Settings;
}
});
function goToMonthly(){
Settings.set('isIndex',false);
Settings.set('isMonthly',true);
Settings.set('isYearly',false);
}
function goToYearly(){
Settings.set('isIndex',false);
Settings.set('isMonthly',false);
Settings.set('isYearly',true);
}
function goToIndex(){
Settings.set('isIndex',true);
Settings.set('isMonthly',false);
Settings.set('isYearly',false);
}
So I'm not sure where the error is but if anyone can help,it'd be extremely appreciated.
There's a fundamental error in your approach here. Your Route/Controller are meant to handle the model for you, so by declaring your model outside of these objects, you're making more work for yourself and leading yourself to potential trouble.
I'm not seeing where your "goTo" functions are being called (I assume they're inside the partials, but if they're in that HTML and I missed it, that's my bad). But here's how they should be referenced:
App.SettingsController = Ember.ObjectController.extend({
actions:{
goToMonthly:function(){
this.set('isIndex', false);
this.set('isMonthly', true);
this.set('isYearly', false);
},
//Other functions go here
}
});
Then within your html, call these actions using handlebars:
<a {{action 'goToMonthly'}}> Click this to run the action </a>
Which ember version are you using? I tried it with ember 1.2 and 1.3 and your example is working fine.

How can I apply KnockoutJS on a master page and an individual page?

I'm stuck on an older WebForms project and I'd like to know if there's a recommended approach for my scenario.
Goal
I have a feedback form in a modal dialog that I bound up using KnockoutJS.
I would like the feedback form to be available on all pages, via a link in the footer of the site.
I would like to have several other pages using knockout as well with their own individual scripts & bindings, irrespective of the feedback form bindings in the modal.
I have some pages that do not use knockout at all. I would like them not to have to insert code to accomplish this.
I would like to avoid global variables, if possible, in favor of namespaced JavaScript.
In essence, I would like for the viewmodels on the page and the feedback viewmodel not to be aware of each others' existence.
Current Setup
Our footer links are in a Site.master file, and so that's where I've placed the Feedback.js script and the div for the modal which has the bindings. So on the master page, I call ko.applyBindings(vm, referenceToFeedbackDiv), which works fine to wire up the feedback form.
Our individual pages occasionally have a knockout viewmodel, and so they may call ko.applyBindings(vm), since to their knowledge they'd like to apply the vm to their entire page.
Problem
This causes a conflict in knockout because one vm is being applied to the feedback form via the Site.master call, and one vm is being applied to the entire body by the page after it.
Question
How can I enable these two things -- a modal dialog across all pages that uses knockout, and individual knockout pages -- to work in harmony?
Demonstration of the Issue in (the Current) Code
Remember, the issue is that I want to be able to have one feedback VM that applies only to the feedback div across the client site, and I want to have other VMs able to be applied that aren't required to know anything about the feedback vm.
Master Page file (Site.Master) -- Excerpt
This is on every page:
<div class="page">
<div class="main">
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
</div>
<div class="footer">
© <%=DateTime.Now.Year.ToString() %> Company, Inc. | Home | About |
<!-- begin feedback area -->
<span id="FeedbackArea">
<a data-bind="click: showModal">Feedback</a>
<div id="feedback-modal" title="What's on your mind?">
<div class="btn-group" id="feedbackButtonGroup">
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-warning fa-2x fa-align-center"></i>
<br />
<span>Problem</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-question-circle fa-2x fa-align-center"></i>
<br />
<span>Question</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-lightbulb-o fa-2x fa-align-center"></i>
<br />
<span>Suggestion</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-thumbs-o-up fa-2x fa-align-center"></i>
<br />
<span>Praise</span>
</button>
<button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
<i class="fa fa-info-circle fa-2x fa-align-center"></i>
<br />
<span>General</span>
</button>
</div>
<br />
<br />
<textarea rows="5" placeholder="Enter feedback here" data-bind="value: feedbackText, valueUpdate: 'afterkeydown'"></textarea>
<br />
<br />
<button>Send Feedback</button>
<button data-bind="click: CancelFeedback">Cancel</button>
<h3>Other Information: </h3>
<ul>
<li><strong>Feedback Type:</strong> <span data-bind="text: feedbackType"></span></li>
<li><strong>Current URL:</strong> <span data-bind="text: pageUserIsOn"></span></li>
<li><strong>Current User: </strong><%=hdnLoggedInUsername.Value %></li>
<li><strong>Current Client: </strong>[Not yet captured]</li>
<li><strong>Current Tab: </strong>[Not yet captured]</li>
</ul>
</div>
</span>
<!-- End feedback area -->
</div>
Feedback.JS -- This is also included in every page
...a somewhat-namespaced definition of a FeedbackVM:
var FeedbackNamespace = FeedbackNamespace || {};
..the definition of the namespace itself:
FeedbackNamespace = {
ViewModel: function () {
// etc. etc.
}
};
...and the declaration of a VM variable plus wiring it up on document.ready():
var FeedbackVM;
$(document).ready(function () {
FeedbackVM = new FeedbackNamespace.ViewModel();
ko.applyBindings(FeedbackVM, $('#FeedbackArea')[0]);
FeedbackVM.Start();
log('FeedbackVM started');
});
Other Pages without Knockout / JS
Other pages may or may not have any javascript on them at all, let alone knockout. On these pages, the FeedbackVM currently works fine.
Pages with their own Knockout ViewModel
These pages would have their own namespaced JS file with their own document.ready() event, that creates a vm of say invoiceUploaderVM = new InvoiceUploader.ViewModel(), and then calls ko.applyBindings(invoiceUploaderVM).
This is where we run into trouble.
Update: One potential Approach and a little trouble
In the Site.master page, I wrapped my entire footer in a "stopBindings: true" div:
<div data-bind="stopBindings: true">
<div class="footer" id="footerDiv">
<!-- Feedback Viewmodel stuff in here -->
</div>
</div>
I've defined stopBindings as:
ko.bindingHandlers.stopBindings = {
init: function () {
return { controlsDescendantBindings: true };
}
};
My Feedback.js file, loaded on every page as part of a global JS file, has:
var FeedbackNamespace = FeedbackNamespace || {};
FeedbackNamespace = {
// defines viewmodel, etc. etc.
};
var FeedbackVM;
$(document).ready(function () {
FeedbackVM = new FeedbackNamespace.ViewModel();
ko.applyBindings(FeedbackVM, $('#footerDiv')[0]);
FeedbackVM.Start();
log('FeedbackVM started');
});
This approach works perfectly well -- as long as there are no other viewmodels being bound. On the pages that inherit from my master page, I might have something like:
$(document).ready(function () {
'use strict';
vm = new invoiceUploader.ViewModel();
ko.applyBindings(vm);
});
I would expect that this:
Sets up the feedback viewmodel applied to the div, stopping other viewmodels
Sets up the invoiceUploader viewmodel and applies it to the body (which is then stopped by the stopBindings div)
However, instead I get an error upon loading the child page along the lines of:
Commenting the line to apply the feedback bindings makes this work just fine again.
What am I doing wrong?
I think I would put the view model for your modal in a global object and do whatever you need to do with it aside from applying the bindings in a shared script:
window.feedbackModal = {
foo: ko.observable("Whatever you need to do here"),
bar: ko.observable("assuming it can be done the same on every page")
};
Then in the Site.master
<div class="feedback-modal" data-bind="with: feedbackModal">
<p data-bind="text: foo"></p>
<p data-bind="text: bar"></p>
</div>
And in every individual page's script:
function ViewModel() {
this.individualProperty = ko.observable(true);
this.specificAction = function() { /* do something specific to this page */ };
this.feedbackModal = window.feedbackModal;
}
ko.applyBindings(new ViewModel());
So window.feedbackModal could be undefined and it won't cause you problems, but if you ko.applyBindings, you have to have a feedbackModal property exposed in the view model or you'll get errors applying those bindings.
Of course, there are more clever ways you could implement this basic idea in order to fit your patterns the best, but the big point is, as you know, you can't apply bindings twice, so you need to defer that task to your most specific code and expose your reusable code to to it.
Here is another strategy for separation of common modules from page dependant modules:
// An example of a module that runs on everypage
var modalDialog = function(){
this.name = "dialog1";
this.title = ko.observable("My Modal Title");
this.content = ko.observable("My Modal content is also something");
}
// An example of a module that runs on everypage
var modalDialog2 = function(){
this.name = "dialog2";
this.title = ko.observable("My Modal Title 2");
this.content = ko.observable("My Modal content is also something 2");
}
// Either generate it automatically or by hand
// to represent which modules are common
var commonModules = [modalDialog, modalDialog2];
// An example of a module only for this page
var pageModule = function(){
this.pageFunction = function(){
alert("Called page function");
}
}
// Composition is the final object you will actually bind to the page
var composition = {
pageMod: new pageModule()
}
// Let's add the common modules to the composition
ko.utils.arrayForEach(commonModules, function(item){
var module = new item();
composition[module.name] = module;
});
// Bind the composition
ko.applyBindings(composition);
example HTML for this would be:
<div class="modalDialog">
<h2 data-bind="text: dialog1.title"><h2>
<h2 data-bind="text: dialog1.content"><h2>
</div>
<div class="modalDialog">
<h2 data-bind="text: dialog2.title"><h2>
<h2 data-bind="text: dialog2.content"><h2>
</div>
<div id="content">
<h2>Welcome to page</h2>
<div id="somePageStuff">
Click me
</div>
</div>
Link to the jsfille for this
You can set this up by using a technique to not have scope your bindings to a specific area in your page.
Check out: How to stop knockout.js bindings evaluating on child elements
Example:
http://jsfiddle.net/anAgent/RfM2R/
HTML
<div id="Main">
<label data-bind="text: ViewModel.Name">default</label>
<div data-bind="stopBindings: true">
<div id="ChildBinding">
<label data-bind="text: AnotherViewModel.Name">default</label>
</div>
</div>
</div>
JavaScript
$(function () {
ko.bindingHandlers.stopBindings = {
init: function () {
return {
controlsDescendantBindings: true
};
}
};
var data = {
ViewModel: {
Name: "Testing"
}
};
var data2 = {
AnotherViewModel: {
Name: "More Testing"
}
};
ko.applyBindings(data, $("#Main")[0]);
ko.applyBindings(data2, $("#MyModalHtml")[0]);
});

Categories

Resources