Meteor Template Events - meteor

I'm trying to get a hold of meteor still so there might be an easy answer to this and i'm hoping that is the case. I have this function which works and returns the correct id when my button is clicked.
$(document).ready(function(){
$("button").click(function(){
var selection = (this.id);
boardSpecs[0] = selection;
return boardSpecs;
});
});
I want to make this into a meteor click event, something like this.
Template.selectBoard.events({
'click button' : function (event) {
event.preventDefault();
var boardType = event.target.id;
Session.set('boardType', boardType);
alert(boardType);
}
});
This is the template where the button exists.
<template name = "selectBoard">
<div class = "container">
<div class = "boardCarousel">
{{#each boardList}}
<div class = "span1">
<div class = "thumbnail">
<img data-src = "{{source}}" alt = "placeholder" class = "img-rounded">
<div class = "something">
<h2>{{name}}</h2>
<p>{{description}}</p>
<button type = "button" id = "{{id}}" class = "btn btn-primary">Select</button>
</div>
</div>
</div>
{{/each}}
</div>
</div>

Assuming that the button is part of your template, you're code is nearly right. The only different is that this won't point to your button, so you'll need to get it from the event:
Template.selectBoard.events({
'click button' : function (event) {
event.preventDefault();
var boardType = event.target.id;
Session.set('boardType', boardType);
alert(boardType);
}
});

Let's make this easier. Your button is defined as:
<button type = "button" id = "{{id}}" class = "btn btn-primary">Select</button>
And your event handler is trying to get at the id of the button which is {{id}}.
If you use nested templates as follows:
<template name = "selectBoard">
<div class = "container">
<div class = "boardCarousel">
{{#each boardList}}
{{> board}}
{{/each}}
</div>
</div>
</template>
<template name="board">
<div class = "span1">
<div class = "thumbnail">
<img data-src = "{{source}}" alt = "placeholder" class = "img-rounded">
<div class = "something">
<h2>{{name}}</h2>
<p>{{description}}</p>
<button type = "button" class = "btn btn-primary">Select</button>
</div>
</div>
</div>
</template>
Then this in your event handler will be the data context of the individual board and you can simply write:
Template.selectBoard.events({
'click button' : function (event) {
event.preventDefault();
var boardType = this.id;
Session.set('boardType', boardType);
alert(boardType);
}
});
I'd argue that this is more Meteoric (to borrow an adjective from Python).
I'd also avoid using the variable name id because of the potential confusion with the natural MongoDB document identifier _id.

I ended up using a body event and it worked right away. Not sure why but it did.
Template.body.events({
'click #selected' : function(event){
event.preventDefault();
Session.set('board',event.target.id);
}
});

Related

How can I route my messenger and messages in Meteor?

Im creating a an instant messenger app and im having a little trouble routing it. So, once you go into the app. There is a list of available users. You can click on a user and start chatting. The issue I have is once I click send, the console show an Uncaught TypeError: Cannot read property 'value' of undefined. Im not sure what im doing wrong here. Also I need help show the chat messages sent above. As if you can see the recent and previous messages. Here are my codes. Any examples and helps would be great.
HTML
minstant
<body>
</body>
<!-- this is the main template used by iron:router to build the page -->
<template name="ApplicationLayout">
{{> yield "header"}}
<div class="container">
{{> yield "main"}}
</div>
</template>
<!-- top level template for the nav bar -->
<template name="navbar">
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="/">
Minstant!
</a>
</div>
<div class="nav navbar-nav">
{{> loginButtons}}
</div>
</div>
</nav>
</template>
<!-- Top level template for the lobby page -->
<template name="lobby_page">
{{> available_user_list}}
</template>
<!-- display a list of users -->
<template name="available_user_list">
<h2>Choose someone to chat with:</h2>
<div class="row">
{{#each users}}
{{> available_user}}
{{/each}}
</div>
</template>
<!-- display an individual user -->
<template name="available_user">
<div class="col-md-2">
<div class="user_avatar">
{{#if isMyUser _id}}
<div class="user_avatar">{{getUsername _id}} (YOU)
<br/>
<img src="/{{profile.avatar}}" class="avatar_img">
</div>
{{else}}
<a href="/chat/{{_id}}">
{{getUsername _id}}
<br/>
<img src="/{{profile.avatar}}" class="avatar_img">
</a>
{{/if}}
</div>
</div>
</template>
<!-- Top level template for the chat page -->
<template name="chat_page">
<h2>Type in the box below to send a message!</h2>
<div class="row">
<div class="col-md-12">
<div class="well well-lg">
{{#each recentMessages}}
{{> message}}
{{/each}}
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<form class="new-message">
<input class="input" type="text" name="chat" placeholder="type a message here...">
<button class="btn btn-default">Send</button>
</form>
</div>
</div>
</template>
<!-- simple template that displays a message -->
<template name="message">
<div class = "container">
<div class = "row">
<div class = "username">{{username}}
<img src="/{{profile.avatar}}" class="avatar_img">
</div>
<div class = "message-text"> said: {{messageText}}</div>
</div>
</div>
</template>
Here is my JS
Messages = new Mongo.Collection("messages");
if (Meteor.isClient) {
Meteor.subscribe("messages");
Meteor.subscribe("userStatus");
// set up the main template the the router will use to build pages
Router.configure({
layoutTemplate: 'ApplicationLayout'
});
// specify the top level route, the page users see when they arrive at the site
Router.route('/', function () {
console.log("rendering root /");
this.render("navbar", {to:"header"});
this.render("lobby_page", {to:"main"});
});
// specify a route that allows the current user to chat to another users
Router.route('/chat/:_id', function () {
this.render("navbar", {to:"header"});
this.render("chat_page", {to:"main"});
});
///
// helper functions
///
Template.available_user_list.helpers({
users:function(){
return Meteor.users.find();
}
})
Template.available_user.helpers({
getUsername:function(userId){
user = Meteor.users.findOne({_id:userId});
return user.profile.username;
},
isMyUser:function(userId){
if (userId == Meteor.userId()){
return true;
}
else {
return false;
}
}
})
Template.chat_page.helpers({
recentMessages: function () {
return Messages.find({}, {sort: {createdAt: 1}});
return Meteor.users.find();
},
});
Template.chat_page.events({
// this event fires when the user sends a message on the chat page
'submit .new-message':function(event){
event.preventDefault();
var text= event.target.text.value;
// stop the form from triggering a page reload
event.target.text.value = "";
// see if we can find a chat object in the database
// to which we'll add the message
Meteor.call("SendMessage", text);
},
});
};
Meteor.methods({
sendMessage: function (messageText) {
if (! Meteor.userId()) {
throw new Meteor.Error("not-authorized");
}
Messages.insert({
messageText: messageText,
createdAt: new Date(),
username: Meteor.user().username
});
}
});
// start up script that creates some users for testing
// users have the username 'user1#test.com' .. 'user8#test.com'
// and the password test123
if (Meteor.isServer) {
Meteor.startup(function () {
if (!Meteor.users.findOne()){
for (var i=1;i<9;i++){
var email = "user"+i+"#test.com";
var username = "user"+i;
var avatar = "ava"+i+".png"
console.log("creating a user with password 'test123' and username/ email: "+email);
Meteor.users.insert({profile:{username:username, avatar:avatar}, emails: [{address:email}],services:{ password:{"bcrypt" : "$2a$10$I3erQ084OiyILTv8ybtQ4ON6wusgPbMZ6.P33zzSDei.BbDL.Q4EO"}}});
}
}
},
),
Meteor.publish("messages", function () {
return Messages.find();
});
Meteor.publish("userStatus", function() {
return Meteor.users.find({ "status.online": true });
});
};
The error is with your form submit code. In the console you can see the error is in Template.chat_page.events.submit .new-message on line 73. That will take you right to the error code:
'submit .new-message':function(event){
event.preventDefault();
var text = event.target.text.value;
// stop the form from triggering a page reload
event.target.text.value = "";
}
event.target.text.value doesn't exist. event.target does, but there is no field for text. There is textContent, and you can access the children of the target (which in this case is the form). Put in a console.log(event); and figure out what you are trying to access within the javascript console and then use that to determine what your code should look like. This might work for you:
'submit .new-message':function(event){
event.preventDefault();
var text = event.target.elements.chat.value;
// stop the form from triggering a page reload
event.target.text.value = "";
}
event.target.elements.chat.value comes from the name field of the <input>.

Meteor Blaze.renderWithData how to InsertAfter is it possible?

I'm having a problem to insert a template after and not before a node. For example:
//Html looks like this
<div class="questions">
<div class="question"></div>
<div class="question"></div>
<div class="question"></div>
</div>
<template name="question">
<div class="question"></div>
</div>
<template name="questionExtraInfo">
<div class="extra"></div>
</template>
I'm trying to get the following:
<div class="questions">
<div class="question"></div>
<div class="extra"></div>
<div class="question"></div>
<div class="question"></div>
</div>
Calling blaze render inside question event
Template.question.events({
'click .more-details': function () {
var instance = Template.instance();
Blaze.renderWithData(Template.questionExtraInfo, {}, document.querySelector('.questions'), instance.find('.question')));
});
I can only figure out how render it before or inside how about after?
<div class="extra"></div>
<div class="question"></div>
<div class="question"><div class="extra"></div></div>
I think a better approach would be to take advantage of reactivity:
Change your questions template to:
<template name="question">
<div class="question"></div>
{{# if shouldIncludeExtra }}
{{> questionExtraInfo }}
{{/if}}
</template>
The above template should be inside an each loop.
Then in your js something like:
Template.question.helpers({
'shouldIncludeExtra': function() {
// replace 'n' with the actual index. I think `this.index` is
// provided within #each blocks, or you can use the new `#each` helper.
var index = n;
return Session.get('shouldIncludeExtra' + index);
}
});
Then, in your click event, you set a session var based on the index to true:
Template.questions.events({
'click .question': function(e, tpl) {
var question = e.currentTarget;
// You can probably come up with something better here..
var index = $(question).parent().find('> .question').index(question);
Session.set('shouldIncludeExtra' + index, true);
}
});
Because of reactivity, you would see the inserts right away when you fire the click event.
I realize this doesn't really answer the headline of your question, but it should get you the desired outcome.

Ui-bootstrap-modal with ui-bootstrap-tpls-0.13 and bootstrap 3.3.2, angular 1.3.14 not working

As mentioned in the title, the modal does not show up.
The content of the form is loaded via formly and the content of the template seems to load, but it only shows the modal very thin, with the overlay but not the content.
I have a main controller in which I have:
$scope.add = function(){
$modal.open({
templateUrl: 'app/js/templates/popupAddCarForm.html',
controller: 'FormsController',
controllerAs: 'vm',
backdrop: 'static',
resolve: {
formData: function(){
return {
fields: getFormFields(),
model: {}
}
}
}
});
};
My html is like so:
<script type="text/ng-template" id="popupAddCarForm">
<div class="modal">
<div class="modal-dialog">
<div class="modal-header">
<h3 class="modal-title">Adauga masina</h3>
</div>
<div class="modal-body">
<form name="vm.addCarForm">
<formly-form model="vm.formData.model" fields="vm.formData.fields">
</formly-form>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-default" type="submit" >Adauga</button>
</div>
</div>
</div>
And my form controller like so:
davidintercar.controller('FormsController',
function($modalInstance, formData) {
var vm = this;
//debugger;
vm.formData = formData;
vm.originalFields = angular.copy(vm.formData.fields);
}
);
The result is like so:
LATER EDIT:
In order to rid ourselfes of other doubts, here is the code from the demo:
app.controller('ModalInstanceCtrl', function ($modalInstance, formData) {
var vm = this;
debugger;
// function assignment
vm.ok = ok;
vm.cancel = cancel;
// variable assignment
vm.formData = formData;
vm.originalFields = angular.copy(vm.formData.fields);
// function definition
function ok() {
$modalInstance.close(vm.formData.model);
}
function cancel() {
$modalInstance.dismiss('cancel');
};
});
Link: angular-formly.com/#/example/integrations/ui-bootstrap-modal
LATER, LATER EDIT:
Plunker: http://plnkr.co/edit/8wgL4t2oXsFFeLBKGGW8?p=preview
Folder Structure:
--app
----js
------controller
------services
------templates
------view
----app.js
intex.html
My popupAddCarForm.html is in the templates directory, but as you see in the plunker, it does not render my loaded content, even in the same directory although a separate template file.
The modal template don't need to have the modal and modal-dialog layer - they will be generated by bootstrap.
<script type="text/ng-template" id="popupAddCarForm.html">
<div class="modal-header">test
<h3 class="modal-title">Adauga masina</h3>
</div>
<div class="modal-body">
<form name="vm.addCarForm">
<formly-form model="vm.formData.model" fields="vm.formData.fields">
</formly-form>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-default" type="submit" >Adauga</button>
</div>
</script>

angular-file-upload: additional properties/options to each file in a multi-file upload?

I'm using nervgh's angular-file-upload, https://github.com/nervgh/angular-file-upload/wiki/Module-API.
Is there a way to use the angular-file-upload and allow additional properties to each file when doing a multi-file upload?
I'm using their image sample to start out with: http://nervgh.github.io/pages/angular-file-upload/examples/image-preview/
Trying to add a boolean to each file that the user can set and then I use that on the server side when it's picked up.
You can use formData property shown in Properties section to send to server whatever you need.
formData {Array}: Data to be sent along with the files.
If you're using PHP in server side, I think this post can help you out.
The question is rather old, but as the documentation didn't really help me much, I would like to note down my solution here:
This is how my html looks like (look for "options"):
<div ng-controller="UploadCtrl2" nv-file-drop="" uploader="uploader" filters="customFilter">
<div class="progress progress-xs margin-top-5 margin-bottom-20">
<div class="progress-bar" role="progressbar" ng-style="{ 'width': uploader.progress + '%' }"></div>
</div>
<div class="row">
<div class="col-md-6">
<div ng-show="uploader.isHTML5">
<div class="well my-drop-zone" nv-file-drop="" options="{formData:[{folder:'attachments'}, {recordid:0}]}" uploader="uploader">
Dateien hierher ziehen.
</div>
</div>
</div>
<div class="col-md-6">
<span class="btn btn-primary btn-o btn-file margin-bottom-15"> Dateien auswählen
<input type="file" nv-file-select="" options="{formData:[{folder:'attachments'}, {recordid:0}]}" uploader="uploader" multiple />
</span>
</div>
</div>
</div>
And this is my controller (look for "fileItemOptions"):
app.controller('UploadCtrl2', ['$rootScope', '$scope', 'FileUploader', 'Store',
function ($rootScope, $scope, FileUploader, Store) {
var fileItemOptions = {};
var uploader = $scope.uploader = new FileUploader({
url: $rootScope.app.api.url + '/?c=uploads&a=set&authToken=' + encodeURIComponent(Store.get('X-Xsrf-Token')),
});
// FILTERS
uploader.filters.push({
name: 'customFilter',
fn: function (item/*{File|FileLikeObject}*/, options) {
if(options) fileItemOptions = options;
return this.queue.length < 10;
}
});
uploader.removeAfterUpload = true;
// CALLBACKS
uploader.onAfterAddingFile = function (fileItem, options) {
//console.info('onAfterAddingFile', fileItem);
if(fileItemOptions.formData) {
fileItem.formData = fileItemOptions.formData;
}
};
uploader.onAfterAddingAll = function (addedFileItems) {
setTimeout(function () {
console.log(uploader);
uploader.uploadAll();
}, 500);
};
uploader.onCompleteAll = function () {
$scope.$parent.run.uploadComplete();
fileItemOptions = {}; // cleanup
};
}]);
Whenever a file is added, the custom filter stores the option object in a global variable. The callback "onAfterAddingFile" will read that variable and it to the fileItem object. Quite hacky, but this was the only way I got it running.

Dynamic Templates and Jquery Steps

I'm working to create a form wizard with jQuery steps. From step 1 to step 2 I need to be able to determine which template is loaded depending on the selection in step 1. I have this template. I would like a different template to be rendered as a session variable changes.
<template name = "selectFrame">
<div class = "container">
<div class = "frameCarousel">
{{> Template.dynamic template=active data=this}}
</div>
</div>
</template>
Each of the internal templates look like this template below.
<template name = "artLineFrame">
{{#each artLineFrames}}
<div class = "thumbnail">
<div class = "something">
<img data-src = "{{source}}" alt = "placeholder" class = "img-circle">
<h2>{{name}}</h2>
<p>{{description}}</p>
<button type = "button" id = "{{tag}}" class = "btn btn-primary">Select</button>
</div>
</div>
{{/each}}
</template>
<template name = "classicFrame">
{{#each classicFrames}}
<div class = "thumbnail">
<div class = "something">
<img data-src = "{{source}}" alt = "placeholder" class = "img-circle">
<h2>{{name}}</h2>
<p>{{description}}</p>
<button type = "button" id = "{{alt}}" class = "btn btn-primary">Select</button>
</div>
</div>
{{/each}}
</template>
<template name = "versionsFrame">
{{#each versionsFrames}}
<div class = "thumbnail">
<div class = "something">
<img data-src = "{{source}}" alt = "placeholder" class = "img-circle">
<h2>{{name}}</h2>
<p>{{description}}</p>
<button type = "button" id = "{{alt}}" class = "btn btn-primary">Select</button>
</div>
</div>
{{/each}}
</template>
<template name = "myHarmonyFrame">
{{#each myHarmonyFrames}}
<div class = "thumbnail">
<div class = "something">
<img data-src = "{{source}}" alt = "placeholder" class = "img-circle">
<h2>{{name}}</h2>
<p>{{description}}</p>
<button type = "button" id = "{{tag}}" class = "btn btn-primary">Select</button>
</div>
</div>
{{/each}}
</template>
Active is a function that gets the value of the session variable and returns the name of the appropriate template.
Template.selectFrame.created = function() {
this.selectFrame = new ReactiveVar(null);
this.autorun(function(){
var templateName = Session.get('board');
console.log('##' + templateName);
Template.instance().selectFrame.set(templateName);
})
Tracker.flush();
}
Template.selectFrame.helpers({
'active' : function() {
var dynamicName = Template.instance().selectFrame.get();
return dynamicName;
}
})
The value of board, the session variable that determines which template will render, changes as it should but the only template that ever renders is the template that the default value of board is set too. Can anyone offer some help or some edits I need to make.
I ran into a similar problem.
Instead of using the session variable directly, I created a ReactiveVar and used that instead in the helper.
(to get access to ReactiveVar add it to your project with: meteor add reactive-var )
First I created a reactive variable to store the name of the template I wanted it to dynamically change to within Template.name.created:
this.templateName = new ReactiveVar(null);
then I created an autorun within the Template.name.rendered function to change this value to whatever the session variable was changed to:
this.autorun(function(){
var templateName = Session.get("template_name");
Template.instance().templateName.set(templateName);
})
And finally I changed the template helper to get the template name from the reactive variable:
dynamic_template: function(){
var dynamicName = Template.instance().templateName.get();
return dynamicName;
}
I hope this approach works for your particular case.

Resources