Categories
Express

Error Handling with Express

Like with any other apps, we have to make Express apps ready to handle errors like unexpected inputs or file errors.

In this article, we’ll look at how to handle errors with Express.

Catching Errors

Error handling is the process of processing any errors that comes up both synchronously and asynchronously. Express comes with a default error handler so that we don’t have to write our own.

For example, if we throw errors in our route handlers as follows:

app.get('/', (req, res, next) => {
  throw new Error('error');
});

Express will catch it and proceed. We should see error instead of the app crashing.

For asynchronous errors, we have to call next to pass the error to Express as follows:

app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      throw new Error('error');
    }
    catch (ex) {
      next(ex);
    }
  })
});

The code above will throw an error in the setTimeout callback and the catch block has the next call with the error passed in to call the built-in error handler.

We should see error instead of the app crashing.

Likewise, we have to catch rejected promises. We can do it as follows:

app.get('/', (req, res, next) => {
  Promise
    .reject('error')
    .catch(next)
});

Or with the async and await syntax, we can write the following:

app.get('/', async (req, res, next) => {
  try {
    await Promise.reject('error');
  }
  catch (ex) {
    next(ex);
  }
});

We should see error displayed instead of the app crashing with the stack trace.

The same logic also applies to routes with a chain of event handlers. We can call next as follows:

app.get('/', [
  (req, res, next) => {
    setTimeout(() => {
      try {
        throw new Error('error');
      }
      catch (ex) {
        next(ex);
      }
    })
  },
  (req, res) => {
    res.send('foo');
  }
]);

We should see the error displayed with the stack trace.

Default Error Handler

The default error handler catches the error when we call next and don’t handle it with a custom error handler.

The stack trace isn’t displayed in production environments.

If we want to send a different response than the default, we have to write our own error handler.

The only difference between route handlers, middleware and error handlers is that error handler has the err parameter before the request parameter that contains error data.

We can write a simple route with a custom event handler as follows:

app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      throw new Error('error');
    }
    catch (ex) {
      next(ex);
    }
  })
});

app.use((err, req, res, next) => {
  res.status(500).send('Error!')
})

Note that we have the error handler below the route. The order is important. It has to below all the routes that we want to handle with it so that the error handler will get called.

We can write more than one custom error handler as follows:

app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      throw new Error('error');
    }
    catch (ex) {
      next(ex);
    }
  })
});

app.use((err, req, res, next) => {
  if (req.foo) {
    res.status(500).send('Fail!');
  }
  else {
    next(err);
  }
})

app.use((err, req, res, next) => {
  res.status(500).send('Error!')
})

What we have above is that if req.xhr is truthy in the first error handler, then it’ll send the Fail! response and not proceed to the second one. Otherwise, the second one will be called by calling next .

So if we add req.foo = true before the setTimeout in our route handler to have:

app.get('/', (req, res, next) => {
  req.foo = true;
  setTimeout(() => {
    try {
      throw new Error('error');
    }
    catch (ex) {
      next(ex);
    }
  })
});

Then we get Fail! . Otherwise, we get Error! .

Calling next will skip to the error handler even if there’re other route handlers in the chain.

Conclusion

To handle errors, we should call next to delegate the error handling to the default event handler if no custom event handler is defined.

We can also define our own error handler function by creating a function that has the err parameter before, req , res , and next . The err parameter has the error object passed from next .

Error handlers have to be placed after all the regular route handling code so that they’ll get run.

Also, we can have multiple error handlers. If we call next on it, then it’ll proceed to the next error handler.

Categories
JavaScript Tips

Even More JavaScript Shorthands

With the latest versions of JavaScript, the language has introduced more syntactic sugar. In this article, we’ll look at handy shortcuts that are easy to read from versions of JavaScript new and old. With them, it’ll save us time and make our code easier to read.

In this article, we’ll look at object properties assignment, finding the index of a function, replacing switch statements with objects, merging objects compacting if statements and more.

