I have a standard template that does an {{#each}} with a list of reactive elements from a collection.
The problem I am having is, I want to do a font property change on a particular element whenever it is updated in the collection, just to draw attention to the fact that it was updated.
I haven't been able to find a good / elegant way to do this. Can anyone point me to the right direction? Thanks!
you might want to check cursor observe, when some document updates or changed, you can do anything like if event called.
li#model-1273927381273 model-1273927381273
Modal.find().observe
changed: (newDocument, oldDocument) ->
($ '#model-'+ newDocument._id).action()
http://docs.meteor.com/#observe
i use similar code currently but dont know how to prevent init documents load.
Similar to your question, I wanted to 'flash' a div whenever a particular session variable (a GPS location) changed. My solution below.
in /client/main.js:
Session.setDefault('lat', 0);
Session.setDefault('lng', 0);
Session.setDefault('locationChange', 1);
And:
Meteor.setInterval(function() {
getLocation();
}, 5000);
function getLocation() {
Location.locate(function(pos){
var oldLat = Session.get("lat");
var oldLng = Session.get("lng");
Session.set('lat', pos.latitude);
Session.set('lng', pos.longitude);
if (oldLat != Session.get("lat") || oldLng != Session.get("lng"))
Session.set("locationChange", -1 * Session.get("locationChange"));
}, function(err){
console.log("Location not obtained: ", err);
});
}
In the template, say /client/templates/someTemplate.html, where I wanted to alert the user to the change:
<span class="glyphicon glyphicon-screenshot" aria-hidden="true" data-locationChange='{{locationChange}}'></span>
In the associated javascript file, say /client/templates/someTemplate.js:
Template.appBody.helpers({
locationChange: function() {
$("#locationIcon").fadeOut(100).fadeIn(100);
return Session.get("locationChange");
},
});
So, now, whenever the GPS coordinates change (not necessarily when they are refreshed), the value for 'locationChange' is flipped from 1 to -1 or vice versa. This triggers the 'locationChange' helper, which updates the (meaningless) attribute 'data-locationChange', but also triggers some jQuery script that flashes the span in the template.
Related
I have some templates corresponding to different places. I am using a navigation bar which has links to different places(Manali). I want the corresponding template to be displayed when a particular link is being clicked. I tried assigning id to each anchor link and use it inside the #if loop of the main file. Like below.
{{#if equals id 'badrinath'}}
{{> Manali}}
{{/if}
I created a helper function also for the comparison purpose.
UI.registerHelper('equals', function(a, b) {
return a == b;
});
But it isn't working. Can anyone suggest a solution. What property of the link can I capture and use it to display the template accordingly.
You sound to be looking for "routing" functionality.
You might be interested in Iron Router or Flow Router.
You can still implement your functionality without router, as it sounds still a simple situation as described. You are probably just lacking some event listeners to set your id variable to the correct value.
Probably something like:
<a data-role="changetemplate" href="targetTemplate">To Target Template</a>
var id = new ReactiveVar(); // add the reactive-var package
Template.myTemplate.helpers({
id: function () {
return id.get();
}
});
Template.myTemplate.events({
"click a[data-role='changetemplate']": function (event) {
event.preventDefault();
id.set(event.currentTarget.href);
}
});
In my meteor app I need to load an array of items corresponding to the item clicked.As I'm new to meteor, I'm held up here.Here is my code.
Template.templatename.events({
'click .showdiv' : function()
{
Template.templatename.vname = function () {
return Db.find();
}
}
Can I set the variable vname dynamically by this code ? This is not working for me.
I think you're misunderstanding the notion of reactivity. A reactive data source will cause any functions which depend on it (including helpers) to rerun when its value is changed, which seems to be the behavior you're looking for here. Instead, you're rewriting the helper function itself every time an item is clicked, which kind of defeats the object of Meteor's reactive data model. Session variables could help:
Template.templatename.events({
'click .showdiv' : function() {
Session.set('vname', Db.find());
}
});
Template.templatename.vname = function () {
return Session.get('vname');
}
If you use an {{#each vname}} block in the templatename template, it will automatically update with the results of the Db.find() query when a .showdiv is clicked. If all you want to do is show the result of that query regardless of whether a click has been registered it would be as simple as:
Template.templatename.vname = function () {
return Db.find();
}
Note that it's still not clear exactly what data you're trying to populate here since the query will return a cursor (which is fine, but you need to loop through it using {{#each ...}} - use findOne if you only want one item), and its contents aren't going to depend on anything intrinsic to the click event (like which .showdiv you clicked). In the former example it will however fail to show anything until the first click (after which you would have to reset with Session.set('vname', null) to stop it showing anything again).
I have a sortable list.
<template name="the_playlist">
{{#each main_list}}
<li id="{{index}}" class="list_element">
<div class="next_song">...</div>
<div class="destroy">...</div>
<div class="element_style">{{song_title}}</div>
</li>
{{/each}}
</template>
And this is the main_list that it prints from.
Template.the_playlist.main_list = function(){
//if ret is valid, it will have a songs member
var ret = Links.find().fetch()[0];
if (typeof ret == 'undefined'){
ret = []
}
else {
ret = Links.find().fetch()[0].songs;
}
return ret;
}
And I am using the sortable plugin and more importantly its update callback which updates everytime the user changes a position the list or an element is added to the list.
$(function() {
$( "#playlist" ).sortable({
update: function(){
Template.list.updateList(); //MODIFIES DB CONTENTS, AND MAIN_LIST's VALUES CHANGE
}});
$( "#playlist" ).disableSelection();
});
*The problem: * If a page already has list elements when it's loaded, for one time only, I would like to add a class that hides (.addClass("hide")) each of the next_song elements that are on the page at that time. This *will work only until main_list changes* by a call to Template.list.updateList above, after which automagically, the added class will disappear - most likely due to the re-rendering that is occuring since the main_list depends on the db changes.
The following in the JQuery snippet I use to try and accomplish this.
$("#playlist li .next_song").each(function(){
$(this).addClass("hide_song");
})
Here is a demo. Try plugging in the above JQUery code into the console. and then move the list elements around to see the problem.
Can you not just determine whether that will be the case in a helper function?
Template.the_playlist.helpers({
'list_elements_exist': function() {
return (!!$('#playlist li').length);
}
}
Then you can just insert the logic straight into the template:
<div class="next_song{{#if list_elements_exist}} hide_song{{/if}}">...</div>
To be honest, I'm not 100% sure that this will float with reactivity depending on the structure of your app. If it doesn't work properly, I'd introduce a new session boolean, list_elements, the value of which is returned by the helper function above. It should be fairly easy to update its value in event handlers or created callbacks to keep it tracking whether there are any items in the list or not, and this will guarantee the list renders as required regardless of other dependencies changing.
So I have a meteor collection full of GPS coordinates which I plot on a map. Anytime data changes in my Meteor collection, I have a deps.autorun that clears all the markers that aren't in my subscription. Then I draw all the markers that have yet to be drawn. If I have a bunch of data that updates quickly, the browser on my iPhone crashes. So I would like to be able to be able to update the map for only what has changed, rather than scanning each time and finding what has changed to lighten the load. Is this possible?
Thanks
#user728291 is right, observeChanges or just observe is used in such situations.
What you want to do is to track all new objects by ids:
Template.map.rendered = function () {
Places.find().observe({
added: function (doc) {
// code to add pin of doc
},
changed: function (newDoc, oldDoc) {
// code to move pin from oldDoc position to newDoc
},
removed: function (doc) {
// code to remove a pin associated with doc
}
});
}
In fact, whenever you combine {{#each things}}...{{/each}} template helper with your data returned as MongoDB cursor:
Template.myTemplate.things = function () {
return Things.find({ rating: { $gt: 3 } });
}
Meteor will do the same thing under the hood. UI system will observe the changes on the cursor an instead of redrawing everything, it will figure out what changed and update only relevant parts of your displayed list.
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.