Add event on top of mizzao:autocomplete package in meteor - meteor

I am using mizzao:autocomplete package in one of my search field in my application. Autocomplete is working fine and auto-suggestion is coming from my DB. As per given in the usage documentation this package using separate template for showing the suggestion list. Usually when someone select from given suggestion, the list will disappear and selected value appears in the textbox.
Now what i want is manually trigger some event in title template & do some extra stuff in autoComplete template when someone select some suggestion..
autoComplete.html
<template name="autoComplete">
<div class="col-md-4">
<h4>Auto Complete</h4>
{{> inputAutocomplete settings=settings id="jobTitle" class="form-control" name="title" placeholder="Job Title" autocomplete="off"}}
</div>
</template>
<template name="titles">
{{title}}
</template>
autoComplete.js
Template.autoComplete.helpers({
settings : function() {
return {
position: 'bottom',
limit: 10,
rules: [
{
collection: JobTitleCollection,
field: 'title',
matchAll: true,
template: Template.titles
}
]
};
}
});

You want to use the autocompleteselect event, as described in the docs.
Template.foo.events({
"autocompleteselect input": function(event, template, doc) {
console.log("selected ", doc);
}
});
(Disclaimer: I am mizzao.)

Related

How to write a jQueryUI selectmenu with dynamic options into a Meteor Blaze template