Object Properties Assignment

We can use the Object.assign method to shallow copy an object from a new object to another.

For example, if we have the following object:

const foo = {
  foo: 1
}

We can clone it as follows:

const copied = Object.assign({}, foo);

Cloning will copy the structure of its own properties and will prevent the modification of the original object.

This means modifying copied won’t modify foo .

It’s a shallow clone so nested objects aren’t cloned.

We can merge 2 objects into one and return it with Object.assign . For example, if we have the following 2 objects:

const foo = {
  foo: 1
}

const bar = {
  bar: 1
}

Then we can merge them into one and return a new object with the merged structure by running:

const merged = Object.assign(foo, bar);

Then we get:

{foo: 1, bar: 1}

which don’t reference the original objects.

IndexOf and findIndex

We can use the indexOf method to find the location of a primitive element in the array. It returns -1 if it’s not found and the first index that the value if found if it exists.

For example, if we have the following array:

const arr = [1, 2, 3, 4, 1];

Then arr.indexOf(1) will get us 0.

For arrays of objects, we can use the findIndex method to do the same thing. For instance, given the following array:

const people = [{
    name: 'Joe',
    age: 10
  },
  {
    name: 'Joe',
    age: 11
  },
  {
    name: 'Mary',
    age: 13
  },
]

We can use the findIndex method as follows:

people.findIndex(p => p.name === 'Joe')

Then we get 0 since the first entry that has 'Joe' in the name property is the first entry.

Object.entries()

With ES2017, we can get all the key-value pairs of an object by using the Object.entries method. It gets its own key-value pairs and not its prototype.

For example, we can write the following code:

const obj = {
  a: 'foo',
  b: 'bar',
  c: 'baz'
};
const arr = Object.entries(obj);

Then we get back:

["a", "foo"]
["b", "bar"]
["c", "baz"]

Then first entry is the key and the second is the value.

Object.values()

We can use the Object.values method to get the values of the object.

For example, if we have:

const obj = {
  a: 'foo',
  b: 'bar',
  c: 'baz'
};
const arr = Object.values(obj);

Then we get back:

["foo", "bar", "baz"]

, which are the values of the object properties.

Getting Characters of a String Literal

We can either use the charAt method or the bracket notation to do this.

For example, we can write:

'foo'.charAt(0);

or:

'foo'[0];

Then both get us 'f' , which is the first character of 'foo' .

Compact Alternative to Switch

We can use an object as a compact alternative to switch statements.

For example, if we have:

const apple = () => console.log('apple');
const orange = () => console.log('orange');
const grape = () => console.log('grape');
const fruit = 'apple';

switch (fruit) {
  case 'apple':
    apple();
    break;
  case 'orange':
    orange();
    break;
  case 'grape':
    grape();
    break;
  default:
    return;
}

We can replace the switch statement with a plain object:

const fruit = 'apple';

const dict = {
  apple() {
    console.log('apple');
  },
  orange() {
    console.log('orange');
  },
  grape() {
    console.log('grape');
  },
}

dict[fruit]();

Replace if’s with indexOf or includes

If we have something like:

const foo = 1;
if (foo == 1 || foo == 5 || foo == 7 || foo == 12) {
  console.log(foo);
}

We can replace with the indexOf method as follows:

const foo = 1;
if ([1, 5, 7, 12].indexOf(foo) !== -1) {
  console.log(foo);
}

-1 means that that value isn’t found in the array. Any other value indicates the index that the value is located at.

Or we can use the includes method:

const foo = 1;
if ([1, 5, 7, 12].includes(foo)) {
  console.log(foo);
}

includes returns a boolean. It’s true if the value is found in the array and false otherwise.

We can get the characters of a string literal with the charAt method or with the bracket notation.

switch statements can be replaced with objects that act as dictionaries.

With newer versions of JavaScript, we can get an object’s values and key-value pairs, with the Object.values method and Object.entries method respectively.

