November Round-up: New JS Editor, Button-group Widget and More Collabs!
7
December
2021
Announcement

November Round-up: New JS Editor, Button-group Widget and More Collabs!

November Round-up: New JS Editor, Button-group Widget and More Collabs!
Vihar Kurama
0
 minutes ↗
#
announcement
#
community
#
js
#
developer
Announcement

It's time for a quick look back on all the stuff that we did in the last 30 days! You know we like to work hard! 😎 We shipped many features, fixed bugs, and hosted some fun events with our community.

JS Editor is Now Live!

We've always strived to be a developer first product, so this is an emotional moment for us on our journey to becoming bigger and better. While it's possible to write JS on Appsmith in the small input boxes on the property pane, as fellow developers building a developer-first product, we know all too well the challenges that come with such an experience. Especially when writing vast chunks of code for transformations, logic, and action-based workflows. So we shipped out a full-fledged JS editor on Appsmith. Here's a quick sneak peek of our all-new JS Editor:

Users can now write full-fledged JS objects to create functions and variables across Appsmith and re-use them. Building this new module was challenging and involved multiple iterations, designs, and vigorous testing. The JS Editor is currently a beta feature, and we can't wait for everyone to use it soon!

Read this blog if you'd like to know more about this feature and why we built it.

New Widgets and Enhancements

New Button Groups Widget: Button Groups is the latest addition to the Appsmith widget family! The buttons are no longer alone on Appsmith. With this new widget, you can create simple buttons or menu buttons with drop-down items. This widget can come in handy when you require a header or perform specific actions on various button clicks. Of course, this widget is also heavily customizable; you can add different styles, add icons, and provide other options under a menu. To find out more about all the things you can do with it, check out the documentation!

Make All Apps Beautiful Again: At Appsmith, we work very hard to provide the best developer experience while building their internal applications with our platform. This month, we had spent a lot of time making things look much better and consistent; these could be minor changes like updating font sizes, increasing paddings, having consistent background colours, etc. But for us, everything matters, and every widget is treated with equal importance; these are consistent, feature-rich, and are tested thoroughly.

SPELING mistakes? We'll Check It! : Even the mighty have fallen with just one spelling error! Our input widget is now loaded with a new superpower! Now it can detect spelling mistakes while in Editor Mode and Preview Mode. This is all thanks to Mozilla's HTML API! To use this, be sure to toggle the Spellcheck property on the input widget.

Bug Fixes and Upgrades

Fixed Issue on selectedRow property while a: First up, we had fixed a small glitch for the selectedRow property on the Table Widget while using the multi-select row option. Now, the selectedRow property considers the last selected row to be its selection while selecting multiple rows on your Table Widget.

More accurate and faster pagination on Table Widget: While binding significant responses on table widgets, our new performance upgrades will make the pagination experience smoother and faster. Not just that, we fixed all bugs that led to displaying the wrong page numbers.

Datasource and Product Updates

No More Additional Region Configuration on S3: Currently, we have a region selector in S3 data sources, and the bucket is selected in S3 queries. This is very confusing since our query fails if we selected region-A in the datasource and it turns out our bucket is in region-B. The mental model breaks here because S3 buckets can ever belong to one and only one region. We have shipped a new feature that automatically detects the region from our service provider to avoid this!

Better Embedding Experience: While embedding Appsmith applications on the web, you can use the embed=true parameter to remove the Appsmith header. However, while navigating through pages inside the embed, the navbar comes back again! The embed=true parameter has now been retained with our latest upgrade. With this, hiding the Appsmith header on embeds can be done in a matter of seconds!

Generate Page Feature for S3: The one-click generate page feature is an awesome feature on Appsmith! With this feature, you can build a CRUD application within a single click by selecting a particular table on the datasource. Now, we have extended support for the S3 datasource to generate CRUD pages on Appsmith. You can now view and select buckets you'd like to create a CRUD page with this.

Dynamic Inputs in the URL field: Managing an entire fleet of Google Sheets? Don't worry; we got you covered. While working with Google Sheets integrations on Appsmith, you can dynamically pass the Sheet ID on the URL property. You can manage multiple Google sheets and perform operations like read/update at runtime with this. Super cool, right? Do check it out and let us know your thoughts!

By the way, I wasn't kidding when I said we were working hard. This blog isn't finished yet. Next up, I'll talk about cool new educational resources and collaborations!

New Collaborations, Technical Content, Videos & Tutorials

We've published some cool tutorials and videos and hosted a few fun events!

  • We built a dashboard on Appsmith to manage and maintain a customizable newsletter using Zapier. Read the full tutorial here, or watch us build it live here.
  • We came up with a cool stack: The SAN Stack (Supbase, Appsmith and n8n) that can help developers build internal tools and add automations, of course with open-source/fair-code! Please read the full blog post here to learn more about how it works.
  • Did you know that on Appsmith, you can call queries and APIs async? If you want to learn more about this, check out our tutorial here.

One last thing, we were able to pull off an incredible event with our friends at Appwrite! Appsmith and Appwrite make an amazing open-source duo for business applications. We built an excellent authenticated dashboard to manage a database with Appsmith as frontend and Appwrite as backend.

Thanks to Matej from Appwrite and Confidence from our team, who successfully built everything live. We thank the entire community for all the positive responses and feedback!

We've got a host of other bug fixes and updates too, be sure to check out our release notes here.

