Meteor use fetch or find in template helper functions? - mongodb

Inside a meteor template helper function, is there any difference in performance, number of re-renders, or anything else if I return the result of a find vs a fetch?
For example, the find approach:
Template.players.topScorers = function () {
return Users.find({score: {$gt: 100}}, {sort: {score: -1}});
};
Or adding a fetch:
Template.players.topScorers = function () {
return Users.find({score: {$gt: 100}}, {sort: {score: -1}}).fetch();
};
The find-only approach is what is currently in the docs, but I’ve seen lots of other people using fetch.

Yes there is.
By using fetch you register a dependency on the entire query result set on the spot. By using find and later on iterating using {{#each}} a dependency is registered on every document separately. So when one document changes, only the relevant code is re-rendered. When using fetch, changing any document in the result-set would re-render the entire scope in which you used fetch.
For small result-sets it doesn't make any difference. For larger sets with frequent changes it could slow down computation and cause undesired visual artefacts.
I wrote a post which may help you understand it (it doesn't answer your question directly though)

This is what we follow in Oodles Technologies.
For defining helper just go to your template js file for example if you have a template name as allInventory so just go to allInventory.js file and write the helper as follows:-
Template.allInventory.helpers({
})
make a function inside this helper in which you put your logic for getting data from Database or Session or from other service and than use that in you html like:-
Template.allInventory.helpers({
productDetails: function() {
return Session.get('dbData');
}
})
On html side you just need to use the function name as follows:-
{{#each productInfo in productDetails}}
<div class="imgb"><img src="{{productInfo.image_url}}"></div>
{{productInfo.item_name}}
{{productInfo.seller_sku}}
{{productInfo.quantity}}
{{productInfo.price}}
<a type="button" class="full-view text-success"><i id="fullView" data="{{productInfo._id}}" class="fa fa-eye"></i></a>
{{/each}}
As you can see in above productDetails a function name in your helper class on which you get the data you want to render on your Html is accessible directly via that name and you can traverse that via each loop in html templates.

Related

How to update content of an existing jodit editor in a function

i use jodit in my website and wonder, how the content of an existing jodit editor can be updated within a function.
first i do....
$( document ).ready(function() {
var editor_adressblock = new Jodit('#adressblock', {
fullsize: false
});
});
and the editor is correctly created.
I have a function, which can be executed by the user and then should load content (via json-request) and then put the content into the existing jodit textarea.
Here is my try (for this example i have simplified the function) :
function takeover_adressblock(kunde_id, kunden_adresse_id) {
// get data by ajax.... not shown in this example
var data_by_ajax="test";
editor_adressblock.value=data_by_ajax; // this fails....
}
Means, i don't know how i can send data to the existing jodit texteditor...
I am a jquery beginner, so please don't be too hard ;-)
Best regards
Daniel
Per the documentation you seem to have the right format, so it would help to see the code for the ajax request you're making in case the issue is there.
Otherwise, I would suggest initializing the editor without jQuery in case it's a reference or scoping issue:
const editor = Jodit.make('#editor');
editor.value = '<p>start</p>';

repopulate form data from an existing document

I am trying to repopulate fields on a HTML form with a users data they initially filled out when they created an account for the first time in Meteor to allow them to edit information. The form contains various inputs, option lists and check boxes. The data stores on in the collection with no issues. I have a field in the user profile called profComp which is either true of false dependent on if the form has been filled out before. I have tried to add an if/else statement on the route -
Router.route("/edit_dashboard", function(){
if(Meteor.user().profile.profComp)
{
this.render("change_edit_dashboard");
}
else
{
this.render('edit_dashboard');
}
The change_edit_dashboard is a copy of the html form the user initially filled out and I have added a helper to the change_edit_dashboard template to retrieve the users info document from the collection.
Template.change_edit_dashboard.helpers({
formData: function()
{
return Base.findOne({_id: Session.get('baseId')})
}
});
And attempted to call context of document using spacebars {{#with}} in change_edit_dashboard
<template name="change_edit_dashboard">
{{#with formData}}
----> form code <------
{{/with}}
</template>
However a few strange things seem to be happening -
The change_edit_dahsboard template is not rendering at all. The formData helper doesn't seem to be getting called as I am not getting a test console.log() I wrote showing in the console.
With the addition of this code it seems to breaking all functionality of other templates. I have placed test console.log() in various template events and they do not seem to be getting called
Can anyone advise on where I may be going wrong? Thanks
As you did not give us your form code, I'll just assume you correctly gave <input name="myInput" value=myValueHelper> attributes to your inputs and go on.
From what I see, you are trying to get data from one of the most error-prone objects in Meteor, Meteor.users when one wants to retrieve data from a collection object. You can find below some possibilities regarding your issue:
Meteor.user().profile.profComp does not exist. As you are manipulating Meteor.users collection, you have to take a few extra-step in order to access data stored there. For example, if I have a mySuperField field at the root of my Meteor.users model, I have to publish it server-side in order to subscribe and retrieve it on the client. You can quickly check it by calling Meteor.user() and see wether it's present or not.
If you have a blank screen with no error server-side, it's often because you broke your templating load. Check error messages at the top of your web-console and make sure you did not put a template helper in a file executed on the server or before your .html file was loaded by Meteor.
I'm not exactly sure why you need 2 edit templates but if it's just a matter of new vs edit, you should consider using autoform with simple-schema as it will greatly help you through your forms. You can find the links below.
https://github.com/aldeed/meteor-simple-schema
https://github.com/aldeed/meteor-autoform

What is the proper way to integrate dynamic content into the layout.ejs file in a Sails.JS application?

Say I wrote a blog app in Sails.js.
On every page in this app, there is a sidebar widget called "Recent Posts", where it lists the titles of the 5 most recent posts and clicking on them takes you to the post in question.
Because this sidebar widget is present on every page, it should be in layout.ejs. But, here we have a conflict - dynamic content is only supposed to be pulled from the database in the controller action for rendering a specific view.
This dynamic content isn't for a specific view, it's for the whole site (via layout.ejs).
By the conventions that I understand, I'd have to get that dynamic content data for the sidebar widget in every controller action that renders a view (otherwise I would get an undefined error when I attempt to call that local in my layout.ejs file).
Things I've tried / considered:
Load that dynamic content in every controller action that renders a view (this solution is very bad) and calling that dynamic content in layout.ejs as if it were a local for the specific view. This works fine, but goes against D.R.Y. principles and quite frankly is a pain in the ass to have to run the same query to the database in every controller action.
As per another similar stackoverflow question, create a new config (E.G. config/globals.js), load my dynamic content from my database into that config file as a variable, and then calling sails.config.globals.[variable_name] in my layout.ejs file. This also worked, since apparently config variables are available everywhere in the application -- but it 's a hacky solution that I'm not a fan of (the content I'm loading is simply the titles and slugs of 5 recent posts, not a "global config option", as the solution implies).
Run the query to get the dynamic content inside the .EJS file directly between some <% %> tags. I'm not sure if this would work, but even if it did, it goes against the separation of concerns MVC principle and I'd like to avoid doing this if at all possible (if it even works).
As per a lengthy IRC discussion # http://webchat.freenode.net/?channels=sailsjs, it was suggested to create a policy and map that policy to all my controllers. In that policy, query the database for the 5 most recent posts, and set them to the req.recentposts. The problem with this solution is that, while the recent posts data will be passed to every controller, I still have to pass that req.recentposts data to my view -- making it so I still have to modify every single res.view({}) in every action. I don't have to have the database query in every action, which is good, but I still have to add a line of code to every action that renders a view... this isn't D.R.Y. and I'm looking for a better solution.
So, what is the proper solution, without needing to load that dynamic content in every controller action (a solution that adheres to D.R.Y. is what I'm lookng for), to get some dynamic content available to my layout.ejs file?
In folder /config you should create a file express.js and add something like that:
module.exports.express = {
customMiddleware: function(app){
app.use(function(req, res, next){
// or whatever query you need
Posts.find().limit(5).exec(function(err, posts){
res.locals.recentPosts = posts;
// remember about next()
next();
});
});
}
}
Then just make some simple loop in your view:
<% for(var i=0; i<recentPosts.length; i++) { %>
<% recentPosts[i].title %>
<% } %>
Here are some links to proper places in documentation:
https://github.com/balderdashy/sails-docs/blob/0.9/reference/Configuration.md#express
and
https://github.com/balderdashy/sails-docs/blob/0.9/reference/Response.md#reslocals
I found out another way to do this. What I did was to create a service that could render .ejs files to plain html by simply taking advantage of the ejs library already in sails. This service could either be invoked by the controller, or even passed as a function in the locals, and executed from within the .ejs. The service called TopNavBarService would look like:
var ejs = require('ejs');
exports.render = function() {
/* database finds goes here */
var userInfo = {
'username' : 'Kallehopp',
'real_name' : 'Kalle Hoppson'
};
var html = null;
ejs.renderFile('./views/topNavBar.ejs', {'locals':userInfo}, function(err, result) { html = result; });
return html;
}
In the constroller it could look like:
module.exports = {
testAction: function (req, res) {
return res.view('testView', {
renderNavbar: TopNavBarService.render // service function as a local!
});
}
};
This way you can create your customized ejs-helper that could even take arguments (although not shown here). When invoked, the helper could access the database and render a part of the html.
<div>
<%- renderNavbar() %>
</div>

Angular 1.2.0: Error: "Referencing private fields in Angular expressions is disallowed" when attempting to access Mongo _id fields

When attempting to read a mongo _id field from an angular expression:
<tr ng-repeat="person in people">
<td>{{person._id}}</td>
<td>{{person.name}}</td>
<td>{{person.location}}</td>
<td>{{person.active}}</td>
</tr>
the following error is thrown:
"Referencing private fields in Angular expressions is disallowed"
plunker link: http://plnkr.co/edit/dgHNP2rVGPwAltXWklL5
EDIT:
This change has been reverted in Angular 1.2.1: https://github.com/angular/angular.js/commit/4ab16aaaf762e9038803da1f967ac8cb6650727d
The Current Solution
This is due to a breaking change in Angular 1.2.0 ( discussed here: https://github.com/angular/angular.js/commit/3d6a89e )
There's an easy, if not annoying, fix to this.
In your app.run function, you can add a $rootScope level accessors object that contains a function to return the correct Mongo ID.
app.run(function($rootScope) {
$rootScope.accessors = {
getId: function(row) {
return row._id
}
}
});
Then, in your markup, use that method instead of directly accessing the data point:
<tr ng-repeat="person in people">
<td>{{accessors.getId(person)}}</td>
<td>{{person.name}}</td>
<td>{{person.location}}</td>
<td>{{person.active}}</td>
</tr>
A plunker with the fix: http://plnkr.co/edit/NcRrKh
Discussion:
This will allow you to progress forward with your development, but know that it does come with more overhead than if you could just access the variable directly. For small projects, this should not impact performance at any perceptible level, though you might see some measurable amount on larger apps. However, this concern is, as of right now, purely academic.

Drag n Drop re-order ability in meteor.js ToDo app

Obj:-
To add drag n drop ability in the meteor ToDo example app.
Why:-
Going through the learning curve.
What i can think of :-
Using jquery UI (as external js) and bind the update event to todo lists. having a data field on the li items, so as to execute update command from the same function itself.
Was wondering if there exists a more meteor-y approach..
Thanks!
Meteor's templating engine (Spark) would redraw your TODO list on any change to the underlying data, which i expect would mess up normal operation of JQuery UI.
Consider using constant for your JQuery UI managed regions.
From this answer and from Lloyd's answer above, here is the work-around:
<template name="todos">
...code...
{{#constant}}
{{sort_code}}
{{/constant}}
</template>
--
<div class="todo-text" data-id="{{_id}}">{{text}}</div>
in todo.js
Template.todos.sort_code = function(){
Meteor.defer(function(){
$('#item-list').sortable({
update: function(e,iq){
$('div.todo-text',this).each(function(i){
var id = $(this).attr('data-id');
Todos.update(id, {$set:{order:i+1}});
});
},
});
$( "#item-list" ).disableSelection();
console.log('dd');
});
};