Meteor controlling copying and pasting in a text field - meteor

I am trying to prevent copying and pasting white spaces in the username field inside my Meteor app template but I am always getting an error as shown below, can someone please tell me what I am doing wrong / missing and is there any other way to control content pasted in a text field in Meteor template? Thanks
Template.UserRegisteration.events({
'input #username':function(e,t){
this.value = this.value.replace(/\s/g,'');
}
});
Error:
Uncaught TypeError: Cannot read property 'replace' of undefined

this is the context is the data context where the input id="username field is.
To get the field's DOM element use e.currentTarget instead of this.

As Akshat mentioned to get field DOM element use e.currentTarget instead of this, back to your question code sample please try the following
Template.UserRegisteration.events({
'input #username':function(e,t){
var text = e.currentTarget.value;
e.currentTarget.value = text.replace(/\s/g,'');
}
});

The following example sets out how to extract and set the value of a DOM element within a Meteor event:
https://www.meteor.com/try/4
Template.body.events({
"submit .new-task": function (event) {
// This function is called when the new task form is submitted
var text = event.target.text.value;
Tasks.insert({
text: text,
createdAt: new Date() // current time
});
// Clear form
event.target.text.value = "";
// Prevent default form submit
return false;
}
});
Within a Meteor events block, "this" is not the DOM element so you cannot call a value on it.

Related

MeteorJS Blaze.getData() occasionally returns undefined

I'm currently rendering bootstrap modals on my webpage using MeteorJS's "renderWithData" method to load each template when it's needed.
I'm running into an issue where my helper methods which access the data in the modal using "Blaze.getData()" will occasionally return undefined and I'm unsure how to fix that.
The only way I've been able to replicate the issue is by constantly creating/destroying the modals and there doesn't seem to be anything that specifically causes the issue.
Here are the steps I've been taking:
1) I instantiate the modal with the proper data
Template.Courses.events({
'click .share-course': function (e,t) {
var courseID = $(e.target).data('courseid');
Template.instance().activeCourse.set(
createModalWithData(
{
currentInstance: Template.instance().activeCourse.get(),
template: Template.Enrollment_Generator,
dataToRender: {courseID: courseID}
}
));
$('#generateEnrollmentURL').modal('show');
}
});
Also, here is the code for "createModalWithData":
// Create a modal with a specific data context
// If modal template already exists, destroy
// and re-create with the new data context.
// If a location to render isn't specified, renders
// content in the body .
// Parameters: [Object] data { currentInstance : Template || null,
// template : Template,
// dataToRender : Object,
// (optional) location : Element
// Return: Blaze Template Instance
createModalWithData = function createModalWithData(data) {
// Ensure data exists
if (_.isUndefined(data) || _.isNull(data)) {
throw "data cannot be null or undefined";
}
// If modal already exists, destroy it
if (!_.isNull(data.currentInstance)) {
Blaze.remove(data.currentInstance);
}
// If location is undefined, set to page body
if (_.isUndefined(data.location)) {
data.location = document.body;
}
// Render modal with dataToRender
return Blaze.renderWithData(data.template,
data.dataToRender,
data.location
);
};
2) I attempt to retrieve the data using "Blaze.getData()" within my modal template
Template.Enrollment_Generator.onCreated(function() {
var courseID = Blaze.getData().courseID; // Occasionally undefined
Meteor.subscribe('enrollment-codes',courseID);
});
So far I've attempted to replace the "onCreated" method with "onRendered" but still had the same issue.
It turns out the issue was within the click event. I had a nested span element within my share-course button:
<small class="share-course" data-courseid="{{_id}}">
Share
<span class="glyphicon glyphicon-share"></span>
</small>
This was messing up the way I was targeting my embedded courseID
Instead of Blaze.getData(), I should have also been using Template.currentData() to retrieve the data within my template
As stated here: https://forums.meteor.com/t/blaze-getdata-question/5688

Get Dropdown Value in Meteor Js?

