Profile Picture

Sirwan Afifi

Stories from a web developer.

© 2018. Sirwan Afifi All rights reserved.

Shahriban - My first Hackathon project (Challenges, Lessons Learned)

Hackathon Event Image courtesy of LiveH2H

As you probably know, Hackathon is an event that programmers, designers, entrepreneurs to get together for a short period of time to collaborate on a project. The cool part of this event is delivering tasks rapidly, In fact, it’s more like a Marathon for programmers. I have never participated in a hackathon before but I always wanted to give it a try because it is something I have been wanting to do for a long time. Several weeks ago I had the opportunity to participate in such event with my friends Sohail and Arash from Kurdsoftware group. By the way, the event was not supposed to be like a competitive sort of events, We just wanted to do something. In this blog post, I am going to share challenges we faced over the course of the event.

Challenge #1

The first challenge for us was picking a project to implement so we decided to brainstorm as many ideas as possible then we tried to write them down. At this point, we had a big picture of all potential ideas. At the end of the day we ended up to an interesting one, Shahriban; the idea was to implement a platform for reporting local issues in a city. it’s more like a collaborative culture, something a citizen should do but does not have to do, to make their community better. Our goal was to encourage citizens across our city to be involved in process of improving the city. You can read more about Shahriban here(English, Persian, Kurdish):

Shahriban

Challenge #2

The next challenge after choosing the project was the technical side of it, Since we have different types of skills, we were not sure what language/platform to use to implement the project. we decided to keep it simple and follow KISS principle and use something like Wordpress but the problem is that customizing Wordpress takes time and it’s not simple as it sounds. So at the end, we decided to implement it from scratch so we picked PHP for the backend and Vue.js for the frontend. Sohail and Arash worked on the backend and used php-crud-api; a single file PHP script that adds a REST API to a SQL database, So at this point we had a fully fledged RESTful API and the process went very smoothly, we had everything at our disposal quickly. On the frontend, I picked Vue.js because getting up and running with Vue.js is really simple. So I used Vue CLI to generate the project. Over the course of 2 hours we had everything in place:

Shahriban

Challenge #3

The last challenge was to implement an app, Since Telegram is the most popular instant messaging app in Iran, we decided to develop a bot to do the job for us. This was really great because we didn’t have to implement an app for Android, iOS:

Shahriban

By the way, there are some restrictions to access Telegram in Iran, Perhaps we should have developed a native app for both Android and iOS. But the problem is that at the time this was not possible for us because we didn’t have an app developer in our team, I have developed some native Android apps in the past but my main focus these days is on the web so I wasn’t able to do that. If you want to contribute and are interested in, or you know someone who is interested in please let us know.

Conclusion

The whole experience was really great, we learned a lot in the process and feel a lot more confident and prepared for future events. Also in addition to the experience, I think this is a best opportunity to have a little bit impact in our community.

Uploading file in Vue.js

In this blog post I am going to show you how you can upload file using Vue.js. On the backend we will use ASP.NET Core MVC to expose an endpoint to the client to receive file. Let’s say we have a HTML form for saving a coffee, this form contains a file input to accept coffee’s image:

Vue Upload File

To allow the user to pick a file we need to have a reference to the file input field using ref attribute then we can easily access this input using $refs object inside our Vue instance this is like selecting an element using jQuery:

<input required type="file" ref="image">

As you can see we named this input image so we can access the element this way:

this.$refs.image

Before we can submit the form we can encapsulate functionality inside a service:

import axios from 'axios';

class CoffeeService {

    saveCoffee(coffee: any, file: any): any {
        const formData = new FormData();
        for (const key in coffee) {
            if (coffee.hasOwnProperty(key)) {
                formData.append(key, coffee[key]);
            }
        }
        formData.append(file.name, file);

        return axios.post('/Coffees/SaveCoffee', formData);
    }
}

// Export a singletone instance in the global namespace
export const coffeeService = new CoffeeService();

This service has a method called saveCoffee which accepts two parameters one is the coffee itself and the second one is uploaded file then we are using FormData to combine these data together and sending it to the server using axios library. Keep in mind, this method returns a promise which means that the caller can use then syntax to get server’s response. So inside the CoffeeComponent we can use Coffeeservice like this:

<template>
    <div class="container">
        <h3>Add a new Coffee</h3>
        <form v-on:submit.prevent="submitted">
            <div class="form-group">
                <label class="control-label">Coffee Name</label>
                <input required type="text" class="form-control" name="Name" v-model="model.name">
            </div>

            <div class="form-group">
                <label class="control-label">Coffee Type</label>
                <select class="form-control" name="CoffeeType" v-model="model.coffeeType">
                    <option v-for="coffeeType in coffeeTypes" :value="coffeeType.value" v-bind:key="coffeeType.value"></option>
                </select>
            </div>

            <div class="form-group">
                <label class="control-label">Coffee Image</label>
                <input required type="file" multiple class="form-control" name="Image" @change="fileChange" ref="image">
            </div>

            <button class="btn btn-primary" type="submit">Save</button>
        </form>
    </div>
