Svelte As a First Class Citizen

I am wrapping up the Svelte step by step guide I made (It will be similar to one that I made for Vue for the Meteor site)

And I was wondering if maybe we should make a checklist with some good ideas to get the Svelte as a first class citizen project off the ground.

Todo List

  1. Add a new Meteor Forum Category (Svelte)
  2. Step by Step Guide added to the Main Site
  3. @stolinski suggested a Youtube video Series (this would be a huge deal)
  4. @captainn, @rdb and @klaussner do a great job on the packages end of things (thank you guys for that, without it we wouldn’t have much haha)
  5. A pull request to the documentation adding Svelte to the official Meteor Guide (also a huge deal)
  6. A meteor create --svelte skeleton added

Can anyone else think of any ideas to add to the list?

20 Likes

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:

  1. Recommended packages to use
  2. Recommended utility functions (e.g. I’ve seen a couple approaches to wrapping Meteor’s Tracker)
  3. Recommended router
  4. 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.

2 Likes

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.

10 Likes

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 :slight_smile:

@rdb, I think that your package is brilliant and that this kind of integration was achieved only in Blaze. Truly “first class”.

1 Like

I saw your comments in the git thread - fantastic work! I’m excited to see where this goes.

2 Likes

I saw your package on the 19th. So awesome!

that package will make a big difference in the sample app I am working on. I had been doing everything like this

<script>
  let currentUser;

  Tracker.autorun(() => {
    currentUser = Meteor.user();
  });
</script>

So ill make that update tonight and then hopefully when the guide gets put on the meteor site that will get that package some attention.

5 Likes

Awesome. Just saw swelte. I just like meteor… and an alternative to blaze is defineltely required considering react is taking over…

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>
1 Like

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.

Thanks for the example @rdb

Where does taskStore become a svelte store? Do all Mongo cursors become svelte stores with your package somehow?

I’ll try running your code when I have a moment to see if there might be a bug somewhere.

Yes, with my package, cursors get a subscribe method, and therefore conform to the Svelte store contract.

1 Like

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.

3 Likes

As far as a page in the Meteor guide what we like to include there?

How to use and configure the svelte compiler package
How to use the meteor data package
How to set up SSR like in @captainn’s repo

any thing else we should include? once the tutorial is done Ill write up a first draft for the guide and submit it for review

@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 :slight_smile:

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)}.

1 Like

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.

I was just looking at that package—I’m going to see if I can make a similar meteor-svelte/blaze-integration package.