I’m building a todo app, any user can create its own todos. I display all the todos created by every users in a list. I want to display the todo’s owner username next to the todo content. What’s the proper way to achieve it?
<script>
import { Todos } from '/imports/db';
let todos = [];
let newTodo = '';
$m: todos = Todos.find({}).fetch();
function createTodo() {
Meteor.call('todos.insert', newTodo);
newTodo = '';
}
function toggleDone(todo) {
Meteor.call('todos.setDone', todo._id, !todo.done);
}
</script>
<h1 class="text-2xl font-bold">Home</h1>
<h2 class="text-xl font-bold">Todos ({todos.length})</h2>
<ul>
{#each todos as todo}
<li>
<label>
<input
type="checkbox"
bind:checked={todo.done}
on:change={() => toggleDone(todo)}
/>
<span class:line-through={todo.done}>
<!-- I want to replace the userId by the username -->
{todo.userId} - {todo.content}
</span>
</label>
</li>
{/each}
</ul>
Maybe a dumb question but all the examples I found directly store the a copy of the username in the Todo document which is not very convenient.
Thanks!
Storing a copy of the username (and any other required data) is a typical NoSQL pattern to avoid joins and prepare data immediately for use by the UI.
EDIT: found another solution to experiment with:
Add a “transform” in the options for your query, or directly onto the Collection definition. When publishing the Todos, the transform can look up the username. It will be a bit slow, so caching it ahead of time is probably better but introduces other issues.
Whatever you do, use the Meteor dev tools to check that the client is only getting usernames sent to it and isn’t getting entire user records along with all sorts of sensitive information.
ORIGINAL:
What you would need to do without saving a copy of the username, would be (off the top of my head):
With collections and pub sub
there’s better ways to do this (I think you can make fake collections on the client side? Or add fields to an existing collection via the cursor?) but I’ll go with my naive solution
have a publication that mirrors whatever your todos publication might have, except instead of returning the cursor from todos, you instead go over all the todos and get the userIds
then you make a query in the user docs to get all docs from that array of userIds (should be a Mongo query operator to find any doc with an Id in a certain array)
in your query, make sure you only project/select relevant fields (eg userId, userName), this is in the options of a Meteor Collection query IIRC
return this cursor
subscribe to that publication from the client side
look up userIds to find usernames and display them
With methods (needs some tweaks for your use case)
Use array functions to map over each doc and get its ID, making a list of IDs. Send this list of IDs to a Meteor Method.
Check the current User’s ID and figure out if they’re allowed to see all the usernames they’re about to be sent (if the answer is always yes, just skip this part).
Get all the user docs for all the userIds provided (there’s a MongoDB query operator for finding all documents who have a field found in a specified array).
return a map (plain Js object in this case) of userIds to usernames
store that on the client state (in Svelte you can do a variable assignment but better off would be a store which persists between component lifetimes so it can act like a cache, cf SWR from Vercel)
Welcome Mireille,
There are many ways to achieve this - it depends on how reactive you want it to be. You can do something like this to do a join in your publication but it won’t update if the username changes. If you want more reactivity you could use publish composite