To get the index of the first occurrence of an array element, we can use the indexOf method for primitives and findIndex for objects.

To copy and merge objects, we can use the Object.assign method.

Categories
Quasar

Developing Vue Apps with the Quasar Library — Virtual Scrolling

Quasar is a popular Vue UI library for developing good looking Vue apps.

In this article, we’ll take a look at how to create Vue apps with the Quasar UI library.

Horizontal Virtual Scrolling

We can add the virtual-scroll-horizontal prop to make the virtual scrolling container horizontal:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-virtual-scroll
        style="max-height: 300px;"
        :items="heavyList"
        separator
        virtual-scroll-horizontal
      >
        <template v-slot="{ item, index }">
          <q-item :key="index" dense>
            <q-item-section>
              <q-item-label>
                #{{ index }} - {{ item.label }}
              </q-item-label>
            </q-item-section>
          </q-item>
        </template>
      </q-virtual-scroll>
    </div>
    <script>
      const maxSize = 10000;
      const heavyList = [];

      for (let i = 0; i < maxSize; i++) {
        heavyList.push({
          label: `option ${i}`
        });
      }

      new Vue({
        el: "#q-app",
        data: {
          heavyList
        }
      });
    </script>
  </body>
</html>

Customized Item Template

We can customize the item template to display items the way we want:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-virtual-scroll style="max-height: 300px;" :items="heavyList" separator>
        <template v-slot="{ item, index }">
          <q-banner
            v-if="item.banner === true"
            class="bg-black text-white q-py-xl"
            :key="index"
          >
            #{{ index }} - {{ item.label }}
          </q-banner>

          <q-item v-else :key="index" dense clickable>
            <q-item-section>
              <q-item-label>
                #{{ index }} - {{ item.label }}
              </q-item-label>
            </q-item-section>
          </q-item>
        </template>
      </q-virtual-scroll>
    </div>
    <script>
      const maxSize = 10000;
      const heavyList = [];

      for (let i = 0; i < maxSize; i++) {
        heavyList.push({
          label: `option ${i}`,
          banner: i === 0
        });
      }

      new Vue({
        el: "#q-app",
        data: {
          heavyList
        }
      });
    </script>
  </body>
</html>

We just put the item template in the default slot.

Table Style Virtual Scrolling Container

Also, we can display the items in a table style virtual scrolling container:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-virtual-scroll
        type="table"
        style="max-height: 70vh;"
        :virtual-scroll-item-size="48"
        :virtual-scroll-sticky-size-start="48"
        :virtual-scroll-sticky-size-end="32"
        :items="heavyList"
      >
        <template v-slot="{ item: row, index }">
          <tr :key="index">
            <td>#{{ index }}</td>
            <td v-for="col in columns" :key="index + '-' + col">
              {{ row[col] }}
            </td>
          </tr>
        </template>
      </q-virtual-scroll>
    </div>
    <script>
      const data = [
        {
          name: "Frozen Yogurt",
          calories: 159,
          fat: 6.0,
          carbs: 24
        },
        {
          name: "Ice cream sandwich",
          calories: 237,
          fat: 9.0,
          carbs: 37
        },
        {
          name: "Eclair",
          calories: 262,
          fat: 16.0,
          carbs: 23
        },
        {
          name: "Cupcake",
          calories: 305,
          fat: 3.7,
          carbs: 67
        },
        {
          name: "Gingerbread",
          calories: 356,
          fat: 16.0,
          carbs: 49
        },
        {
          name: "Jelly bean",
          calories: 375,
          fat: 0.0,
          carbs: 94
        },
        {
          name: "Lollipop",
          calories: 392,
          fat: 0.2,
          carbs: 98
        },
        {
          name: "Honeycomb",
          calories: 408,
          fat: 3.2,
          carbs: 87
        },
        {
          name: "Donut",
          calories: 452,
          fat: 25.0,
          carbs: 51
        },
        {
          name: "KitKat",
          calories: 518,
          fat: 26.0,
          carbs: 65
        }
      ];

      const columns = ["name", "calories", "fat", "carbs"];

      const heavyList = [];
      for (let i = 0; i <= 1000; i++) {
        heavyList.push(...data);
      }
      Object.freeze(heavyList);
      Object.freeze(columns);

      new Vue({
        el: "#q-app",
        data: {
          columns,
          heavyList
        }
      });
    </script>
  </body>
