Thanks for putting together the guide. Looking forward to taking a look. I suppose the step by step guide will include this or maybe itās part of what you mean by #4:
Recommended packages to use
Recommended utility functions (e.g. Iāve seen a couple approaches to wrapping Meteorās Tracker)
Recommended router
Would also like to see an advanced section for SSR / hydration and usage of Ground:DB
Apart from the guide, I think a Svelte version of user accounts core and unstyled templates https://github.com/meteor-useraccounts/core/blob/master/Guide.md would be great. Would prefer not to need to reinvent the wheel or figure out a way for Blaze to play nicely.
I am really excited about Meteor and Svelte, will be doing a lot of work with the two in the coming months, and would really like to help out with making Svelte a first-class citizen in Meteor.
The existing svelte:tracker package does not work with Svelte 3. I just published a package that integrates Meteorās reactivity with Svelteās: https://atmospherejs.com/rdb/svelte-meteor-data
At the moment, it provides facilities for exposing Tracker, Session, ReactiveVar, and Mongo cursors as Svelte stores, which makes it easy to get two-way interaction between Meteorās and Svelteās reactivity.
It looks like some of the other Svelte packages have not been updated in a while. I would love to help out with maintenance of those if needed.
I will check out that package, i am using Tracker in my step by step guide directly at the moment but I will make a branch using your package and see how it comes out.
I think getting Svelte added to the tutorials on the meteor.com site and to the Office Documentation will be a good way to advertise to people new to Meteor that Svelte is a viable option. then once more people are using Svelte the package ecosystem will hopefully grow to support those users. thanks for the contribution
I saw your sample app on GitHub; I noticed that your App.svelte still had some unnecessary boilerplate. You donāt need to manually stop the autorun computation (my package does that for you), nor do you need the manual dependency tracking. As of todayās version, my package allows putting Tracker.autorun under $:, but actually, you can avoid using Tracker.autorun in most cases, since both useTracker and Cursor will more gracefully handle bidirectional dependencies and should therefore work inside $:. The below version cuts 16 lines from App.svelte:
App.svelte
<script>
import { Meteor } from "meteor/meteor";
import { useTracker } from 'meteor/rdb:svelte-meteor-data';
import { Template } from 'meteor/templating';
import { onMount } from 'svelte';
import Task from './Task.svelte';
import { Tasks } from '../api/tasks.js'
let newTask = "";
let hideCompleted = false;
let tasks = [];
onMount(async () => {
const id = document.getElementById("loginTemplate");
Blaze.render(Template.loginButtons, id);
Meteor.subscribe('tasks');
});
$: incompleteCount = useTracker(() => Tasks.find({ checked: { $ne: true } }).count());
$: currentUser = useTracker(() => Meteor.user());
const taskStore = Tasks.find({}, { sort: { createdAt: -1 } });
$: {
tasks = $taskStore;
if (hideCompleted) {
tasks = tasks.filter(task => !task.checked);
}
};
function handleSubmit(event) {
Meteor.call("tasks.insert", newTask);
// Clear form
newTask = "";
};
function showPrivateButton(task) {
let isPrivate = false;
if ($currentUser) {
isPrivate = task.owner === $currentUser._id;
}
return isPrivate;
}
</script>
<div class="container">
<header>
<h1>Todo List ({ $incompleteCount })</h1>
<label className="hide-completed">
<input
type="checkbox"
bind:checked={hideCompleted}
/>
Hide Completed Tasks
</label>
<span id="loginTemplate"></span>
{#if $currentUser}
<form class="new-task" on:submit|preventDefault={handleSubmit}>
<input
type="text"
placeholder="Type to add new tasks"
bind:value={newTask}
/>
</form>
{/if}
</header>
<ul>
{#each tasks as task}
<Task
key={task._id}
task={task}
showPrivateButton={showPrivateButton(task)}
/>
{/each}
</ul>
</div>
When i would set the currentUser like you have it in the snippet I had a bug. I think it might have been due to the way the Blaze template is embedded in the Svelte component. It doesnt always work without issues. Like I would have to refresh the page cause the sign in button would freeze up if i signed in, added a task and then signed out ā¦ or the public/private buttons wouldnt show up correctly when i signed in.
But i like the refactor with the taskStore. that looks much better. Let me test the updates you made and see how it works.
I pushed up the changes you had suggested if you want to work off that copy. Once that issue is resolved it should be ready for me to write the steps up. and then Ill ask @filipenevola to take a look and get it on the site
Itās worth noting that @rdbās patch to Cursor.prototype to add that subscribe method might be perfect for other integrations. It might be exactly what is needed to use Reactās useSubscription for example. When I get some time Iāll look in to it. The point is, it might be worth including that in the Core Meteor implementation of Cursor, rather than keeping it monkey patched.
@rdb if I add in a reference to the currentUser variable in the āreactive computationā that generates the task array it behaves as I want it to. Because whenever the user signs in or out that tasks array gets recomputed. Can you think of a better way to achieve this effect? I am still new to Svelte so any help would be appreciated
But once this bug is fixed the step by step guide can go live shortly after and I can start writing up a Meteor Guide page. So svelte isnt far away from getting a marketing push
$: currentUser = useTracker(() => Meteor.user());
const taskStore = Tasks.find({}, { sort: { createdAt: -1 } });
$: {
let user = $currentUser; <- this is what I was referring to
tasks = $taskStore;
if (hideCompleted) {
tasks = tasks.filter(task => !task.checked);
}
};
I have been digging into Svelteās documentation and I cant find a way to trigger that reactive statement unless the variable that has changed is inside of it. I think that may be how I publish the tutorial until a better solution presents itself. Maybe with more people looking at it someone will tell me how to improve it and ill submit the change
Iām also pretty new to Svelte, but one problem Iām encountering when playing around with your code is that unlike in Blaze, Svelte functions are not reactive. So Svelte wonāt rerun a helper function (like showPrivateButton) when one of its input values changed. I think youāre meant to move this button into a child component, or something like that. Otherwise, when you log in, it wonāt show up the buttons right away.
As for why Blaze isnāt updating, Iām not sure, I must say Iāve never used Blaze.render before. Iām planning to create a svelte:blaze-integration package if I find time at some point. Such a package would allow using Blaze templates directly as components, or something like that, and vice versa.
But perhaps itās better to just redo the template in Svelte for this demo? Maybe we ought to have a svelte:accounts-ui? Hmm, let me see if I have some time to play with this sometime next week.
As an aside, you probably also want to use the keyed each format {#each tasks as task (task._id)}.
When i started having trouble with the accounts-ui packages loginButtons Blaze template āfreezingā up on me I thought this package might provide some insight into how Vue is able to work so well with Blaze Templates. I am going to review a little further and see a similiar package could be made for Svelte.
I will try making a child component for the button though and see if that leads to a cleaner deisgn than what I did here
$: {
let user = $currentUser; ā this is what I was referring to
tasks = $taskStore;
if (hideCompleted) {
tasks = tasks.filter(task => !task.checked);
}
};
Cause this will achieve the effect I want its just kind of hacky
Update
Instead of passing in the boolean as a prop to the Task component I just made a reactive computation in the Task component that is updated by the currentUser field (which the Task component also tracks now in addiotn to the App component doing so) to set that boolean value. And this works as well and seems a little better in terms of its design.