Skip to content

Blog

Hello everyone.

I've created this thread so people can share their awesome workflows using modal forms.

A lot of times people that open feature requests mention they need such feature to complete their workflow, or that such feature will make their workflow simpler, and then they mention what their workflow is about and then blow my mind.

I saw other plugins also have similar threads, so I decided to create this one to encourage people to share their forms and how are they using them.

Anyone is free to provide their experiences however they want, but here are some suggestions that I think will make each post even more useful for everyone:

  • Start the thread with a summary of the workflow or the problem you are solving.
  • If you can, export your form as a JSON file and share it in the thread using a code block. This will make it easier for others to use your form.
  • If you can, share a screenshot of your form. This will make it easier for others to understand what your form looks like and makes it a lot more engaging.
  • If you are calling the form from a template, please share the template as well in another code block.
  • If you are okay about including your post as part of the example vault on modal form repository, please say so, then others just looking at the plugin repository can see your example.

And that's all. I'm very excited about seeing all the different workflows and how you use modal forms, and I hope this will be a great way to share and learn from each other.

Release notes for 1.53.0

Introducing Image Input

This release introduces a brand new input type: the image input! This new component allows you to upload images directly through your forms, automatically saving them to your vault. I'm particularly excited about this new feature because I already have a lot of usecases for it on my daily life (I like to keep a record of the restaurants I visit and the meals I eat there). The possibilities it opens are big and I'm really excited about seeing what new and more visual experiences you can create using it. I also think that having a specialized way of adding images to your notes/templates is better than the obsidian way in a lot of situations.

What's New

The image input is a powerful new addition to our form inputs family that lets you:

  • Save images directly to your vault
  • Preview the image
  • Atomically define where each image will be saved for each input
  • Use templates for file naming with date/time placeholders

Here's how it looks in action: Image input example

There is also a new helper in the FormResult object that simplifies the creation of markdown links to the image. For example, if you have a field called image in your form result, you can use it like this in your templates:

<% result.image.link %>

Using the Image Input

Adding an image input to your form is very straightforward using the FormBuilder. Here's an example screenshot: image in input builder

The configuration has two main settings:

  • filenameTemplate: Define how your files will be named. You can use placeholders like {{date}}, {{time}}, or {{datetime}}.
  • saveLocation: Specify where in your vault the image will be saved. Don't worry if the folder doesn't exist - it will be created automatically!

Drawbacks

As with every first version of every new feature, there is an important drawback: the image input is eager to save the image. This means that the image will be saved as soon as you select it, even if the form is not submitted, or if you pick a different image. This is a tradeoff that simplified the implementation and makes some scenarios simpler , like using the image in a markdown block or in other dynamic inputs.

It is also worth mentioning that the value you get in the form result is not a TFile directly but a wrapped TFile object. This is because a lot of places assume it is safe to just serialize the form results to JSON, which is not the case for the TFile object. The wrapped TFile object has direct access to the most essential properties of the file, like path, name, basename, and extension. The wrapped TFile is also available through the TFile property in the wrapping class FileProxy.

Here is an example of how you can use the wrapped TFile object:

const result = await MF.openForm("my-form");
// assuming your image input is called "image"
const file = result.image.value.TFile;
- ctime: <% file.stat.ctime %>
- mtime: <% file.stat.mtime %>
- size: <% file.stat.size %>

Release notes for 1.52.0

Markdown block

This release introduces a new input, the "markdown block". Just like the existing "document block" this is not a real input, but more a building block for richer forms. The definition is expected to contain a function body returning a markdown string. This markdown string will then be rendered into the form and the user filling it will be able to see it.

To illustrate with an example, imagine a simple form with a single text input, we can define a markdown block with this content:

return `# hello
- line 1
- ${form.text}
- ![[image.png]]`

Have you noticed the image syntax? Yes, images are supported too. This is how it looks once rendered: form containing markdown block

Updates to the existing document block

Both Markdown block and document block now have access to the dataview API (along with the form data itself). This allows to build much complex and data packed information panels. To access the dataview API, you do normally just like in any other place using a special variable named dv. For example, and continuing with the markdown example, we could render a list of all the people (in markdown) using dataview like this:

return dv.markdownList(dv.pages('#person').map(x => x.file.name))

That will render something like this: markdown using dataview

Release notes for 1.51.0

Hidden fields

This release introduces the concept of hidden fields. The intended usage of this feature is to provide values to the form that are not visible to the user. This has some applications such as:

  • providing variables to the form-runtime that your dataview functions can use
  • having certain fields deactivated using the conditional without any user interaction
  • having certain values be part of the generated result, and used in the templates

I bet the community will be able to figure out some other interesting use-cases. The reason I like this approach to much is a) because it is a web standard (this is common practice to communicate between backend and frontend in forms) and b) it plays nicely with all the existing form mechanisms without having to complicate the logic at all: formatting the output, reading the value from dataview functions, conditional rendering other fields, etc.

Hope you like it and find it useful.