</html>

We render the columns in the default slot.

And we set the virtual-scroll-item-size prop to change height or width of the item in pixels, depending on if the list is vertical or horizontal respectively

The virtual-scroll-sticky-size-start prop to change the height or width of the sticky part in pixels, depending on if the list is vertical or horizontal respectively.

And the virtual-scroll-sticky-size-end prop to change the height or width of the bottom sticky part in pixels, depending on if the list is vertical or horizontal respectively.

Conclusion

We can add a virtual scrolling container with various styles with Quasar’s q-virtual-scroll component.

Categories
Quasar

Developing Vue Apps with the Quasar Library — Uploader, Video, and Virtual Scrolling

Quasar is a popular Vue UI library for developing good looking Vue apps.

In this article, we’ll take a look at how to create Vue apps with the Quasar UI library.

Customized Uploader Header

We can customize the header by populating the header slot:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-uploader url="http://localhost/upload" label="Custom header" multiple>
        <template v-slot:header="scope">
          <div class="row no-wrap items-center q-pa-sm q-gutter-xs">
            <q-btn
              v-if="scope.queuedFiles.length > 0"
              icon="clear_all"
              @click="scope.removeQueuedFiles"
              round
              dense
              flat
            >
              <q-tooltip>Clear All</q-tooltip>
            </q-btn>
            <q-btn
              v-if="scope.uploadedFiles.length > 0"
              icon="done_all"
              @click="scope.removeUploadedFiles"
              round
              dense
              flat
            >
              <q-tooltip>Remove Uploaded Files</q-tooltip>
            </q-btn>
            <q-spinner
              v-if="scope.isUploading"
              class="q-uploader__spinner"
            ></q-spinner>
            <div class="col">
              <div class="q-uploader__title">Upload your files</div>
              <div class="q-uploader__subtitle">
                {{ scope.uploadSizeLabel }} / {{ scope.uploadProgressLabel }}
              </div>
            </div>
            <q-btn
              v-if="scope.canAddFiles"
              type="a"
              icon="add_box"
              round
              dense
              flat
            >
              <q-uploader-add-trigger></q-uploader-add-trigger>
              <q-tooltip>Pick Files</q-tooltip>
            </q-btn>
            <q-btn
              v-if="scope.canUpload"
              icon="cloud_upload"
              @click="scope.upload"
              round
              dense
              flat
            >
              <q-tooltip>Upload Files</q-tooltip>
            </q-btn>

            <q-btn
              v-if="scope.isUploading"
              icon="clear"
              @click="scope.abort"
              round
              dense
              flat
            >
              <q-tooltip>Abort Upload</q-tooltip>
            </q-btn>
          </div>
        </template>
      </q-uploader>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {},
        methods: {}
      });
    </script>
  </body>
</html>

scope.queuedFiles has an array of files added for upload.

scope.removeQueuedFiles is a method to remove the queued files.

scope.uploadedFiles has an array of the uploaded files.

scope.removeQueuedFiles is a method to remove the uploaded files.

scope.canAddFiles is a boolean that indicates if we can add files.

scope.uploadSizeLabel has the total size of the files uploaded.

scope.uploadProgressLabel has the upload progress.

scope.canUpload lets us know if we can upload.

Video

Quasar comes with a video component to let us embed videos.

For example, we can add a YouTube video by writing:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-video src="https://www.youtube.com/embed/KfYrKGPUi94"> </q-video>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {},
        methods: {}
      });
    </script>
  </body>