I am having trouble making the jQueryUI selectmenu work in a Meteor app when the options are dynamic. I am using:
Meteor 1.4.1
jQuery 2.2.4
jQueryUI 1.11.4
lodash 4.15.0
physiocoder said on a different question, "The Meteor reactivity force you to choose who is in charge of DOM updates.".
I realize that this is fundamental to my problem here. Accordingly, there is no problem if a page/template can let Meteor load all the page content/data and then hand over DOM control to jQueryUI's widgets. However I have a case where I want to have my cake and eat it too -- I want to have Meteor reactively feed the options for a jQueryUI widget (specifically the selectmenu at the moment) but still let jQueryUI handle the styling/theming.
Initializing jQueryUI widgets in template onRendered functions works just fine, as does destroying jQueryUI widgets, as necessary, in template onDestroyed functions. Calling selectmenu('refresh') on the option template's onRendered function does refresh the selectmenu when new options are available. However, I cannot find anywhere I can effectively call refresh when options are reactively removed such that the selectmenu is refreshed to the new, correct UI state -- not at the end of the event which changes the Meteor data context, not on the option template's onDestroyed function, and not a Tracker.autorun function tied to the appropriate data source.
HTML:
<head>
<title>Proof of Concept</title>
</head>
<body>
<div id="myApp">
{{> myForm}}
</div>
</body>
<template name="myForm">
<div>
<div id="selectedEntries">
<h3>Selected Entries</h3>
<ul class="display-list">
{{#each entry in selectedEntries}}
{{> myForm_entry entry}}
{{/each}}
</ul>
</div>
<br/>
<form id="includeEntry">
<select name="entryToInclude" id="entryToInclude">
{{#each potentialEntry in availableEntries}}
{{> myForm_option potentialEntry}}
{{/each}}
</select>
<input type="submit" value="Include Entry">
</form>
</div>
</template>
<template name="myForm_entry">
<li>
<div class="button removeEntry" data-id="{{_id}}">X</div>
<span>{{name}}</span>
</li>
</template>
<template name="myForm_option">
<option value="{{_id}}">{{name}}</option>
</template>
JavaScript:
Template.myForm.helpers({
availableEntries: function () {
return _.filter(Session.get('someEntries'), function(o) {
return Session.get('selectedEntryIds').indexOf(o._id) == -1;
});
},
selectedEntries: function () {
return _.filter(Session.get('someEntries'), function(o) {
return Session.get('selectedEntryIds').indexOf(o._id) != -1;
});
}
});
Template.myForm.events({
'submit #includeEntry': function (event) {
event.preventDefault();
if (_.isEmpty(Session.get('selectedEntryIds'))) {
Session.set('selectedEntryIds', [$('#entryToInclude').val()]);
} else {
let selectedEntryIds = Session.get('selectedEntryIds');
selectedEntryIds.push($('#entryToInclude').val());
Session.set('selectedEntryIds', selectedEntryIds);
}
$('#entryToInclude').selectmenu('refresh')
},
'click .removeEntry': function (event) {
event.preventDefault();
let selectedEntryIds = Session.get('selectedEntryIds');
selectedEntryIds = _.pull(selectedEntryIds, $(event.target).parent().attr('data-id'));
Session.set('selectedEntryIds', selectedEntryIds);
}
});
Template.myForm.onCreated(function () {
let someEntries = [{
_id:'1',
name:'One'
},{
_id:'2',
name:'Two'
},{
_id:'3',
name:'Three'
},{
_id:'4',
name:'Four'
},{
_id:'5',
name:'Five'
},{
_id:'6',
name:'Six'
}];
Session.set('someEntries', someEntries);
Session.set('selectedEntryIds', []);
});
Template.myForm.onRendered(function () {
$('#entryToInclude').selectmenu();
$('input:submit').button();
});
Template.myForm_entry.onRendered(function () {
$('.button').button();
});
Template.myForm_option.onRendered(function () {
if ($('#entryToInclude').is(':ui-selectmenu')) {
$('#entryToInclude').selectmenu('refresh');
}
});
Template.myForm_option.onDestroyed(function () {
$('#entryToInclude').selectmenu('refresh');
});
Tracker.autorun(function () {
if (Session.get('selectedEntryIds')) {
if ($('#entryToInclude').is(':ui-selectmenu')) {
$('#entryToInclude').selectmenu('refresh');
}
}
});
The #entryToInclude selectmenu continues to include the entry that was just selected; selecting a second entry numbered as high or higher actually selects the subsequent entry (e.g. selecting 4 then 5 actually selects 4 and 6) except that selecting the last entry immediately after a successful selection does nothing but refresh the selectmenu.
Adding a refresh to the entry template's onRendered function makes this work.
Template.myForm_entry.onRendered(function () {
$('.button').button();
if ($('#entryToInclude').is(':ui-selectmenu')) {
$('#entryToInclude').selectmenu('refresh');
}
});
But a better approach to the entire problem would be appreciated.

Meteor autoform access nested property

I've got some linked schemas and am trying to access properties of a subschema from the form for the primary schema. It's a mouthful, I know. Code may help:
//js
Collection = new Meteor.Collection('collection');
Schemas = {};
Schemas.secondary = new SimpleSchema({
unitType: {
type: String,
autoform: {
type: 'select',
options: //function with all the options
}
}
});
Schemas.primary= new SimpleSchema({
name: {
type: String,
},
units: {
type: [ Schemas.secondary ]
}
});
Collection.attachSchema(Schemas.primary);
//HTML
{{#autoForm collection="Collection" id="someId" type="insert"}}
{{> afQuickField name='name'}} // this works
{{> afQuickField name='units'}} // this works
{{> afQuickField name='units.unitType'}} // this doesn't work :-(
{{/autoForm}}
The reason I'm doing this is because there are other properties in the secondary schema that I want to show conditionally, based on the value of the select box. I also tried to put a form inside a form and then run {{#each afFieldNames name='"units"}} but that didn't quite work either. Instead of giving me just the fields contained in units (i.e., the secondary schema), it looped through all fields of both primary and secondary.
Thoughts? I'm not married to this pattern but I can't think of another way.
Thanks again, all.
db
I had this issue myself.
Give this a go
{{> afQuickField scope='units.unitType' name='units.unitType'}}
If you dump your modifier in your before submit hook you should be able to see the subdocument successfully filled out
AutoForm.hooks({
someId: {
before: {
'insert': function(modifier) {
console.log(modifier);
}
}
}
});
Let me know if this works for you!
All the best,
Elliott

Reactive-Tables Meteor

I am trying to get reactive-tables to work but I am having no luck following the instructions on GitHub.
This is what I have:
In my Main.html:
{{> courseTable }}
in my course_table.html:
<template name="courseTable">
<div id="table">
{{ > reactiveTable collection=Courses}}
</div>
</template>
in courses.js:(works with autoForm)
Courses = new Meteor.Collection("courses", {
schema: {....
Is there something I am missing? From my understanding once these commands are used, the rest is done from within the package. I can't find any more information on this package anywhere.
What I have now just shows a blank screen.
Thanks in advance!
This is what I have: (I'm using Meteor framework and bootstrap-3 package)
in index.html
<template name="clientes">
<div class="container">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Clientes</h3>
</div>
<div class="panel-body">
{{> reactiveTable collection=tables settings=tableSettings}}
</div>
</div>
</div>
</template>
in index.js
var Clientes = new Meteor.Collection('clientes')
Template.clientes.tables = function () {
return Clientes;
}
Template.clientes.tableSettings = function () {
return {
rowsPerPage: 10,
showFilter: false,
showNavigation: 'auto',
fields: [
{ key: 'nombre', label: 'Nombre' },
{ key: 'apellido', label: 'Apellido' },
{ key: 'correoe', label: 'E-mail' }
],
useFontAwesome: true,
group: 'client'
};
}
With this I can display all the records in the collection.
I hope this help you to go any further.
Courses is the collection object. To get some courses, you need to query the courses with find:
Courses.find()
However, to make this accessible in the template, you need a helper function.
//course_table.js
Template.courseTable.helpers({
courses: function () {
return Courses.find()
}
});
Then you can can set the table collection using the helper method (I used a lowercase "courses" for the helper method for clarity):
{{> reactiveTable collection=courses}}

How do I get templates inserted from custom block helpers to be individually rerendered in Meteor?

When I use the built-in block helper #each, book templates are rerendered individually when changed:
users =
_id: 'foo'
books: [
{name: 'book1'}
{name: 'book2'}
]
<template name="user">
{{#each books}}
{{> book}}
{{/each}}
</template>
<template name="book">
<div>{{name}}</div>
</template>
When the data is changed - the first book name is set to 'bookone' instead of 'book1' - only the book template (the div containing 'book1') is rerendered. This is the desired behavior. When I use a custom block helper, the behavior is different:
<template name="user">
{{#each_with_id}}
{{> book}}
{{/each}}
</template>
<template name="book">
<div data-id="{{_id}}">{{name}}</div>
</template>
Templates.user.each_with_id = (options) ->
html = "
for book, i in this.books
this.name = book.name
html += Spark.labelBranch i.toString(), ->
options.fn this
html
Now when the name of the first book changes, the whole user template is rerendered.
It does not work as you expect, because the implementation of built-in each is based on the cursor.observeChanges feature. You will not be able to achieve the same exact result without using an auxiliary collection of some sort. The idea is quite simple. It seems that you don't have a "books" collection but you can create a client-side-only cache:
Books = new Meteor.Collection(null);
where you will need to put some data dynamically like this:
Users.find({/* probably some filtering here */}).observeCanges({
added: function (id, fields) {
_.each(fields.books, function (book, index) {
Books.insert(_.extend(book, {
owner: id,
index: index,
}));
}
},
changed: function (id, fields) {
Books.remove({
owner:id, name:{
$nin:_.pluck(fields.books, 'name')
},
});
_.each(fields.books, function (book, index) {
Books.update({
owner : id,
name : book.name,
}, {$set:_.extend(book, {
owner : id,
index : index,
})}, {upsert:true});
}
},
removed: function (id) {
Books.remove({owner:id});
},
});
Then instead of each_with_id you will be able to the built-in each with appropriate cursor, e.g.
Books.find({owner:Session.get('currentUserId')}, {sort:{index:1}});
You may also look at this other topic which basically covers the same problem you're asking about.

Content wrapped in currentUser re-rendering when user updated

I'm using Meteor and having an issue where my content is being re-rendered when I don't want it to.
I have my main content wrapped in a currentUser if statement which I feel is fairly standard.
{{#if currentUser}}
{{> content}}
{{/if}}
The problem with this is my content template is being re-rendered when I update my user object. Is there any way around this? I don't reference users anywhere inside the content template.
Thank you!
Here's a sample app to replicate my problem:
HTML
<head>
<title>Render Test</title>
</head>
<body>
{{loginButtons}}
{{> userUpdate}}
{{#if currentUser}}
{{> content}}
{{/if}}
</body>
<template name="userUpdate">
<p>
<input id="updateUser" type="button" value="Update User Value" />
User last update: <span id="lastUpdated">{{lastUpdated}}</span>
</p>
</template>
<template name="content">
<p>Render count: <span id="renderCount"></span></p>
</template>
JavaScript
if (Meteor.isClient) {
Meteor.startup(function() {
Session.set("contentRenderedCount", 0);
});
Template.content.rendered = function() {
var renderCount = Session.get("contentRenderedCount") + 1;
Session.set("contentRenderedCount", renderCount);
document.getElementById("renderCount").innerText = renderCount;
};
Template.userUpdate.events = {
"click #updateUser": function() {
Meteor.users.update({_id: Meteor.userId()}, {$set: {lastActive: new Date()}});
}
};
Template.userUpdate.lastUpdated = function() {
return Meteor.user().lastActive;
};
}
if (Meteor.isServer) {
Meteor.users.allow({
'update': function () {
return true;
}
});
}
Update:
I should've explained this example a little. After creating a user, clicking the Update User Value button, causes the render count to increment. This is because it's wrapped in a {{#if currentUser}}. If this is if is removed, you'll notice the render count remains at 1.
Also, you'll need to add the accounts-ui and accounts-password packages to your project.
Meteor will re-render any template containing reactive variables that are altered. In your case the {{currentUser}} is Meteor.user() which is an object containing the user's data. When you update the users profile, the object changes and it tells meteor to re-calculate everything reactive involving the object.
We could alter the reactivity a bit so it only reacts to changes in whether the user logs in/out and not anything within the object itself:
Meteor.autorun(function() {
Session.set("meteor_loggedin",!!Meteor.user());
});
Handlebars.registerHelper('session',function(input){
return Session.get(input);
});
Your html
{{#if session "meteor_loggedin"}}
{{> content}}
{{/if}}

Resources