See you next month with more updates! Do join us on Discord, follow us on Twitter, Youtube, and Linkedin to stay up to date.

Evaluating JS in the Browser for a Low Code Product
26
January
2021
Engineering

Evaluating JS in the Browser for a Low Code Product

Evaluating JS in the Browser for a Low Code Product
Hetu Nandu
0
 minutes ↗
#
engineering
#
performance
#
reactjs
#
js
Engineering

Appsmith is an open-source low code platform for developers to build internal apps and workflows.

In Appsmith, our developer users define business logic by writing any Javascript code in between {{ }} dynamic bindings almost anywhere in the app. They can use this while creating SQL queries, APIs, or triggering actions. This functionality lets you control how your app behaves with the least amount of configuration. Underneath the hood, the platform will evaluate all this code in an optimized manner to make sure the app remains performant yet responsive.

Let us take an example of binding a query response to a table widget.

It all starts with the binding brackets {{ }} . When the platform sees these brackets and some code in it, in a widget or action configuration, it will flag the field as a dynamic field so that our evaluator can pick it up later. In our example let us bind usersQuery to usersTable

Screenshot_2020-12-05_at_2.46.08_PM.png

Since we have added this binding in our tableData field, we will flag this field and store it in our widget config

// usersTable config
{
  "usersTable": {
        ...
        "tableData": "{{
            usersQuery.data
                .map(row => ({
                    name: row.name,
                    email: row.email
                }))
            }}",
        "dynaminBindingPathList": [
            {"key": "tableData"}
            ...
        ]
    }
}

In the background, our evaluation listener, always keeps a lookout for such events that would need an evaluation. For our example, this is a scenario that definitely needs an evaluation, so it kicks off our evaluator.

We pass on our current list of app data constructed in what we call as DataTree to the evaluator thread and patiently wait to hear back from it ⏱

// DataTree
{
    "usersQuery": {
        "config": {...},
        "data": [...]
    },
    "usersTable": {
        "tableData": "{{
            usersQuery.data
                .map(row => ({
                    name: row.name,
                    email: row.email
                }))
            }}",
        "dynaminBindingPathList": [{"key": "tableData"}]
    }
}

For performance reasons, we run our evaluation process in a separate background thread with the help of web workers. This ensures that evaluation cycles running longer than 16ms do not hang up the main thread giving the app bandwidth to always respond to user events.

Inside the thread, the event listener gets a wake-up call and gets to work.

  • Get differences: First it will calculate differences in the DataTree from the last time. This will ensure we only process changes and not the whole tree. In our example, we would see the usersTable.tableData has changed and usersTable.dynamicBindingPathList has a new entry. It takes each difference, filters any un-important changes, and processes the rest.
  • Get evaluation order with dependency map: It also maintains a DependencyMap between various entity properties. The evaluator will notice if any bindings have changed and recreate the sort order accordingly.For our example, we will infer that usersTable.tableData now depends on usersQuery.data. This means that the query response should always be evaluated before we can evaluate the table data and that whenever we see a change in the query response, we need to re-evaluate the table data as well
// DependencyMap
  {
      ...
      "usersTable.tableData": ["usersQuery.data"]
  }
  // Evaluation order
  [
      "usersQuery.data",
      "usersTable.tableData"
  ]
  • Evaluate: After creating an optimized evaluation order, we will evaluate the update the tree, in that said order. Evaluation happens via a closed eval function with the whole DataTree acting as its global scope. This is why we can directly reference any object in our DataTree in our code.
// Evaluator

  const code = `
    usersQuery.data.map(row => ({
      name: row.name,
      email: row.email
    }))
  `;
	const scriptToEvaluate = `
    function closedFunction () {
      const result = ${code};
      return result
    }
    closedFunction()
  `;
	const result = eval(scriptToEvaluate);
  • Validate and parse: We always want to make sure the values returned after evaluation to be in the right data type that the widget expects. The ensures the widget always gets predictable data even if your code has returned some errors. This is also needed for any function down the line in the evaluation order, if it refers to this field, will always get a reasonable data type to work with.

And that completes it. At the end of this, we will have a fully evaluated DataTree that we can then send back to the main thread and start listening for any new event to do this whole process again.

// Evaluated DataTree
{
    "usersQuery": {
        "data": [...] 
    }
    "usersTable": {
        "tableData": [...]
    }
}

Our main thread gets an event saying the evaluation is complete, with the new evaluated DataTree which it stores in the app redux state. From here, the widgets pick up their data and render it.

Screenshot_2020-12-05_at_2.46.22_PM.png

Summarizing our philosophy

  • Pull vs Push: While building a low code app builder for varied developers, we thought hard about how the written code works with the rest of the platform. We wanted configuration to be easy to start yet powerful when it needed to be. For this reason, we went with a Pull based architecture rather than Push. What this means is that in most places, you won't have to think about how the data will get to a field. You write code that pulls everything from the global DataTree and sets it to the field where you write it. This way the moment the underlying data changes, it get propagated to all the fields dependant on it and you as a developer do not have to orchestrate ui changes.
  • One-way data flow: Since we are built on top React.js and Redux, we strongly embrace the one-way data flow model. What this means is that you cannot set a table's data directly to that field from some other part of the app. If you do need to update the table, you will have to trigger the query to run, which will then cause the table to re-render with the new data. This helps the code you write easy to reason about and bugs easy to find. It also encapsulates each widget's and action's logic in itself for good separation of concern.