</template>

<script lang="ts">
import Vue from "vue";
import { Component, Prop, Watch } from "vue-property-decorator";
import { coffeeService } from "../services/CoffeeService";
/// <reference path="./toaster.d.ts" />

@Component({
  components: { UploadFileSimpleComponent }
})
export default class UploadFileSimpleComponent extends Vue {
  coffeeTypes = [
    { name: "Espresso", value: 0 },
    { name: "Latte", value: 1 },
    { name: "Mocha", value: 2 }
  ];

  model = {};

  submitted() {
    coffeeService
      .saveCoffee(this.model, (<any>this.$refs.image).files)
      .then(function(response: any) {
        toastr.success(response.data);
      })
      .catch(function(error: any) {
        toastr.error(error);
      });
  }
}
</script>

Showing upload progress

Now we want to show upload progress, axios makes this easy all we need to do is add a third argument to the post method, this argument is actually an object, inside this object we can configure the request inside this object we have access to an event handler called onUploadProgress which we can do the calculation. let’s change the CoffeeService to this:

import axios from 'axios';
import { eventBus } from '../main';

class CoffeeService {

    saveCoffee(coffee: any, file: any): any {
        const formData = new FormData();
        for (const key in coffee) {
            if (coffee.hasOwnProperty(key)) {
                formData.append(key, coffee[key]);
            }
        }

        formData.append(file.name, file);

        let startTime = Date.now();

        return axios.post('/Coffees/SaveCoffee', formData, {
            onUploadProgress: uploadEvent => {
                const queueProgress = Math.round(uploadEvent.loaded / uploadEvent.total * 100);
                const timeElapsed = Date.now() - startTime;
                const uploadSpeedFirst = uploadEvent.loaded / (timeElapsed / 1000);
                const uploadTimeRemaining = Math.ceil(
                    (uploadEvent.total - uploadEvent.loaded) / uploadSpeedFirst
                  );
                const uploadTimeElapsed = Math.ceil(timeElapsed / 1000);
                const uploadSpeed = uploadSpeedFirst / 1024 / 1024;

                eventBus.$emit('uploadData', {
                    queueProgress,
                    uploadTimeRemaining,
                    uploadTimeElapsed,
                    uploadSpeed
                });
            }
        });
    }
}

// Export a singletone instance in the global namespace
export const coffeeService = new CoffeeService();

Inside the method we construct an object, this object will notify all subscribers with current progress. This notification is done using $emit method. We also need to update the template to show this progress:

<div v-if="uploadDetails.queueProgress > 0">
    <table class="table">
        <thead>
        <tr>
            <th width="15%">Event</th>
            <th>Status</th>
        </tr>
        </thead>
        <tbody>
        <tr>
            <td><strong>Elapsed time</strong></td>
            <td nowrap> second(s)</td>
        </tr>
        <tr>
            <td><strong>Remaining time</strong></td>
            <td nowrap> second(s)</td>
        </tr>
        <tr>
            <td><strong>Upload speed</strong></td>
            <td nowrap> MB/s</td>
        </tr>
        <tr>
            <td><strong>Queue progress</strong></td>
            <td>
            <div class="progress-bar progress-bar-info progress-bar-striped" role="progressbar"
                aria-valuemin="0" aria-valuemax="100" :aria-valuenow="uploadDetails.queueProgress"
                :style="{ 'width': uploadDetails.queueProgress + '%' }">
                %
            </div>
            </td>
        </tr>
        </tbody>
    </table>
</div>

The backend is a simple MVC controller, all it does is uploading the file and saving the coffee into the database:

public async Task<IActionResult> SaveCoffee(Coffee coffee)
{
    // Uploading files
    var fileName = await UploadFiles();

    // Saving data
    coffee.Image = fileName;
    _coffeeService.Add(coffee);
    _coffeeService.SaveChanges();

    return Json("Coffee has been saved!");
}

private async Task<string> UploadFiles()
{
    var uploadsRootFolder = Path.Combine(_environment.WebRootPath, "uploads");
    if (!Directory.Exists(uploadsRootFolder))
    {
        Directory.CreateDirectory(uploadsRootFolder);
    }

    var files = Request.Form.Files;
    foreach (var file in files)

    {
        if (file == null || file.Length == 0)
        {
            continue;
        }

        var filePath = Path.Combine(uploadsRootFolder, file.FileName);
        using (var fileStream = new FileStream(filePath, FileMode.Create))
        {
            await file.CopyToAsync(fileStream).ConfigureAwait(false);
            return file.FileName;
        }
    }

    return string.Empty;
}

Upload file using Vue.js

You can grab the working sample project from GitHub.

TypeScript and Vue.js

Webpack