I did one sample Searchapp using meteor add sebdah:autocompletion package.When ever given inputs it shows drop down list.In this list how to get selected value as shown below code:
Js Code :
Friends = new Meteor.Collection('friends');
if (Meteor.isClient) {
/**
* Template - search
*/
Template.search.rendered = function () {
AutoCompletion.enableLogging = true;
var res = AutoCompletion.init("input#searchBox");
console.log("res :"+res);
}
Template.search.events = {
'keyup input#searchBox': function (e,t) {
AutoCompletion.autocomplete({
element: 'input#searchBox', // DOM identifier for the element
collection: Friends, // MeteorJS collection object
field: 'name', // Document field name to search for
limit: 0, // Max number of elements to show
sort: {name: 1}
});
}
}
}
I didn't get any idea about this.So please suggest me how to get selected drop down list values?
AutoCompletion package doesn't give any good API to read value on select. Instead you need to manually read the value of input#searchBox.
Please take a look at source code.
I would recommend to implement searching in your meteor app using Arunoda's approach : https://meteorhacks.com/implementing-an-instant-search-solution-with-meteor.html

Meteor issue with UI.renderWithData while dynamically adding Gridster widgets to DOM

I'm trying to use the gridster.add_widget method to pass pre-rendered templates into the DOM.
I must not understand how to use UI.renderWithData because I get the error message message at the very bottom each time I call the UI commands.
My understanding from the docs is that these commands will return the fully formed HTML of the template passed in. I've validated that gridster is working right by creating a red box for each 'project' that gets called.
Any help would be mucho appreciated. Thanks!
Template.projects.rendered = function(){
gridster = $(".gridster div").gridster({
widget_margins: [10, 10],
widget_base_dimensions: [140, 140]
}).data('gridster');
var projectz = Projects.find({}, {sort: { time: -1}});
$.each(projectz, function(index, widget){
if (widget != undefined) {
console.log(widget);
// This works and adds a red box for each project in the database
gridster.add_widget('<div style="background-color: red;">');
// This does NOT work, I assume it is an issue with my use of UI.renderWithData
// gridster.add_widget(UI.insert(UI.renderWithData(Template.projects, widget)), 1, 1);
}
});
};
//
// Error I get trying to gridster.add_widget() or console.log()
// with (UI.insert(UI.renderWithData(Template.projects, widget)));
//
Exception from Deps afterFlush function function: TypeError: Cannot read property 'nodeName' of undefined
at tbodyFixNeeded (http://localhost:3000/packages/ui.js?b523ef986d3d39671bcb40319d0df8982acacfe8:1368:13)
at Function.DomRange.insert (http://localhost:3000/packages/ui.js?b523ef986d3d39671bcb40319d0df8982acacfe8:1337:7)
at Object.UI.insert (http://localhost:3000/packages/ui.js?b523ef986d3d39671bcb40319d0df8982acacfe8:2123:15)
at null.<anonymous> (http://localhost:3000/client/controllers/projects.js?c493c07746d208ff23d7a134ed4353a706244d46:41:29)
at Function.o.extend.each (http://localhost:3000/client/compatability/jquery/jquery-2.1.0.min.js?4c694ca97ed11ffba659454ac63c35e0452a994d:2:2931)
at Object.Template.projects.rendered (http://localhost:3000/client/controllers/projects.js?c493c07746d208ff23d7a134ed4353a706244d46:37:7)
at http://localhost:3000/packages/ui.js?b523ef986d3d39671bcb40319d0df8982acacfe8:424:23
at _.extend.flush (http://localhost:3000/packages/deps.js?7afb832ce6e6c89421fa70dc066201f16f9b9105:331:13)
UI.insert expects a DOM node as a second argument, see documentation. You are calling it without any additional arguments.
Is there a reason you aren't rendering all of the Project elements to the page in the template?

Bootboxjs: how to render a Meteor template as dialog body

I have the following template:
<template name="modalTest">
{{session "modalTestNumber"}} <button id="modalTestIncrement">Increment</button>
</template>
That session helper simply is a go-between with the Session object. I have that modalTestNumber initialized to 0.
I want this template to be rendered, with all of it's reactivity, into a bootbox modal dialog. I have the following event handler declared for this template:
Template.modalTest.events({
'click #modalTestIncrement': function(e, t) {
console.log('click');
Session.set('modalTestNumber', Session.get('modalTestNumber') + 1);
}
});
Here are all of the things I have tried, and what they result in:
bootbox.dialog({
message: Template.modalTest()
});
This renders the template, which appears more or less like 0 Increment (in a button). However, when I change the Session variable from the console, it doesn't change, and the event handler isn't called when I click the button (the console.log doesn't even happen).
message: Meteor.render(Template.modalTest())
message: Meteor.render(function() { return Template.modalTest(); })
These both do exactly the same thing as the Template call by itself.
message: new Handlebars.SafeString(Template.modalTest())
This just renders the modal body as empty. The modal still pops up though.
message: Meteor.render(new Handlebars.SafeString(Template.modalTest()))
Exactly the same as the Template and pure Meteor.render calls; the template is there, but it has no reactivity or event response.
Is it maybe that I'm using this less packaging of bootstrap rather than a standard package?
How can I get this to render in appropriately reactive Meteor style?
Hacking into Bootbox?
I just tried hacked into the bootbox.js file itself to see if I could take over. I changed things so that at the bootbox.dialog({}) layer I would simply pass the name of the Template I wanted rendered:
// in bootbox.js::exports.dialog
console.log(options.message); // I'm passing the template name now, so this yields 'modalTest'
body.find(".bootbox-body").html(Meteor.render(Template[options.message]));
body.find(".bootbox-body").html(Meteor.render(function() { return Template[options.message](); }));
These two different versions (don't worry they're two different attempts, not at the same time) these both render the template non-reactively, just like they did before.
Will hacking into bootbox make any difference?
Thanks in advance!
I am giving an answer working with the current 0.9.3.1 version of Meteor.
If you want to render a template and keep reactivity, you have to :
Render template in a parent node
Have the parent already in the DOM
So this very short function is the answer to do that :
renderTmp = function (template, data) {
var node = document.createElement("div");
document.body.appendChild(node);
UI.renderWithData(template, data, node);
return node;
};
In your case, you would do :
bootbox.dialog({
message: renderTmp(Template.modalTest)
});
Answer for Meteor 1.0+:
Use Blaze.render or Blaze.renderWithData to render the template into the bootbox dialog after the bootbox dialog has been created.
function openMyDialog(fs){ // this can be tied to an event handler in another template
<! do some stuff here, like setting the data context !>
bootbox.dialog({
title: 'This will populate with content from the "myDialog" template',
message: "<div id='dialogNode'></div>",
buttons: {
do: {
label: "ok",
className: "btn btn-primary",
callback: function() {
<! take some actions !>
}
}
}
});
Blaze.render(Template.myDialog,$("#dialogNode")[0]);
};
This assumes you have a template defined:
<template name="myDialog">
Content for my dialog box
</template>
Template.myDialog is created for every template you're using.
$("#dialogNode")[0] selects the DOM node you setup in
message: "<div id='dialogNode'></div>"
Alternatively you can leave message blank and use $(".bootbox-body") to select the parent node.
As you can imagine, this also allows you to change the message section of a bootbox dialog dynamically.
Using the latest version of Meteor, here is a simple way to render a doc into a bootbox
let box = bootbox.dialog({title:'',message:''});
box.find('.bootbox-body').remove();
Blaze.renderWithData(template,MyCollection.findOne({_id}),box.find(".modal-body")[0]);
If you want the dialog to be reactive use
let box = bootbox.dialog({title:'',message:''});
box.find('.bootbox-body').remove();
Blaze.renderWithData(template,function() {return MyCollection.findOne({_id})},box.find(".modal-body")[0]);
In order to render Meteor templates programmatically while retaining their reactivity you'll want to use Meteor.render(). They address this issue in their docs under templates.
So for your handlers, etc. to work you'd use:
bootbox.dialog({
message: Meteor.render(function() { return Template.modalTest(); })
});
This was a major gotcha for me too!
I see that you were really close with the Meteor.render()'s. Let me know if it still doesn't work.
This works for Meteor 1.1.0.2
Assuming we have a template called changePassword that has two fields named oldPassword and newPassword, here's some code to pop up a dialog box using the template and then get the results.
bootbox.dialog({
title: 'Change Password',
message: '<span/>', // Message can't be empty, but we're going to replace the contents
buttons: {
success: {
label: 'Change',
className: 'btn-primary',
callback: function(event) {
var oldPassword = this.find('input[name=oldPassword]').val();
var newPassword = this.find('input[name=newPassword]').val();
console.log("Change password from " + oldPassword + " to " + newPassword);
return false; // Close the dialog
}
},
'Cancel': {
className: 'btn-default'
}
}
});
// .bootbox-body is the parent of the span, so we can replace the contents
// with our template
// Using UI.renderWithData means we can pass data in to the template too.
UI.insert(UI.renderWithData(Template.changePassword, {
name: "Harry"
}), $('.bootbox-body')[0]);

Subscribing to changes in a Collection but not in a template

I'm very new to meteor, so apologies if I'm missing something very basic here.
I thought it would be fun to create a very simple textpad style app to check out meteor. I took the todo app and changed the data structures to be 'folders' and 'docs' rather than 'lists' and 'todos', so I have a list of folders and when you click on the folder you get a list of the documents in that folder.
I've then added some code to show the 'content' attribute of a single 'doc' when one of the docs in the list is clicked.
I'm using ace to add some pretty print to the content of the doc (https://github.com/ajaxorg/ace). I've set ace up to work with a hidden textarea containing the plaintext version of my document, and the editor object takes this text and pretty prints it.
The problem with ace is that I don't want the template containing the ace editor to be replaced every time the contents of the doc changes (as it takes half a second to reinitialise, which is a crappy experience after every character is typed!). Instead, I want to update the textarea template and then use the ace API to tell the editor to update it's input based on what is in the textarea.
Now, this is probably the wrong way to approach the problem, but I've ended up using two templates. The first contains a textarea containing doc.contents, which is reactive to the underlying model:
<template name="doc_content">
<textarea name="editor">{{content}}</textarea>
</template>
The second one contains the 'editor' div which ace uses to display the pretty printed text.
<template name="doc_init">
<div id="editor"></div>
</template>
The idea is that the first template will update every time the user types (on all clients), and the second template is only ever re-loaded for each new doc we load.
Template.doc_content.content = function() {
var doc_id = Session.get('viewing_itemname');
if (!doc_id) {
return {};
}
var doc = Docs.findOne({_id:doc_id});
if (doc && doc.content) {
// #1 Later
var editor = Session.get('editor');
if (editor) {
editor.getSession().setValue(doc.content);
}
return doc.content;
} else {
return '';
}
};
When you enter text into the editor div I make a call to Docs.update(doc_id, {$set: {content: text}});, which updates the value in the textarea on each client. All good so far.
editor.getSession().on('change', function(){
var text = editor.getSession().getValue();
Docs.update(doc_id, {$set: {content: text}});
});
What I want to do, for all clients other than the client which made the change, is to subscribe to the change for that doc and call editor.getSession().setContent() with the text which has just been changed, taking the text from the textarea and using it to fill the editor.
I've tried to do this by making that call from the template containing the textarea (as this changes whenever the doc is updated - see #1 above). However, this puts the clients into an infinite loop because changing the value in the editor causes another call to Docs.update.
Obviously this doesn't happen when you render a template, so I'm assuming there's some magic in meteor which can prevent this happening, but I'm not sure how.
Any thoughts?
TIA!
There's a lot to absorb in your question, but if I understand correctly, you might simply be after Deps.autorun:
Deps.autorun(function () {
var doc_id = Session.get('viewing_itemname');
if (!doc_id) {
return {};
}
var doc = Docs.findOne({_id:doc_id});
// do stuff with doc
});
Deps.autorun is really useful in that it will get re-run if any of its
dependencies change. These dependencies are limited to those that are "reactive"
such as Collections and Sessions, or anything that implements the reactive API.
In your case, both Session.get and findOne are reactive so if their values
change at all, Deps.autorun will run the function again.

Resources