Release notes for 1.48.0

This is a very exciting release for me, because it includes one of the features that I have been wanting to implement for a long time, and also one of the most requested features. But it also includes some minor improvements, so let's take those first:

  • The placeholder of the label is set by default to the name of the field. This will make it easier for people to understand that the default label value is the name of the field. name as label placeholder

Now the big feature:

Conditional fields

As with every new feature, I like to start small, so this first version is very simple. It just settles the basic foundation and works only with the most basic field types. This will allow me to gather feedback and improve it in the next releases after making sure that the basic functionality is working as expected.

In this first approach there are not many safeguards either, so you can end up in forms that don't show any field, for example because of fields that are excluding each other. I don't think this is going to be a big problem in practice, but I will be monitoring the feedback to see if it is necessary to add some kind of validation, or at least some kind of warning. The reason I am not adding it any limitations in this first version is because flexibility: forms can be called with parameters to omit fields, default values, etc. and I don't want to limit that flexibility.

Here are some screenshots of the feature in action.

Form builder: boolean condition text condition

Form in preview mode with the condition met condition met with the condition not met condition not met

This first iteration is purely visual: just because a field is hidden it does not mean that, if it has a value, is not going to be included in the result. If you fill a field, and then do something that makes it hidden, the value will still be included in the result. I think in practice most people just needs a way to start with several fields hidden, and then show them based on the value of other fields, so I think this is a good first approach.

The wording of the feature is not final, I'm not very satisfied with the current wording, so I'm open to suggestions. I hope you like it, that it does not introduce too many inconveniences and that it is useful to you.

Please let me know your thoughts and suggestions.

Release notes for 1.47.0 and a statement of intent

Release notes for 1.47.0

This is mostly a technical release that also includes some minor improvements.

The technical part of it is that now the fields of the form are rendered using svelte rather than manually calling DOM manipulation methods or the obsidian Setting class. I made this because it simplifies the creation of the view, it will be more maintainable, but the most important reason is that it will make much much easier to implement functionalities that require atomic re-renders of the form. The most important and requested feature this will enable is the ability to have dynamic fields that depend on the value of other fields. This is not implemented yet, but it will be much easier to do now and I will start working on it soon.

For the minor improvements, I added some more color to the form fields, so now the stars that indicate required fields are colorful.

colorful stars

I also improved the error messages when submitting a form with errors. Now the error message appears below the field in red color:

better field errors

Also related to errors, the notifications that appear when submitting a form with errors are now grouped in a single notification containing all the errors:

grouped notification errors

Statement of intent

When I started this project, I had not much experience with Obsidian and absolute no experience with Obsidian plugin development. Also, my initial idea was to create a very simple plugin, basically something wrapping some basic existing Obsidian functionality and glueing it with some other tools I like to use (templater mainly) while making the process streamlined and more user friendly. Since then, the plugin has gained a lot of popularity, it has some thousand of installs, making it my most popular project by far. Coming from a web development background, I was imposing myself some limitations that I learned that do not make any sense in Obsidian. For example, at first I was very worried about the bundle size of the plugin (which is still very small) and I was trying to avoid using external libraries as much as possible. However, some of the most popular plugins weight several MBs and nobody seems to care. Also, many plugins diverge significantly from the Obsidian default appearance, and that is not a problem at all. What does that mean for modal form? That I will try to build better functionalities, always trying to keep the plugin simple and focused, but I will no longer limit myself to what Obsidian offers out of the box, and I will not be afraid of using external libraries if they make sense.

Hope you like the direction I'm taking with the plugin. I'm very excited about the future of it.

Release notes for 1.46.0

This release contains one little change that was easy to make, but I think that it unlocks a very interesting use case for modal forms. There is a not very well documented feature in the FormEditor that allows to define a specific (and simple) template linked to that form. I implemented it partially for fun, because I love coding parsers but also partially because many times I only need very basic templating functionality and I don't want to have to write another templater template. Also, I find very appealing the idea of having both the form and the template visible and linked in the same place, which is not the case when you use templater templates. Up until now, the only thing you can use this templates for was for creating new notes from that template. You trigger a command, select the form to use, fill it and the template is used to create the note.

But I find myself many times defining a very simple for for easier the insertion of certain data in an existing note. However, for that I was forced to first create the form then create a templater template that uses that form (whith all the boilerplate it entails) and then either tell templater to create a command for that template or use the insert template from templater command. When I saw how easy is to create commands that insert text in the current note I thought it was a no brainer to not take advantage of that with the form templates.

So that is the little but big improvement in this release. You can now use the form templates to insert text in the current note at the cursor position.

To give an example, I have a very simple form for inserting an external link. While the experience of doing this in the Obsidian desktop app is fine, it is a bit tedious to do on mobile.

My form looks as simple as this: External link form

And the template is also very simple, just:

[{{title}}]({{url}})

Now, I can trigger a command that will insert the template in the current note at the cursor position after filling and submitting the form.

Hope you find this useful. I'm already using it a lot.