</html>

We add it by setting src to the embed URL.

We can set the aspect ratio with the ratio prop:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-video :ratio="16/9" src="https://www.youtube.com/embed/KfYrKGPUi94">
      </q-video>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {},
        methods: {}
      });
    </script>
  </body>
</html>

Virtual Scrolling

Quasar comes with a virtual scrolling container component.

It lets us render data that are only shown on the screen instead of rendering everything, increasing performance.

For example, we write:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-virtual-scroll style="max-height: 300px;" :items="heavyList" separator>
        <template v-slot="{ item, index }">
          <q-item :key="index" dense>
            <q-item-section>
              <q-item-label>
                #{{ index }} - {{ item.label }}
              </q-item-label>
            </q-item-section>
          </q-item>
        </template>
      </q-virtual-scroll>
    </div>
    <script>
      const maxSize = 10000;
      const heavyList = [];

      for (let i = 0; i < maxSize; i++) {
        heavyList.push({
          label: `option ${i}`
        });
      }

      new Vue({
        el: "#q-app",
        data: {
          heavyList
        }
      });
    </script>
  </body>
</html>

to add it.

We add the item into the default slot to render it.

items has the items.

separator adds a separator between items.

Conclusion

We can add a file uploader into our Vue app with Quasar’s q-uploader component.

Also, we can add videos and virtual scrolling into our Vue app with Quasar.

Categories
Quasar

Developing Vue Apps with the Quasar Library — Uploader

Quasar is a popular Vue UI library for developing good looking Vue apps.

In this article, we’ll take a look at how to create Vue apps with the Quasar UI library.

Uploader

We can add an upload widget into our Vue app with Quasar’s q-uploader component.

For instance, we can write:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-uploader
        url="http://localhost/upload"
        label="Upload files"
        color="purple"
        square
        flat
        bordered
        style="max-width: 300px;"
      >
      </q-uploader>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {}
      });
    </script>
  </body>
</html>

We set the URL to upload to with the url prop.

label is displayed on the widget.

color changes the color of the top var.

bordered, square and flat change the appearance of the widget.

We can enable multiple uploads with the multiple prop.

And the batch prop lets us upload items in parallel.

Restrict File Type

We can restrict the file type allowed with the accept prop:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-uploader
        url="http://localhost/upload"
        label="Upload files"
        color="purple"
        square
        flat
        bordered
        style="max-width: 300px;"
        accept=".jpg, image/*"
        @rejected="onRejected"
      >
      </q-uploader>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {},
        methods: {
          onRejected(files) {
            console.log(files);
          }
        }
      });
    </script>
  </body>
</html>

We set the accept prop to the MIME types.

And the rejected event listener has the rejected files in an array in the first parameter of the listener function.

We can restrict the file size of the files that are accepted with the filter prop:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-uploader
        url="http://localhost/upload"
        label="Upload files"
        color="purple"
        square
        flat
        bordered
        style="max-width: 300px;"
        :filter="checkFileSize"
        @rejected="onRejected"
      >
      </q-uploader>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {},
        methods: {
          onRejected(files) {
            console.log(files);
          },
          checkFileSize(files) {
            return files.filter((file) => file.size < 1000);
          }
        }
      });
    </script>
  </body>
</html>

We pass the checkFileSize method to chekc the file size.

The size property is in bytes.

Async Factory Function

We can add an async factory function to create the q-uploader :

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-uploader :factory="factoryFn" multiple> </q-uploader>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {},
        methods: {
          factoryFn(files) {
            return Promise.resolve({
              url: "http://localhost/upload"
            });
          }
        }
      });
    </script>
  </body>
</html>

We return a promise in the factoryFn method and pass the fucntion to the factory prop.

This will apply the settings in the resolved object.

Conclusion

We can add a file uploader into our Vue app with Quasar’s q-uploader component.