You might be wondering why do we need to worry about yet another framework when we already know there are things like Angular, React, … the answer is simplicity. I have used Angular in some of my previous projects by Angular I mean the first version of it (AngularJS 1.x) but these days I feel like Angular team is going to force developers to migrate to Angular (2, 3, 4, 5, 6, …). I really like Vue.js, it’s really a great one because I think Vue.js is Declarative, Easy to Maintain and Powerful. Also, the integration between Vue and TypeScript is really good. Just like other frameworks Vue also has CLI which helps you to scaffold your project quickly. In this blog post, I would like to show how to combine Vue.js with TypeScript inside an ASP.NET Core 2.x application.

Project Setup

The easiest way to get started is by using dotnet cli tool to create a project:

dotnet new mvc --name aspnet-vue-typescript

Then cd to that directory and then use code . to open VSCode. At this point the project structure looks like this:

Sample app

To get started with our client side code we need to install some packages, for doing so we need package.json file:

{
  "name": "aspnet-vue-typescript",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Sirwan Afifi",
  "license": "ISC",
  "dependencies": {
    "ts-loader": "^4.3.0",
    "typescript": "^2.8.3",
    "vue": "^2.5.16",
    "vue-class-component": "^6.2.0",
    "vue-property-decorator": "^6.1.0",
    "webpack": "^4.9.1",
    "webpack-dev-server": "^3.1.4"
  },
  "devDependencies": {
    "aspnet-webpack": "^2.0.3",
    "css-loader": "^0.28.11",
    "file-loader": "^1.1.11",
    "sass-loader": "^7.0.1",
    "style-loader": "^0.21.0",
    "vue-loader": "^15.2.1",
    "vue-template-compiler": "^2.5.16",
    "webpack-cli": "^2.1.4",
    "webpack-hot-middleware": "^2.22.2"
  }
}

Once you installed this packages using npm install, we’ll create our webpack.config.js file:

let webpack = require('webpack');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
let path = require('path');

module.exports = {
    entry: {
        main: './ClientApp/main'
    },
    output: {
        path: path.resolve(__dirname, 'wwwroot', 'js'),
        filename: '[name].js',
        publicPath: '/js/'
    },
    module: {
        rules: [
            { test: /\.vue$/, loader: "vue-loader" },
            {
                test: /\.tsx?$/,
                loader: 'ts-loader',
                exclude: /node_modules/,
                options: { appendTsSuffixTo: [/\.vue$/] }
            }
        ]
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js'],
        alias: {
            'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
        }
    },
    optimization: {
        splitChunks: {
            cacheGroups: {
                commons: {
                    test: /[\\/]node_modules[\\/]/,
                    name: "main",
                    chunks: "all"
                }
            }
        }
    },
    plugins: [
        new VueLoaderPlugin()
    ]
};

As you can see we’re telling webpack where the entry point is, so create a new directory inside the project called ClientApp, this directory contains all Vue’s related files, Inside this directory, create a ts file called main.ts this file is going to be our entry point:

import Vue from 'vue';
import MyComponent from './components/MyComponent.vue';


Vue.config.productionTip = false;

const v = new Vue({
    el: '#app',
    data() {
        return {
            name: 'Sirwan'
        }
    },
    components: {
        MyComponent
    }
});

Next, we have specified the output which is bundle.js file, this file doesn’t yet exist, it’s going to be created by webpack. Then we have specified TypeScript loader for webpack. Next thing left to do is adding tsconfig.json file to the project:

{
    "compilerOptions": {
        "sourceMap": true,
        "noImplicitReturns": true,
        "noImplicitAny": true,
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "target": "es5",
        "strict": true,
        "module": "es2015",
        "moduleResolution": "node",
        "lib": [
            "es2016",
            "dom"
        ]
    },
    "exclude": [
        "node_modules",
        "wwwroot"
    ]
}

At this point if you run npx webpack you’ll see the bundle file:

Webpack

Now we can add a script reference inside Views/Shared/_Layout.cshtml to bundle.js file:

<script src="~/js/bundle.js" asp-append-version="true"></script>

Now we can use Vue inside our views:

@{
    ViewData["Title"] = "Home Page";
}

<div class="jumbotron">
  <h1 class="display-4">Hello, </h1>
</div>

Hot Module Replacement

Webpack has something called Hot Module Replacement:

Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running, without a full reload. This can significantly speed up development in a few ways: Retain application state which is lost during a full reload. Save valuable development time by only updating what’s changed. Tweak styling faster – almost comparable to changing styles in the browser’s debugger.

For adding this functionality we need to install webpack-hot-middleware package:

npm i -D webpack-hot-middleware

Then we need to register this component into MVC’s HTTP request pipeline in the Configure method, for doing this we need to install Microsoft.AspNetCore.SpaServices:

app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
    HotModuleReplacement = true
});

This middleware looks for webpack file and automatically executes it for us when we change client side code:

Webpack