David Berri's Blog https://dberri.com/feed Software development tips đŸ‘šđŸ»â€đŸ’» Wed, 27 Jan 2021 21:18:29 GMT https://validator.w3.org/feed/docs/rss2.html https://github.com/nuxt-community/feed-module en <![CDATA[How to look for a branch in git]]> https://dberri.com/how-to-look-for-a-branch-in-git https://dberri.com/how-to-look-for-a-branch-in-git Fri, 31 Jan 2020 22:09:00 GMT Your coworker created a new feature and asked you to review it. How to look for a branch that someone else created and pushed to the remote repository using Git? It's as simple as running the command:

git fetch origin

(Assuming that the remote repository is called "origin")
Then, you can checkout the branch the the other person was working on with:

git checkout new-feature

(Assuming the name of the new branch Ă© “new-feature” - it's going to show up once you run the first command).

If you need to list all the branches that were pulled from the remote repository, you can use git branch.

]]>
<![CDATA[How to use git cherry-pick]]> https://dberri.com/how-to-use https://dberri.com/how-to-use Fri, 14 Feb 2020 22:26:00 GMT This command is extremely useful when you need only one or few commits from one branch in another branch, but don't want or cannot merge.

Let'' say that in your application you use the branch master to deploy to production and develop new code in another branch, say dev. You implement a few features, but realize you need one of them needs to be merged to master soon and cannot wait for you to finish whatever you were developing in the dev branch.

You can use the following git commands:

# Find and copy the commit hash that you want to send to another branch
git log

# change to the branch where this commit is needed
git checkout master

# execute the cherry pick using the hash you copied earlier
git cherry-pick bbb70c579ce1382af60eebacde95c2aef6676abf

If there are any conflicts, resolve them and commit the new code. Done.

]]>
<![CDATA[Smarter Vue Watchers]]> https://dberri.com/smarter-vue-watchers https://dberri.com/smarter-vue-watchers Sat, 08 Feb 2020 22:41:00 GMT In some Vue projects, you might need to load data from an API as soon as the component is instantiated and load new data once a value or property of the component is changed. For example, when you use a search input field. I've done that several times, and always used the following pattern:

// Inside a Vue component
created() {
  this.search()
},
watch: {
  searchInput() {
    this.search()
  }
}

And that's ok, it works well. But then I found a simpler, more elegant way to do the same thing:

// Inside a Vue component
watch: {
  searchInput: {
    handler: 'search',
    immediate: true
  }
}

This code can replace the previous one entirely. That works because a watcher can receive an object, instead of a function, and this object contains a handler, which is the method to be executed when the watcher is notified, and the property immediate, which ensures that this method is also executed as soon as the component is in the lifecycle hook created.

This tip was extracted from a video of a talk by Chris Fritz, one of the members of Vue.js' core team. Here's the video:

7 Secret Patterns Vue Consultants Don’t Want You to Know - Chris Fritz
There’s an astounding amount of power packed away in Vue, despite its small size. The official guide provides a gentle learning path and the API is thorough,...
]]>
<![CDATA[How to sort an array by date in JavaScript]]> https://dberri.com/how-to-order-an-array-by-date https://dberri.com/how-to-order-an-array-by-date Sun, 09 Feb 2020 22:42:00 GMT We can find this problem in our daily lives working with JavaScript, and of course we can use libraries that abstract this away for us. Nevertheless, it can be interesting to understand how to implement this feature and not rely on third party packages that might bring more complexity to your project.

Let's say you have an array of dates in a string format (e.g. 'yyyy-mm-dd') and you would like to sort it. With JavaScript, a good approach is to transform these dates in Date objects, and then use the Array method sort:

function fromStringToDate(date) {
 	// transforms string in array, splitting at the “-“
	const arrDate = date.split(“-“)
	const day = arrDate[2] ;
	// JavaScript represents the months starting at “0”
 	// but that's content for another post :)
 	const month = arrDate[1] - 1;
 	const year = arrDate[0];
    
    return new Date(year, month, day);
}

function compareDates(date1, date2) {
	if (date1 > date2) {
        return -1;
    } else if (date1 < date2) {
        return 1;
    } else {
        return 0;
    }
    // This code could be simplified to a one-liner:
    // return (date1 > date2) ? -1 : ((date1 < date2) ? 1 : 0)
}

const dates = [
	“2020-02-01”,
	“2020-01-05”,
	“2020-03-20”
];

dates
	.map(fromStringToDate)
	.sort(compareDates)

// [
//   Fri Mar 20 2020 00:00:00 GMT-0300 (-03),
//   Sat Feb 01 2020 00:00:00 GMT-0300 (-03),
//   Sun Jan 05 2020 00:00:00 GMT-0300 (-03)
// ]

If you want to invert the order, you just need to change the operator < to > and vice-versa.

]]>
<![CDATA[How to watch nested object properties in Vue]]> https://dberri.com/como-observar-propriedades-de-objetos-no-vue https://dberri.com/como-observar-propriedades-de-objetos-no-vue Fri, 21 Feb 2020 13:02:00 GMT When it's necessary to react to a change of state in a more generic way, we can use Vue's watchers. They work quite well for most data structures, but it can cause some confusion when you're watching arrays or objects with nested properties. For example:

export default {
	data() {
    	return {
        	price: 100.0
        }
    },
    watch: {
    	price: function (newPreco, oldPrice) {
        	// whenever the car price changes, this function will be executed
        }
    }
}

In this case, since price is a simple number, everytime it changes somewhere in the component, the function price inside the watch block will be executed. Now, let's say the price we want to keep watching is inside an object, like in the following example:

export default {
	data() {
    	return {
        	car: {
                color: 'black',
        		price: 100.0
            }
        }
    },
    watch: {
    	car: function (newCar, oldCar) {
	        // whenever the price changes, this function will be executed
            // or will it?
        }
    }
}

Now, even change the name of the watcher to car, if the car price changes, the function won't be executed. Well, in this case Vue has a "trick up its sleeve". You can define the watcher as an object that receives a handler function and a property called deep which will watch for changes in the nested properties of the object, like this:

export default {
	data() {
    	return {
        	car: {
                color: 'black',
        		price: 100.0
            }
        }
    },
    watch: {
    	car: {
        	handler: function (newCar, oldCar) {
                // whenever the car price changes, this function will be executed
            },
            deep: true
        }
    }
}

Now, every time the car price changes, the watcher function will run.

We solved a problem, but introduced another: if any other property in the car object changes, for instance the color, the watcher will execute that function.

For a long time I thought there wasn'' much to do regarding this. I would usually check if the newCar price was  different than oldCar price and execute whatever I needed. But thanks to a tweet, I found out there was a much much simpler way to do this:

Michael Thiessen on Twitter
â€œđŸ”„ You can watch nested values directly in @vuejs by using dot notation as a string. This avoids watching an object deeply and ignoring everything but one specific value. It also communicates your intent better because you don’t *actually* care about the whole object.”

Yes, we can simplify the previous code like this:

export default {
	data() {
    	return {
        	car: {
                color: 'black',
        		price: 100.0
            }
        }
    },
    watch: {
    	'car.price': function (newPrice, oldPrice) {
        	// now, only when the car price changes, it will execute the function
        }
    }
}

And besides, it's clear which property you want to watch in the car object.

]]>
<![CDATA[For Loops in JavaScript]]> https://dberri.com/for-loops-in-javascript https://dberri.com/for-loops-in-javascript Fri, 20 Mar 2020 16:37:00 GMT JavaScript has at least three distinct ways of defining "for" loops:

for: Runs a block of code a number of times;
for/in: Iterates over properties of a given object;
for/of: Iterates over the values of iterable objects;

For

This loop repeats the execution of your block of code until a specified condition becomes false.

Let's check the syntax of this simpler loop:

for (statement 1; statement 2; statement 3) {
  // block of code to be executed
}

Statement 1 is executed only once before your block of code runs. Usually we define the loop's initial condition, but this is an optional parameter and can be defined before the loop statement if needed.

Statement 2 defines the condition for the execution of the code block. Here you end up defining how many times the code block will run. This parameter is also optional, though if omitted, it's necessary to add a break inside your code block, otherwise the loop will never finish and the browser will crash.

Statement 3 is executed once for each execution after your code block runs. As you might have guessed by now, this parameter is also optional.

const languages = [‘javascript’, ‘python’, ‘php’];

for (let i = 0; i < languages.length; i++) { 
  console.log(`${i}: ${languages[i]}`)
}

// expected output:
// “0: javascript”
// “1: python”
// “2: php”

For/in

This loop iterates over an object's properties (keys).

const js = { name: ”JavaScript”, author: ”Brendan Eich”, year: 1995 };

for (const prop in js) {
  console.log(`${prop}: ${js[prop]}`);
}

// expected output:
// “name: JavaScript”
// “author: BrendanEich”
// “year: 1995”

The same could be done using a simple "for" loop using Object.keys, which extracts the properties of an object to an array:

const js = { name: ”JavaScript”, author: ”Brendan Eich”, year: 1995 };
const properties = Object.keys(js);

for (i = 0; i < properties.length; i++) { 
  const prop = properties[i]
  console.log(`${i} ${prop}: ${js[prop]}`);
}

// expected output:
// “0 name: JavaScript”
// “1 author: Brendan Eich”
// “2 year: 1995”

For/of

This runs the loop over the values of iterable objects. A few examples of iterable objects in JS are Arrays, Strings, Maps, Sets and NodeLists. The syntax is as follows:

for (*variable* of *iterable object*) {
  // code block to be executed
}

Here's an example like the previous:

const languages = [{ name: ”JavaScript” }, { name: ”Python” }, { name: ”PHP” }];

for (const lang of languages) {
  console.log(lang);
}

// expected output:
// { name: ”JavaScript” }
// { name: ”Python” }
// { name: ”PHP” }

Both for/in and for/of are very useful, each being used in their own way can make your code more legible, in my opinion. Hopefully this tip has been useful for you :)

]]>
<![CDATA[Const, Let and Var]]> https://dberri.com/const-let-and-var https://dberri.com/const-let-and-var Fri, 28 Feb 2020 16:48:00 GMT I like to imagine variables like they were drawers with labels. They are used to store data. There are three ways of declaring a variable in JavaScript: var, let or const.

Scope

The scope is the portion of code where the variable is visible. In JavaScript, every function has a scope.

var

Until ES2015 spec, this was the only way of declaring a variable in JavaScript.

var cat = ‘meaw’

var allows one to “redeclare” the same variable infinite time, always overwriting the previews declaration:

var cat = ‘meaw’
console.log(cat) // meaw

var cat = ‘oof oof’
console.log(gato) // oof oof

let

let works almost the same way as var, but it uses the local scope, not global. That means that if the variable is declared using let inside a function, for example, it will not be available outside that function. The same goes for blocks like if, for, while, etc...

function bark() {
   let dog = ‘oof oof’
   console.log(dog)
}

latir() // oof oof

console.log(dog) // ReferenceError: Can't find variable: dog

const

const uses the same scope rules as let, though it does not allow for redeclaration.

const x = 10
x = 11 // TypeError: Attempted to assign to readonly property.

It is possible to declare an array or object with const and later assign or reassign values that they store:

const obj = { a: 1 }
obj.a = 2
console.log(obj) // { a: 2 }

const arr = [1]
arr.push(2)
console.log(arr) // [1, 2]

Using let and const and avoiding var can help you code less bugs related to scope. If you use any linter like eslint, it's posible that it already points things like this to you.

]]>
<![CDATA[Local Storage]]> https://dberri.com/local-storage-2 https://dberri.com/local-storage-2 Sat, 11 Apr 2020 23:32:51 GMT With more logic and data manipulation brought to the frontend, some data storage problems start to arise. Among them, storing data betwen page reloads or sessions. This is a very common problem in SPAs (Single Page Applications), where you may have several data manipulations happening in between page reloads or would have to keep downloading the same data over and over from the server. There are cases where it's possible to minimize the user's wait time displaying info that is stored right in the browser using LocalStorage.

LocalStorage is a type of web storage that allows JavaScript applications to access data stored in the browser with no expiration date.

This functionality is available in the window object and you can access it like this:

const myStorage = window.localStorage
// or
const myStorage = localStorage

This will return a Storage object. This object has the property length that returns an integer representing the quantity of items stored in the object and it also has the following methods:

  • setItem
  • getItem
  • key
  • removeItem
  • clear

setItem

It accepts the parameters key and value that will be stored (or updated if that particular key already has some data associated):

myStorage.setItem(“name”, “David”)

In this case, "name" is the key and "David" is the value.

Important: If you want to store arrays or objects - something that will probably be more common in an application - first you will have to transform them into strings, using, for instance, JSON's stringify method:
const myBrowser = {
  name: “Chrome”,
  openTabs: 3,
  consumedMemory: 512e9
}

myStorage.setItem(“browser”, JSON.stringify(myBrowser))

getItem

You pass the key to this method and it returns what you stored with that key, or null if it does not find anything associated.

myStorage.getItem(“name”)

// expected output
// “David”

If you stored an object or array like in the previous example, you will notice that getItem returns a string representation of the object, so you should use JSON's parse method to turn it into a "real" object again:

const myBrowser = myStorage.setItem(“browser”)
console.log(myBrowser)

// expected ouput
//  “{\”name\":\"Chrome\",\"openTabs\":3,\"consumedMemory\":512000000000}"

console.log(JSON.parse(myBrowser))

// expected output
// {
//    name: "Chrome",
//    openTabs: 3,
//    consumedMemory: 512000000000
// }

key

This method accepts an integer as a parameter and with that, it returns a key if it is stored in that position or null if there's nothing there. It works more or less like accessing positions in an array. In our examples, we had stored two items in LocalStorage: name and browser:

const position0 = myStorage.key(0)
const position1 = myStorage.key(1)
const position2 = myStorage.key(2)

console.log(`${position0}, ${position1}, ${position2}`)

// expected output
// name, browser, null

This method is useful if you need to iterate over the keys inside a loop, for instance.

removeItem

You pass a key to this method and it removes the associated value:

myStorage.removeItem(“name”)

Now,  getItem(“name”), will return null and key(0) becomes: “browser”.

clear

Last but not least, we have the method clear which completely clears the application's LocalStorage, removing all of its keys.

myStorage.clear()
console.log(myStorage.length)

// expected output
// 0

Limitations

It's important to note that:

  • You should not store sensitive information or user data in LocalStorage
  • It is not a replacement for traditional databases, since data is only available in the browser
  • It has a 5mb limit in most browsers

With this introduction to LocalStorage, you will surely be able to create more powerful applications. I hope this has inspired you to use it in your apps and if you want an example to start, take a look here in my GitHub. It's a very simple task manager written in Vue.js and uses LocalStorage to persist data.

]]>
<![CDATA[Cleaning up merged git branches]]> https://dberri.com/cleaning-up-git-branches https://dberri.com/cleaning-up-git-branches Mon, 04 May 2020 19:10:47 GMT If you use any git workflow such as git flow, you end up with several branches like feature, release and hotfix that become stale as they are merged with other branches.

So, when you need to look for a specific branch using the git branch command, you could waste time skimming through a big list of branch names.

One way to handle that is to use git branch --merged, which lists only the branches that have already been merged, and then copy the name of each one and delete them with git branch -d branch-name. That works fine if you do that every time you merge a branch, but if you're like me, you'll quickly forget to do this, and once you realize it, you'll have several branches on the list again.

Now, to remove all merged branches at once, we can use the following command:

git branch --merged | egrep -v "(^\*|master|develop)" | xargs git branch -d

This will remove all branches that have been merged with exception of master and develop. You can even create an alias to in your shell environment to not have to type or copy that every time you need it.

Careful with the -d flag. If you change it to  -D (capital case), you might end up removing unmerged branches and losing hours of work
]]>
<![CDATA[How to create a Rest API with Laravel]]> https://dberri.com/how-to-create-a-rest-api-with-laravel https://dberri.com/how-to-create-a-rest-api-with-laravel Sat, 16 May 2020 18:02:27 GMT Laravel makes it quick to get up and running with Rest APIs that you can use to feed data to mobile apps, javascript applications or other applications. In this post, I'll show you how easy it is to do it.

To make it simple, let's create an API that only delivers content about books and their authors. We're not going to add "create" and "update" functionality for now.

Setup

We can start by creating a new Laravel application. In your terminal execute the command: laravel new books-api. If you don't have Laravel installed globally, you can use Composer:

composer create-project --prefer-dist laravel/laravel books-api
Keep in mind that by the time of this article, I'm using Laravel version 7.10.3

That will create a new folder books-api, you can go inside and edit the .env file. We need to setup the database access for the application:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=<YOUR-DB-NAME>
DB_USERNAME=<YOUR-DB-USER>
DB_PASSWORD=<YOUR-DB-USER-PASSWORD>

To make sure it connects correctly, you can run php artisan migrate. The application already ships with database migrations for the User model. If no errors popped up, you're good to go.

Models, Migrations and Controllers

We will need at least a model for books and one for authors, so let's create them using Artisan's command: php artisan make:model Author -mcr.

This command should create three files:

  • app/Author.php : That's our model
  • app/Http/Controllers/AuthorController.php : A resource controller (created with the help of the cr flag)
  • and database/migrations/XXXX_create_authors_table.php: That's the database migration created with the help of the m flag in the command.

Let's start with the migration. Go to the newly created file and add the following code:

public function up()
{
    Schema::create('authors', function (Blueprint $table) {
        $table->id();
        $table->string('name'); // we add this line here
        $table->timestamps();
    });
}

Whatever fields you add here will be added to the authors table in the database. To learn what are the available methods, check out Laravel's documentaton.

Now let's do the same for books: php artisan make:model Book -mcr.

Then go to database/migrations/XXXXX_create_books_table.php and add the following fields inside the up function:

public function up()
{
    Schema::create('books', function (Blueprint $table) {
        $table->id();
        $table->foreignId('author_id');
        $table->string('title');
        $table->decimal('price', 6, 2)->nullable();
        $table->timestamps();

        $table->foreign('author_id')
            ->references('id')
            ->on('authors')
            ->onDelete('cascade');
    });
}

Here we are also adding a reference to the author who wrote the book using a foreign key. After you add any other fields you want, you run the migrations with: php artisan migrate. If you got no errors in the console, good job! Now let's keep going.

We also need to create a relationship between authors and books in the models:

app/Author:

class Author extends Model
{
    public function books()
    {
        return $this->hasMany('App\Book');
    }
}

app/Book:

class Book extends Model
{
    public function author()
    {
        return $this->belongsTo('App\Author');
    }
}

With these, we can call the relationships as properties from each model:

$book = Book::first(); // Book instance
$book->author; // Author instance

$author = Author::first(); // Author instance
$author->books; // Collection of Books

If you call them using the "method" form, it will return you a QueryBuilder instance, and you can use to chain other methods, like:

$author = Author::first(); // Author instance
$author->books()
	->where('published', true)
    ->get(); // Collection of Books

Factories and Seeders

Now that we have the database setup, we need some data to test the API. In the real world, either you will already have the data, or maybe you can create it manually. I like to using factories and seeders, because it's less work and it's useful for automated testing. Let's create a factory for authors and for books:

php artisan make:factory AuthorFactory -m Author
Factory created successfully.

php artisan make:factory BookFactory -m Book
Factory created successfully.

Now if you go to the database/factories folder, you will see both files created.

In AuthorFactory, let's use the faker library to create random Author data:

$factory->define(Author::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
    ];
});

And we do the same for books, with the catch that it will receive the author_id as a parameter:

$factory->define(Book::class, function (Faker $faker, $author_id) {
    return [
        'author_id' => $author_id,
        'title' => $faker->sentence,
        'price' => $faker->randomFloat(2, 5, 100)
    ];
});

Now, we create a seeder for Authors:

php artisan make:seeder AuthorTableSeeder
Seeder created successfully.

In that seeder (which is in database/seeders/AuthorTableSeeder) we create 5 authors and 5 books for each author using the followinf code:

public function run()
    {
        factory(App\Author::class, 5)->create()
            ->each(function ($author) {
                factory(App\Book::class, 5)->create([
                    'author_id' => $author->id
                ]);
            });
    }

It calls the factory for the Author model and for each one created, it creates 5 books using basically the same syntax. Now, to finish it up, we call this seeder in the DatabaseSeeder class:

public function run()
    {
        $this->call(AuthorTableSeeder::class);
    }

With all migrations, factories and seeders in place, we run php artisan migrate --seed. You should see a list of all the migrations and seeders run.

Routes

We need routes to access the data, so we can go to routes/api.php and add them there. Routes added to that file will already have a /api prepended to the route, so keep that in mind. I want to see a list of authors, a list of books and some details of a specific book. So, I can start by adding those three routes at the end of the file:

Route::resource('authors', 'AuthorController')->only('index');
Route::resource('books', 'BookController')->only(['index', 'show']);

Now, since we created a resource controller in the beginning of this tutorial, we can now use the Route's resource static method. You pass as arguments the name of the route and a controller to map each of these seven routes:

  • Index
  • Create
  • Store
  • Show
  • Edit
  • Update
  • Delete

If you go to the controllers, you will see that all of those methods are in the there. Since we're developing a Rest API, we would not even need a route for create and edit, because those are meant to return a view with the forms for creating and editing the resource, respectively. As stated before we only need a list of authors, a list of books, and details of a specific book, so we use the  only() method to limit the resource routes.

Of course, we didn't need all of that boiler plate code, so might as well create each of those routes and methods from scratch, but I wanted to show you these methotds, because they save you time in your day-to-day.

Now, let's implement index() in the AuthorController, so go to app/Http/Controllers/AuthorController.php and add this code:

public function index()
{
    $authors = Author::all();

    return response()->json($authors);
}

Since we only have 5 authors (created by the seeder, remember?), we would not even need pagination, so let's query all the authors.

In BookController we need to implement index() for the list of books and show() for the details of the specific book:

public function index()
{
    $books = Book::with('author')->paginate(10);

    return response()->json($books);
}

public function show(Book $book)
{
    return response()->json($book);
}

The with() queries the relationship and already returns a book with its author.

The string you pass to this method must be equal to the name of the method you gave to the relationship in the Book model.

Since we have at least 25 books, let's create a pagination query 10 books every time.

In the case of show(), Laravel already resolves the book id passed to the route and gives us the Book instance as an argument.

Now if you run Laravel's server using php artisan serve, you can access http://localhost:8000/api/authors or http://localhost:8000/api/books in your browser or in app like Postman or Insomnia and you will get a JSON response like this:

{
  "current_page": 1,
  "data": [
    {
      "id": 1,
      "author_id": 6,
      "title": "Inventore voluptatem iure veniam quis nesciunt aut.",
      "price": "42.19",
      "created_at": "2020-05-16T17:39:22.000000Z",
      "updated_at": "2020-05-16T17:39:22.000000Z",
      "author": {
        "id": 6,
        "name": "Brandyn Gorczany",
        "created_at": "2020-05-16T17:39:22.000000Z",
        "updated_at": "2020-05-16T17:39:22.000000Z"
      }
    },
    ...
  ],
  "first_page_url": "http:\/\/localhost:8000\/api\/books?page=1",
  "from": 1,
  "last_page": 3,
  "last_page_url": "http:\/\/localhost:8000\/api\/books?page=3",
  "next_page_url": "http:\/\/localhost:8000\/api\/books?page=2",
  "path": "http:\/\/localhost:8000\/api\/books",
  "per_page": 10,
  "prev_page_url": null,
  "to": 10,
  "total": 25
}

If you want to access details of a book,  just go to http://localhost:8000/api/books/1 where "1" is the id of the book you want to access. This will return this response:

{
  "id": 1,
  "author_id": 6,
  "title": "Inventore voluptatem iure veniam quis nesciunt aut.",
  "price": "42.19",
  "created_at": "2020-05-16T17:39:22.000000Z",
  "updated_at": "2020-05-16T17:39:22.000000Z"
}

So, there you have it. It doesn't take too much time to create a Rest API with Laravel, as a bonus you already have factories and seeders setup to use in automated tests.

Stay tuned because in the next posts you'll learn how to authenticate in the API using Laravel Passport and how to use Eloquent's API Resources to fine tune what you want to display in each route.

]]>
<![CDATA[Let’s build a tab navigation with Alpine.js]]> https://dberri.com/lets-build-a-tab-navigation-with-alpine-js https://dberri.com/lets-build-a-tab-navigation-with-alpine-js Sat, 06 Jun 2020 14:06:14 GMT From the docs: it is

a rugged, minimal framework for composing JavaScript behavior in your markup.

I like to think of it as a lighter and lower cost Vue.js mixed with TailwindCSS. The syntax is very similar to Vue.js and you sprinkle it in your HTML just like TailwindCSS.

I found out about Alpine.js while following Caleb Porzio, an active member of the Laravel community. At the time I saw the Github link and to be honest didn’t care too much or was skeptical about it: “Ok, it’s just another JS framework, next week a new one comes out”.

Then I heard him talk about it in his podcast No Plans To Merge and it started to spark my interest. I took another look at the docs and was like: “well, I could just use Vue.js or plain JS, no need to do it with something else”. It was not until I heard him and Adam Wathan discuss it in the Full Stack Radio podcast that I finally took Alpine.js for a ride.

In the episode, they discuss the use cases for the framework and it made sense to me. Here’s what made me want to use it: let’s say you are building a website that does not need too much JavaScript. There’s only one thing here and there, like a modal, a dropdown menu or tabs navigation. Of course, you could just write the JavaScript for that and don’t worry about it. It will be a bit of a pain to switch contexts and think about the imperative way of doing it with JS (get elements, toggling classes, etc
) or you could add Vue.js and create a component and all that. Seems a little overkill for just a simple interactive element. Or you could use Alpine.js and do it in a declarative way all in the HTML.

In the documentation, the author states that the framework’s syntax is entirely borrowed from Vue.js, so you will find things like x-on:click, x-bind, x-show, x-for which are basically the same as v-on:click, v-bind and so on. If you know Vue.js, it’s a breeze to learn Alpine.js, but even if you don’t, I think it is simple enough to pick up and understand the concepts.

What are we going to build?

Tab navigation representation

It's a simple tab navigation component. It's not the most exciting thing to do with JS, but it will be great to show you what Alpine.js can do. We are going to add TailwindCSS just so we don't have to worry too much about CSS and focus on Alpine's features. We get the framework form a CDN, so it's as easy as pasting the link in the HTML and be ready to go. You can go to this Github repository to get the code and follow along, or use the develop branch to get the final result.

The initial HTML body looks like this:

<div class="bg-gray-100 text-gray-800">
    <nav class="bg-gray-300">
      <ul class="flex">
        <li class="px-4 py-2 bg-gray-100">
          <a href="#option-1">Option 1</a>
        </li>
        <li class="px-4 py-2">
          <a href="#option-2">Option 2</a>
        </li>
        <li class="px-4 py-2">
          <a href="#option-3">Option 3</a>
        </li>
      </ul>
    </nav>

    <div class="p-4">
      <h2 class="text-xl mb-4">Here, you have the content of <strong>Option 1</strong></h2>
      <p class="mb-2">Qui ipsorum...</p>
    </div>

    <div class="p-4">
      <h2 class="text-xl mb-4">Here, <strong>Option 2</strong> contents are shown</h2>
      <p class="mb-2">Qui ipsorum...</p>
    </div>

    <div class="p-4">
      <h2 class="text-xl mb-4">And here, only <strong>Option 3</strong> contents should be displayed</h2>
      <p>Qui ipsorum...</p>
    </div>
  </div>

If you open this with your browser, the styles will be there, but all the content (for all tabs) are shown at the same time. Now let's sprinkle some Alpine.js to only show a single tab's content:

<div class="bg-gray-100 text-gray-800" x-data="{ selected: 'option-1' }">
    <nav class="bg-gray-300">
      <ul class="flex">
        <li class="px-4 py-2 bg-gray-100">
          <a href="#option-1" x-on:click="selected = 'option-1'">Option 1</a>
        </li>
        <li class="px-4 py-2">
          <a href="#option-2" x-on:click="selected = 'option-2'">Option 2</a>
        </li>
        <li class="px-4 py-2">
          <a href="#option-3" x-on:click="selected = 'option-3'">Option 3</a>
        </li>
      </ul>
    </nav>

    <div x-show="selected === 'option-1'" class="p-4">
      ...
    </div>

    <div x-show="selected === 'option-2'" class="p-4">
      ...
    </div>

    <div x-show="selected === 'option-3'" class="p-4">
      ...
    </div>
  </div>

Here we are greeted by three directives: x-data, x-on:click and x-show.

x-data declares a new component scope. You can pass a JS object to it, and every element that is wrapped by the element which contains the x-data declaration, will have access to the JS object you passed to it. So, for example, if I had declared x-data in the nav element, only nav, ul, li and a would have access to that data. Since we want to use selected in the tab's content elements, we wrap everything with another element and use x-data there. You can have multiple x-data declarations in your page, just remember what each component has access to.

x-on:click attaches a click event listener to that element. The directive part is actually only x-on, the second part (click) is what event you want to listen to. You could for instance use x-on:keydown.escape to listen for presses on the escape key. Take a look at the docs to learn all the other events you can listen to. So, x-on:click can receive a function or an expression. Since we only want to change the state of selected, we can add this short expression directly, like x-on:click="selected = 'option-1'". This will change the selected value in the x-data directive.

Finally, we have x-show. You can pass an expression that resolves to true or false and it will toggle the display: none; style on the element.

Ok, so with that, we should already have a basic tabs functionality:

Wait, I mean, it's working but the tabs' styles are not changing to reflect their state. Well, let me present you x-bind:class (if you know Vue.js, this is not going to be a big surprise):

<ul class="flex">
        <li class="px-4 py-2" x-bind:class="{ 'bg-gray-100': selected === 'option-1' }">
          <a href="#option-1" x-on:click="selected = 'option-1'">Option 1</a>
        </li>
        <li class="px-4 py-2" x-bind:class="{ 'bg-gray-100': selected === 'option-2' }">
          <a href="#option-2" x-on:click="selected = 'option-2'">Option 2</a>
        </li>
        <li class="px-4 py-2" x-bind:class="{ 'bg-gray-100': selected === 'option-3' }">
          <a href="#option-3" x-on:click="selected = 'option-3'">Option 3</a>
        </li>
      </ul>

Here, we pass an object to the x-bind:class directive where the key is the class that should be applied and the value should be an expression that resolves to true or false. If it is true, the class is applied, else it is not. With that, this component is done.

Now let's add some fluff. First, just like Vue.js, we can use a shorter syntax, where x-on can be replaced by @, and x-bind can be replaced by ::

<li class="px-4 py-2" :class="{ 'bg-gray-100': selected === 'option-1' }">
          <a href="#option-1" @click="selected = 'option-1'">Option 1</a>
        </li>

Now, for the grand finale let’s add some transitions to the content boxes, so it’s easy on the eyes:

<div x-show.transition.in.opacity.duration.750ms="selected === 'option-1'" class="p-4">
      ...
    </div>

    <div x-show.transition.in.opacity.duration.750ms="selected === 'option-2'" class="p-4">
      ...
    </div>

    <div x-show.transition.in.opacity.duration.750ms="selected === 'option-3'" class="p-4">
      ...
    </div>

Yep, that’s it. It’s that easy. You can basically chain several options to make your transition look better. By default, if you only add x-show.transition it will add an in and out opacity plus scale transition with 200ms of duration. I changed that in this demo to only transition in, only change opacity and to last 750ms. If you want to take a look at other options, check out the docs.

That’s it. With that little amount of code we were able to add tabs navigation to the application. Alpine.js is great when you want to make simple stuff like this, but it can also be used to create more complex interactions such as forms. In a future post I’ll show you how 😎.

]]>
<![CDATA[Let's build an ajax form with Alpine.js]]> https://dberri.com/lets-build-an-ajax-form-with-alpine-js https://dberri.com/lets-build-an-ajax-form-with-alpine-js Tue, 23 Jun 2020 00:07:38 GMT In the previous post, we built a tab navigation with Alpine.js and I said I wanted to bring you a more complex example. So, let’s build a regular contact form like this:

The catch is that we will send the data through ajax and handle all the form data with Alpine. I have done this countless times with vanilla JS or jQuery and it is always a monotonous task. You have to get all of the elements by reference, access their values and then send the data. Alpine (and other frontend frameworks) make this task a breeze.

As I said, this will be a simple form (name, email, message submit button) , but if you get the idea behind the implementation, you can apply it in more advanced situations. You can go to  this Github repository  to get the code and follow along from the master branch, or use the develop branch to get the final result. This is the important part:

<form action="/contact" method="POST" class="w-64 mx-auto">
	<div class="mb-4">
		<label class="block mb-2">Name:</label>
		<input type="text" name="name" class="border w-full p-1">
	</div>
    <div class="mb-4">
		<label class="block mb-2">E-mail:</label>
        <input type="email" name="email" class="border w-full p-1">
	</div>
	<div class="mb-4">
		<label class="block mb-2">Message:</label>
		<textarea name="message" class="border w-full p-1"></textarea>
	</div>
	<button class="bg-gray-700 hover:bg-gray-800 text-white w-full p-2">Submit</button>
</form>

That’s the basic HTML structure of the form. Up to now, there’s no javascript at all, it’s just a regular form that would work with a page reload. Now, let’s sprinkle some Alpine.js on it. Last time, I added the data object inline inside the x-data. This time, since that object will be more convoluted, I’ll show you that you can do most of the “heavy lifting” inside a script tag as such:

<script>
    function contactForm() {
      return {
        formData: {
          name: '',
          email: '',
          message: ''
        },
      }
    }
</script>

Then you just need to add that function call inside the x-data:

<form action="/contact" method="POST" class="w-64 mx-auto" x-data="contactForm()">

Now, let me present you the x-model directive. This keeps input elements in sync with the component data. We have the formData object inside the component scope, so we can use them in the inputs and textareas like this:

<form action="/contact" method="POST" class="w-64 mx-auto" x-data="contactForm()">
    <div class="mb-4">
      <label class="block mb-2">Name:</label>
      <input type="text" name="name" class="border w-full p-1" x-model="formData.name">
    </div>

    <div class="mb-4">
      <label class="block mb-2">E-mail:</label>
      <input type="email" name="email" class="border w-full p-1" x-model="formData.email">
    </div>

    <div class="mb-4">
      <label class="block mb-2">Message:</label>
      <textarea name="message" class="border w-full p-1" x-model="formData.message"></textarea>
    </div>
    <button class="bg-gray-700 hover:bg-gray-800 text-white w-full p-2">Submit</button>
</form>

In vanilla JavaScript, you would probably have to grab the element with something like getElementById and then access its value. With x-model, you don’t have to worry about it. As you type in the input element, your data object is automatically updated with whatever you typed.

Now, as for the ajax part, let’s just use the fetch API, so we don’t have to pull an external dependency, but you can adapt this to your needs of course:

function contactForm() {
	return {
		formData: {
			name: '',
			email: '',
			message: ''
		},
		message: '',
        
		submitData() {
			this.message = ''

			fetch('/contact', {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(this.formData)
            })
			.then(() => {
				this.message = 'Form sucessfully submitted!'
			})
			.catch(() => {
				this.message = 'Ooops! Something went wrong!'
			})
		}
	}
}

and add this paragraph before the form closing tag:

<p x-text="message"></p>

If you don’t understand what the .then and .catch statements are, dont' worry, you can check out this article about Promises. I’ll probably do a blog post about it in the future, stay tuned. Basically, this whole submitData method will do a POST request to the /contact route and pass the form data as a stringified JSON. If everything is successful the .then block is executed, if there's and error in the response, the .catch is executed.

Now, we have to call this method upon form submission. The form element emits a submit event, so we can listen to it using the x-on directive, and since we don’t want to reload the page we add the .prevent event modifier to sort of “hijack” the form submission and use our own method “submitData”:

<form action="/contact" method="POST" class="w-64 mx-auto" x-data="contactForm()" @submit.prevent="submitData">

That's it! You’ve got yourself a working ajax form built with Alpine.js. But let’s take a step forward and add some dynamic styling to the submit button to improve the user experience:

Add this style tag inside the head (I'll just add this style because as of now, TailwindCSS does not support disabled state out of the box):

<style>
    button:disabled {
      cursor: not-allowed;
      opacity: 0.5;
    }
</style>

Now, replace the old submit button with this one:

<button class="bg-gray-700 hover:bg-gray-800 disabled:opacity-50 text-white w-full p-2 mb-4" x-text="buttonLabel" :disabled="loading"></button>

The two interesting bits are the x-text directive and the :disabled. We will use the x-text to change the button's label dynamically and :disabled to, well, disable the button while the form is being submitted.

Update the contactForm function with the following:

loading: false,
buttonLabel: 'Submit',

submitData() {
	this.buttonLabel = 'Submitting...'
	this.loading = true;
	this.message = ''
    
	fetch('/contact', {
		method: 'POST',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify(this.formData)
	})
	.then(() => {
    	this.message = 'Form sucessfully submitted!'
    })
	.catch(() => {
		this.message = 'Ooops! Something went wrong!'
	})
	.finally(() => {
		this.loading = false;
		this.buttonLabel = 'Submit'
	})
}

That's it (again and finally)! We have a fully working ajax form built with Alpine.js and with some UX sugar. Do you want to see something specific using Alpine.js? @ me!

]]>
<![CDATA[Web scraping with Puppeteer]]> https://dberri.com/web-scraping-with-puppeteer https://dberri.com/web-scraping-with-puppeteer Mon, 06 Jul 2020 16:59:41 GMT Today, I decided to bring sort of a different topic to the blog: web scraping.
Sometimes, I find myself wanting to analyse or visualize some data that is not available through an API or is not really structured, so I turn to web scraping. This is a data extraction method where the user or an automated software copies specific data from a website. I used to use Python to do that, but recently I came across puppeteer and that is the Node library we’re going to use today.

According to the docs, puppeteer is “a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol.” This means we can do things like: crawl simple web pages or even SPAs (single page applications), automate form submissions, UI testing, generate screenshots and PDFs, and more.

Assuming you have Node.js installed in your computer, you can run this snippet as a .js file and quickly see how it works.

const puppeteer = require('puppeteer');

(async () => {
	// Launch the headless browser
	const browser = await puppeteer.launch();

	// open a new page
	const page = await browser.newPage();

	// go to the specified url
	await page.goto('https://example.com');

	// take a screenshot
	await page.screenshot({path: 'screenshot.png'});

	// close the browser
	await browser.close();
})();

If you want to see what the browser is doing instead of just waiting for the results, you can change the headless mode:

const browser = await puppeteer.launch({
	headless: false
});

This will open up a browser window as soon as the script starts running and you will see each step as they happen.

Now, the cool thing about using puppeteer for web scraping is that you can use “vanilla” JavaScript syntax to find elements and data on the page. You will have access to the document interface inside the evaluate method. Let’s try it on the Google News page:

const puppeteer = require('puppeteer');

(async () => {
	const browser = await puppeteer.launch();
	const page = await browser.newPage();

	await page.goto('https://news.google.com/news/');

	const data = await page.evaluate(() => {
		// Find all anchor tags whose parent is an H3
		const headlineNodes = document.querySelectorAll('h3 > a')
		// Transform the NodeList in an array and map it to get the textContent of each anchor

		return {
			headlines: Array.from(headlineNodes).map(a => a.textContent)
		}
	});

	console.log(data);

	await browser.close();
})();

As I write this post, these are the headlines it returned:

{
  headlines: [
    "POLITICO's Election Forecast: Trump, Senate GOP in trouble",
    'Trump campaign “strongly” encourages face masks at outdoor rally in New Hampshire.',
    'Facebook, WhatsApp Suspending Review of Hong Kong Requests for User Data',
    'Trump celebrates Fourth of July by stoking division over pandemic and race',
    '7-Year-Old Among 13 Killed in Weekend Shootings in Chicago',
    'LA County New COVID-19 Cases Shatter Daily Record In First Report Since Data Processing Changes'
  ]
}

Google News has an infinite scroll behaviour. This means that when you first load the page, only a few articles are shown and the others are loaded as you scroll down the page. You can mimic this behaviour to get more data by using a combination of window.scrollBy and setInterval inside the evaluate method. Something like this (beware this can cause an infinite loop, make sure to create an exit strategy that meets your requirements):

const data = await page.evaluate(async () => {

    await new Promise((resolve) => {
      var totalHeight = 0;
      var distance = 100;
      var timer = setInterval(() => {
        var scrollHeight = document.body.scrollHeight;
        window.scrollBy(0, distance);
        totalHeight += distance;

        if (totalHeight >= scrollHeight) {
          clearInterval(timer);
          resolve();
        }
      }, 100);
    })

    const headlineNodes = document.querySelectorAll(‘h3 > a’)
    return {
      headlines: Array.from(headlineNodes).map(a => a.textContent)
    }
  });

And that’s it. Hopefully you can see how this technique can be useful to automate boring tasks and maybe create APIs where there isn't one. On a final note, be respectful of the website you’re scraping. You should follow the rules stated in the /robots.txt for each website, make sure to agree with the terms of service and check if scraping is actually legal wherever you are doing it. And try to not DDOS them when running this in loops :)

]]>
<![CDATA[How to build a real time chat app with Node.js and Socket.io]]> https://dberri.com/how-to-build-a-real-time-chat-app-with-node-js-and-socket-io https://dberri.com/how-to-build-a-real-time-chat-app-with-node-js-and-socket-io Sun, 09 Aug 2020 14:33:47 GMT Lots of applications rely on real time data delivery to provide value to the user. From instant messaging, to online multiplayer games, IoT, ad servers, and stock exchange brokers, at one point or another in your career you might be challenged to build something like that. And with Node.js and Socket.io, it became so simple that you will might as well learn it now.

But before we start building the app, let's talk about some topics and approaches that you could use to build a real time application.

Regular polling

A good and simple way to summarise this is if you have an application that periodically (let’s say every 10s) sends a request to the server like asking: “Hey do you have any new information for me?”. Now, this can work in some situations, but you can imagine what would happen if hundreds of clients kept bombing the server with this amount of request every few seconds. Been there, done that, it was a very stressful day


Long Polling

Long polling is similar to regular polling, but after a request to the server, the connection hangs and the server will only close the connection with a response once there’s new information. The client, after receiving the response, immediately sends a new request waiting for new information. This is a good option for delivering messages without delay but the server architecture must be able to handle multiple pending connections. Depending on the type of technology used, each pending connection can take up a lot of memory, which was the case when I tried this option
  it was a very long day.

WebSocket

While regular polling and long polling make use of HTTP, WebSocket is another communication protocol that enables two-way communication between the server and the client. After the connection is opened, the client can send messages to the server, and receive event-driven responses without having to poll the server for a reply.

Socket.io

In their website, it says: ”Socket.IO enables real-time, bidirectional and event-based communication.”.  It tries to establish a WebSocket connection if possible, but will fall back to HTTP long polling if not. Which is an important distinction to consider when you’re thinking about building something on top of it.

Their website also lists examples of applications that make good use of Socket.io like real-time analytics that push data to clients (like counters, charts and logs) or instant messaging and chat (like what we will be doing) and document collaboration where users editing a document can see other users changes in real time (think Google Docs). One can also think of how games could make use of this technology to send and receive multiplayer data.

It’s incredibly easy to integrate it in a Node.js application (they say it works on every platform, but I haven’t tried).

Let’s start 🚀

This is what the chat app will look like by the end of this tutorial:

Screenshot of the chat application

It should go without saying that we need Node.js installed, so if you still don’t have, go to their website and download at least the LTS version.

With that comes npm, the node package manager. I prefer Yarn (and that’s what I’ll be using throughout the tutorial), but feel free to use npm if you want. With that, go ahead and create a folder to store the application files. Now, open your terminal and navigate to the newly created folder (e.g. cd realtime-chat) and run yarn init -y which will quickly create a package.json file and you will be able to add the only dependency we need: yarn add socket.io.

Now, we need am HTML page where the users will be able to use the chat and a Node.js server. So, go ahead and create an index.html and a server.js files.

With that, let’s open package.json and edit a few lines. First, let’s change the main from index.js to server.js, and in scripts we can remove the test script and add "start": "node server.js" which will enable us to run yarn start from the root folder of the application and start up our server. That part of your package.json should look like this:

“main”: “server.js”,
“scripts”: {
  “start”: “node server.js”
}

The interface

Since HTML is not the focus here, you can go ahead and copy this to your index.html file:

<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>RealTime</title>
  <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
</head>

<body>
  <div class="bg-white overflow-hidden overflow-hidden shadow rounded-lg px-4 py-4 sm:px-6 w-4/5 mx-auto mt-8">
    <h2 class="text-2xl leading-8 font-extrabold text-gray-900 sm:text-3xl sm:leading-9">
      Chat
    </h2>

    <div class="px-4 py-5 sm:p-6" id="message-box">
      <!-- Content goes here -->
    </div>

    <div class="border-t border-gray-200 px-4 py-4 sm:px-6">
      <form id="form" action="#" method="POST" class="grid grid-cols-1 row-gap-6">
        <div>
          <div class="mt-1 relative rounded-md shadow-sm">
            <input id="input" placeholder="Start typing..."
              class="form-input py-3 px-4 block w-full transition ease-in-out duration-150">
          </div>
        </div>
        <button type="submit"
          class="w-full inline-flex items-center justify-center px-6 py-3 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition ease-in-out duration-150">
          Send message
        </button>
      </form>
    </div>

    <div class="border-t border-gray-200 px-4 py-4 sm:px-6">
      <h3 class="px-4 py-4">Who's online:</h3>
      <ul id="peer-list"
        class="px-6 py-3 max-w-0 w-full whitespace-no-wrap text-sm leading-5 font-medium text-gray-900">
        <!-- Content goes here -->
      </ul>
    </div>

  </div>
</body>
</html>

This is the basic structure of the chat app. There’s a box to display all messages, a form to type the message and a button to send it. All of the important parts have ids so that we can retrieve them in JavaScript later. Here, I’m using TailwindCSS to make it look good fast.

The server

Now, open server.js and add the following:

const fs = require('fs');
const http = require('http');
const SocketIO = require('socket.io');

// Prepares HTML file to be served
const content = fs.readFileSync(__dirname + '/index.html', 'utf8');
const httpServer = http.createServer((req, res) => {
  res.setHeader('Content-Type', 'text/html');
  res.setHeader('Content-Length', Buffer.byteLength(content));
  res.end(content);
})

// Creates socket.io connection
const io = SocketIO(httpServer);

// Handles "connect" event
io.on('connect', socket => {
	// Handles "message" event sent by client
  socket.on('message', data => {
		// Emits new message to every connected client
		io.emit('newMessage', {
			message: data
		})
	})
});

// Starts up server
httpServer.listen(3000, () => {
  console.log("đŸ”„ Listening on http://localhost:3000");
})

This enough for the basic functionality of the app. We could further simplify things by using a framework like express, but for now, a classic Node.js server will suffice. It serves the index.html file and then creates a Socket.io connection on line 14. Then we can use the event listening functionality to listen for a “connect” event emitted from the client and handle that connection. You can create your own event keywords (like  “connect”), but you have to keep in mind that there are a few keywords that should not be used as they conflict with the ones implemented by Socket.io. A few examples include connect, disconnect, reconnect and error. A full list of these event names can be found here.

On line 16 we listen for an event named “message” and pass a callback to handle the data received by that event. Then on line 18 we emit an event named “newMessage” to all connected sockets. Note that we listened on socket which is an individual client connected and we emit with io which is sort of a pool of sockets. You can always refer to this emit cheatsheet to see all the options you have, like emitting events to all connected sockets but the emitter, or emitting to “rooms” or sending privately from socket to socket.

Now, I want to make things more interesting and assign random names to the clients send these names to all clients so they know who’s connected and able to chat. Let’s add this:

const animals = [
  'fish',
  'cat',
  'tiger',
  'bear',
  'bull',
  'fox'
]

const colors = [
  'red',
  'green',
  'blue',
  'yellow',
  'purple',
  'pink'
]

/**
 * Generates a random name based on an animal and a color
 * 
 * @return {String}
 */
function randomName() {
  const color = colors[Math.floor(Math.random() * colors.length)]
  const animal = animals[Math.floor(Math.random() * animals.length)]

  return `${color}-${animal}`;
}

// Stores the names and ids of connected clients
const sockets = {}

// Creates socket.io connection
const io = SocketIO(httpServer);

Then, inside the “connect” event handling, let’s add a few new even handlers:

// Handles “connect” event
io.on('connect', socket => {
  sockets[socket.id] = randomName();
  socket.emit('name-generated', sockets[socket.id]);
  io.emit('update-peers', Object.values(sockets));

  // Handles “message” event sent by client
  socket.on('message', data => {
    // Emits new message to every connected client
    io.emit('newMessage', {
      sender: sockets[socket.id],
      message: data
    })
  });

  // Handles “disconnect” event
  socket.on('disconnect', () => {
    delete sockets[socket.id]
    io.emit('update-peers', Object.values(sockets))
  })
})

Here we’re basically wait for a client to connect, then we assign a random name to their socket id and send their “random name” so they know who they are. Then we send the list of connected socket names. We also need to handle the disconnect event, so if anyone disconnects, we update the list of connected sockets and send that to everyone in the chat to update their user interface.

Cool, now let’s implement the client so it can connect to the server and do its magic.

The client

Go to the index.html file and before closing the body tag, add the following:

<script src="/socket.io/socket.io.js"></script>
<script>

</script>

This will “import” the Socket.io script (when you’re building a more complex application and are using a module bundler, this will probably look different, as the import will happen in another JavaScript file, but this is out of the scope of this article).

Let’s start the program by getting access to a few elements that we will use throughout the script:

<script>
    const form = document.getElementById('form');
    const input = document.getElementById('input');
    const msgBox = document.getElementById('message-box');
    const peerList = document.getElementById('peer-list');
</script>

Now, in order to make use of Socket.io, we need to call it and store it in a variable, then we will start listening and emitting events:

<script>
  const form = document.getElementById('form');
  const input = document.getElementById('input');
  const msgBox = document.getElementById('message-box');
  const peerList = document.getElementById('peer-list');

  const socket = io();

  // Handles the "name-generated" event by storing the client's name in a variable
  socket.on('name-generated', () => ());

  // Handles the "update-peers" event by updating the peers list
  socket.on('update-peers', () => ());

  // Handles "newMessage" event and add that message to the chat
  socket.on('newMessage', () => ());

</script>

All of the events listed above with socket.on() are emitted by the server at some point, they are still not implemented (i.e. we still don’t do anything after we listened for those events, but we will do it shortly. Before that, let’s handle the submission of a message:

/**
 * Retrieves message from input and emits to the server
 * 
 * @param {Object} evt Event fired by the form submission
 */
function submitHandler(evt) {
  evt.preventDefault();
  socket.emit('message', input.value);
  input.value = ''
  msgBox.focus();
}

form.addEventListener('submit', submitHandler)

Here, we attach an event listener to the form. It will listen for the “submit” event and the submitHandler will prevent the default (just so the form does not trigger a page reload or navigating to the action attribute) and then we emit a “message” event containing the input field value. Then we clear the field and focus on something that is not a field, so if the user is in a mobile device, the keyboard goes away.

Now let’s go back to the other socket’s event listeners, an we will implement them. First, the simplest one, we listen for the “name-generated” event, if you remember, this is the event the server emits after generating a random name for the client. We need to store this name to use in other functions, so let’s create a variable in the same scope as the socket listeners like this:

let myName = ''
const socket = io();

// Handles the “name-generated” event by storing the client’s name in a variable
socket.on('name-generated', name => {
  myName = name
});

And now, let’s handle the “newMessage” event. This event is emitted by the server whenever a socket emits the “message” event. In other words, someone sends a message to the server and the server broadcasts this message to everyone connected:

// Handles “newMessage” event and add that message to the chat
socket.on('newMessage', ({ sender, message }) => {
  let name = document.createElement('strong');
  name.textContent = `${sender} says: `

  let msgEl = document.createElement('span');
  msgEl.textContent = message

  let paragraph = document.createElement('p');
  paragraph.appendChild(name);
  paragraph.appendChild(msgEl);

  msgBox.appendChild(paragraph);
});

Here, we expect the server to send an object containing the message and the sender’s name. We use this information to create a paragraph element that will be something like this: “blue-fish says: I am a new message”. And then appends this paragraph in the message box.

Let’s finish this by implementing the list of online clients:

// Handles the “update-peers” event by updating the peers list
socket.on('update-peers', peers => {
  peerList.innerHTML = ''

  const template = `<li class=“flex items-center space-x-3 lg:pl-2”>
    <div class=“flex-shrink-0 w-2 h-2 rounded-full bg-%PEER_COLOR%-600”></div>
      <span>%PEER_NAME%</span>
  </li>`

  for (const peer of peers) {
    let name = peer
    if (name === myName) {
      name += ' (you)'
    }
    peerList.innerHTML += template.replace('%PEER_NAME%', name).replace('%PEER_COLOR%', peer.split('-')[0])
  }
});

This might seem a little complex, but we just clear the list of online peers whenever we listen to the “update-peers” event and then create an HTML template to attach to the DOM with the names and colors of the connected clients, including yourself (which will use myName variable to add an indication that it’s you).

And that’s it! Now if you go run yarn start in your terminal and go to http://localhost:3000 you should see the chat page and if you connect with other browser windows, tabs or devices you will see the growing list of users connected. And if you close those windows, leaving the chat, the list will also update.

I hope you liked this article and will create awesome applications with this new tool under your belt đŸ»

]]>
<![CDATA[How I added Dark Mode to my blog using TailwindCSS]]> https://dberri.com/how-i-added-dark-mode-to-my-blog-using-tailwindcss https://dberri.com/how-i-added-dark-mode-to-my-blog-using-tailwindcss Fri, 30 Oct 2020 20:50:25 GMT If you’re here, you probably already know what dark mode is, so I’m not going to bore you with a long story about it. But if you want to read more about, here’s a very interesting article.

TL;DR: Dark mode is a color scheme of interfaces that display light text and elements on a dark background. Some say it’s for aesthetics only. Others say it mitigates digital eye strain, or even involved in the regulation of the circadian rhythm. Also, on AMOLED screens, it’s known to save more energy.

With that said, let’s dive right into the content:

A quick trick to make it “look like dark mode”

If you want to add dark mode to your website really quick, here’s a tip by Rik Schennink that I found on Dev.to:

html.dark-mode {
  filter: invert(100%);
}

html.dark-mode img {
  filter: invert(100%);
}

This will literally invert the colors of your website (although keeping the images the same color). That means white will become black and vice-versa, but also red will become green and blue will turn into orange (picture the color wheel now). In some cases, this will be fine, really, but if your UI has more colors and especially brand colors, inverting everything is not really what you want. I tried this on my blog just to see if I could get away with it and for the background and text it did work quite well, but for the tags (each has a distinct color) and for the header menu items, the inverted colors looked
 weird...

Enters prefer-colour-scheme (or how to properly add dark mode)

Remember media queries? Yes, those @media that makes your block of CSS apply only at specific conditions (like min or max width of viewport). Well, what if I told you that not only can you use that to make a responsive layout, but you can also recognise when the user has dark mode activated on his OS or user-agent (browser, etc)? Yeah! That blew my mind when I figured that there are many many more media features you can use.

There’s one called prefers-reduced-motion which means the user prefers less motion on the page (the name of the property gives it away, I guess), inverted-colors which means the user agent has inverted colors, and yes, there’s one called prefers-color-scheme  which detects if the user, well, prefers a dark or light color scheme. If the user does not express any preference, it is assumed to be light.

Here’s how we can use this:

body {
  color: #464646;
  background-color: #FFF;
}

@media (prefers-color-scheme: dark) {
  body {
    color: #FFF;
    background-color: #3E3E3E;
  }
}

Now, if we had this setting in our CSS, and the user visited our website without any active color scheme preference, the font color would be that dark gray and background would be white. If they visit with a dark mode setting active on their system, the website would automatically apply the white color to the font and the darker color to the background.

What I like the most about this setting is that the user does not need to care about this setting on each website they visit. They can set this on their browser or OS, and it will be automatically detected and applied. I, for example, don’t like to use dark mode all the time, so I configured my OS to apply it only at night, so all the websites that use this CSS media feature will apply the light color scheme during the day and the dark mode during the night. Pretty cool, huh?

You could stop right here and go to your CSS and start applying this all over your website, but if you are already using TailwindCSS I’ll show you a cool trick that will make dark mode development much more enjoyable.

How to add custom media queries in TailwindCSS

So, let’s say I have a title of an article in my blog with the following TailwindCSS classes:

<h1 class="text-black text-2xl sm:text-3xl md:text-4xl mb-2">
	How to build a real time chat app with Node.js and Socket.io
</h1>

Nothing new here, just sets the title to be black and a different sizes at different viewports. Now, this is what I want to do:

<h1 class="text-black dark:text-gray-400 text-2xl sm:text-3xl md:text-4xl mb-2">
	How to build a real time chat app with Node.js and Socket.io
</h1>

This would be cool right? Just like we have the different sizes (sm, md, lg and so on) I would like to have the dark mode to be an option like that, so I can choose a color and immediately choose what that element will look like on dark mode. In order to do that, we need to add a plugin to TailwindCSS. So go to your tailwind.config.js and in the extends section, add:

module.exports = {
  theme: {
    extend: {
      screens: {
        dark: { raw: '(prefers-color-scheme: dark)' }
      }
    }
  }
}

That piece of code will tell TailwindCSS to add a custom media query to its set and now you can use just like the other ones.  By the way, you can use the same trick to style for any of those media features.

Now you could do: bg-white dark:bg-black or text-blue dark:text-purple or you know, go crazy with it! (Ok, maybe not, think of your users first).

Adding that scheme and using it inline and in CSS

If you’ve been using TailwindCSS for some time, you know that not always you can get the desired result only using the created classes, so you would have to write some CSS or PostCSS with `@apply` for example. Since we added that custom media query to the config file, you can also do something like this:

@screen dark {
  .MyArticle {
    @apply text-white;
  }
}

And that’s it! Hopefully you got inspired to add dark mode (or use an exotic media feature) in your web projects :) Are you going add dark mode to a project you’re working on? I’d love to see how you’ve done it, so tweet  @ me

Pro tip: if you’re in a website that does not support dark mode and you want to read something in dark mode, try the browser’s “reading mode”. For me at least, it seems to be easier on the eyes.
]]>
<![CDATA[2020 in Review]]> https://dberri.com/2020-in-review https://dberri.com/2020-in-review Wed, 13 Jan 2021 08:32:46 GMT What a crazy year 2020 was, huh? So much stuff happened, ups and downs and ups again


I had finally left a job that required me to go to the office in the end of 2019, so I was already only working from home by the time we were requested to that in order to fight the pandemic. To be fair, even before I left that job I was already working from home as a freelancer and I felt very productive when doing so. The downsides were that I was not going to see that many people anymore and I wouldn’t have to commute. Yeah, I actually liked commuting only because I did so in 15 minutes riding my bike 😬.

Anyway, I decided to write this post to reflect on what I achieved last year to give me some perspective. Sometimes I get frustrated because it looks like I’m not achieving my goals but I tend to forget all the great things I did achieve along the way. I plan on doing this post every year from now on with this objective in mind.

So, to get started I’ll write about the projects I started or launched this year:

This Blog / Website

dberri.com

Yeah, so this was a long time coming. I was planning on launching a website or blog for quite sometime in 2019 but did not have the time or patience to set it up. In January, I decided I was going to do it and use the same stack I was using the my job at the time: Nuxt. I set up a local installation of Ghost CMS and used the JSON output to feed Nuxt which then creates a static site during a build in Netlify.

The first post went live on January 31st, 2020. Since then, I wrote 15 posts. That’s a little more than one post per month but it is 15 posts more than I had in 2019, so that’s a big win 😅. I still don’t think this stack is perfect for what I want, but it does the job well and while I don’t have too many posts, it will suffice. I played around with GraphCMS and seems cool but I’m still not sold on the idea of using it for this blog specifically (I might write a post about GraphCMS in the future because it is really cool, so stay tuned).

The purpose of writing and publishing these articles is to share things I’m learning, help other developers who are struggling to grasp concepts that I also had struggled to learn and improve my writing skills and the way I convey information. I also do some research while I write, so that makes me consolidate the knowledge on the topics. For this year, I plan to write at least 12 posts or one post per month. Let’s see in my 2021 review if accomplished this. đŸ€ž

project-name is not defined

stocks.dberri.com

I don’t have a name for this one yet, even though I launched it on May 1st, 2020. I basically developed it to “scratch my own itch” since I didn’t find anything similar on the web at that time. It’s a small application that helps me balance the risk in my investment portfolio. The user can type a stock ticker in the text field and add it, then assign a grade and the amount of shares they have. They do that for all the companies in their portfolio and while at it, the application highlights which stocks to buy next to keep the portfolio balanced according to the grades they assigned. If my explanation was not clear enough, here is the “about” page I wrote where I also added some examples: http://stocks.dberri.com/en-US/about .

I also developed this project using Nuxt, but since there is some API usage and it needs a Node.js server, I decided to host it on Heroku. It was also the first time I used the serverless and hosted a function in AWS Lambda to fetch prices and some other stock data.

A few months after I launched it, I discovered a similar application while watching a YouTube channel about personal finances, so I have some plans for this application going forward. For one, everything is stored in LocalStorage which works fine for me, but then I can't really use it in different browsers or devices, so I plan on improving the UX by using some authentication and database so the users can use the application on multiple devices. Maybe even a PWA would be cool 😎

usdbrl

npmjs.com/package/usdbrl-cli

I was quite excited to work on this one and launch it. It was the first npm package that I published. USD-BRL CLI is a tool that allows developers to fetch the exchange rate of USD/BRL  directly on the terminal.

To file taxes in Brazil for foreign earnings, one needs to convert that income to USD using the closed exchange rate of the payment date and then convert to BRL using the exchange rate on the 15th workday of the previous month. After a few months doing this manually, it struck me that I could automatize the process by fetching the data from the official Brazilian Central Bank website.

My plan with this was to also learn how to create CLI tools using Node.js and that goal was achieved 😬. Now, I use it every month and it does it job well, so I don’t have any plans to build upon it. Let’s see đŸ€·â€â™‚ïž.

2WEBP Optimizer

image-optimizer.dberri.com

The idea for this one came from the fact that I had to optimize a few images for a website I was building on my day job and I couldn’t find any online tool  that would convert images to the webp format and resize it to the sizes I wanted to. I also wanted to learn Vue 3 which was building up hype the whole year but I didn’t have the time to spend on it before. I also wanted to build something more elaborate with serverless and this was the perfect opportunity for it. I didn’t want this to be one of the many applications I have in my “dead side-projects folder”, so, to create some public accountability, I told my girlfriend about it so she would keep asking me about the progress of the development and I gave myself a deadline (December 31st, 2020).

Everyday, I built a small part of it, starting with the serverless functions. The backend is hosted on AWS, and I use Lambda, API Gateway, Cloudwatch, S3 and DynamoDB, which is all managed by the serverless framework. The frontend is hosted on Netlify as a simple Vue SPA. It was quite rewarding to finally hit the deploy button on the deadline and sip some wine to celebrate.

Now, this one is not completely done yet. It’s like an MVP, it does the job I want it to do, but I still want to iron out a few details, build a few features and advertise it so people can find it and use it. Hopefully the users will be very happy about it.

What I learned?

I started the year learning React and React Native. Back when I started to fell comfortable writing JavaScript, I decided to pick a framework and at that time Vue 2 had been released and was picking up steam and I started to read about it and comparisons with React which was already popular. My idea was to first learn Vue because it seemed to have a good learning curve, and it was bundled with Laravel which for me served as validation since I was and still am a big fan of that PHP framework (*Vue does not come with the Laravel installation anymore). Even though I decided to start with Vue, I always wanted to learn React too, just to see how they both differ, and what problems they solve and in which ways. Since now I already had a very good grasp of Vue and know how JavaScript frameworks work more or less, I decided to learn React and use it in a few side projects. It was a very good experience in my opinion, and it’s good to have another tool under my belt. I still prefer working with Vue and that pays the bills, so đŸ€·â€â™‚ïž

Because of my job and the requirements for the projects, I learned and used Laravel Nova,  learned how to use Twig in Laravel instead of Blade and also learned and used Alpine.js. All of these things might become blog posts in the future. Alpine.js already features in a few of them from last year, and this year I plan on bringing more related content.

I’m also learning quite a bit about accessibility, animations and performance on the web, and I’m very thankful for my coworkers who are helping me with this through code reviews, tips and so on.

What I want to learn in 2021?

Well, this year I’ll double down on learning and applying accessibility aspects correctly on the applications and websites I build, learn more about animations and motion design and keep improving the performance of the stuff I work on (gotta catch those 100s in Lighthouse 😜).

I’m currently learning Laravel Livewire while I build a side project to help my girlfriend with her business and hopefully I’ll be able to bring you more content and tutorials featuring it here.

And that’s a wrap. I wish you all a great 2021 and go after your goals 🚀

]]>
<![CDATA[How to use Twig in Laravel]]> https://dberri.com/how-to-use-twig-in-laravel https://dberri.com/how-to-use-twig-in-laravel Wed, 27 Jan 2021 21:05:44 GMT Laravel comes bundled with its own templating engine called Blade which is very powerful and a breeze to use, but there are times when it's not the best tool for the job. In my case, I first had to replace Blade with Twig in Laravel in order to use Miyagi for component development, and that made me spend a few hours out of my comfort zone learning how to integrate it in Laravel and because of that I'll share my lessons with you.

First you need to install Twig itself and a package called TwigBridge which  will allow you to seamlessly use it in Laravel. Open your terminal (who am I kidding, it's probably already open) and navigate to your application root folder, and run the following command:

composer install rcrowe/twigbridge twig/twig

After the installation is complete, you can run php artisan vendor:publish --provider="TwigBridge\ServiceProvider" which will publish the pacakge's configuration file at config/twigbridge.php. In this file you will be able to tweak a few things like which Laravel Facades are made available to Twig, as well as filters and global functions. With that, you are ready to start using Twig templates, so go ahead and create a new view inside resources/views called welcome.twig (if you don't already have one) to test that:

<!DOCTYPE html>
<html lang="{{ app.getLocale }}">
    <head>
        <!-- other regular head stuff -->
    </head>
    <body class="antialiased">
        <div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0">        
            {% if Route.has('login') %}
                <div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
                    {% if auth_check() %}
                        <a href="{{ url('/home') }}" class="text-sm text-gray-700 underline">Home</a>
                    {% else %}
                        <a href="{{ route('login') }}" class="text-sm text-gray-700 underline">Login</a>

                        {% if Route.has('register') %}
                            <a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 underline">Register</a>
                        {% endif %}
                    {% endif %}
                </div>
            {% endif %}

            <div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
                <div class="mt-8 bg-white dark:bg-gray-800 overflow-hidden shadow sm:rounded-lg">
                    <!-- irrelevant content -->

                    <div class="ml-4 text-center text-sm text-gray-500 sm:text-right sm:ml-0">
                        Laravel v{{ app.version }}
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>

I adapted the original welcome.blade.php view that comes with a fresh installation to show you how it would like like when using Twig. Notice the "Route" facade is readily available but instead of using the static method call like Route::has() you use Twig's dot notation: Route.has(). Other global functions like route, url and app are also available.

If it's a fresh install of Laravel, your base route will already point to the "welcome" view, otherwise just create a route in the routes/web.php file like this:

Route::get('/', function () {
	return view('welcome');
});

Now, let's go a little bit further and use components in the application. Go ahead and create resources/views/components/navbar/navbar.twig and add this content:

<nav>
  <ul>
    <li>
      <a href="{{ url('/') }}">Home</a>
    </li>
    {% for item in items %}
      <li>
        <a href="{{ item.url }}">{{ item.label }}</a>
      </li>
    {% endfor %}
  </ul>
</nav>

and in the welcome.twig view, you can include it just after opening the body tag:

{% include "./components/navbar/navbar.twig" with {
    items: [
      {
        url: url('/nachos'),
        label: 'Nachos'
      },
      {
        url: url('/tacos'),
        label: 'Tacos'
      }
    ]
} %}

And just like that, now you're able to include, embed and extend Twig templates in Laravel. Though, the more your application grow and the more components you have, it might become unwieldy to write all those paths to components. To fight that, you can use Twig namespaces. Instead of writing ../../components/component-name.twig you can write it like this: @components/component-name.twig. All you need to do is add this snippet to the boot method in app/Providers/AppServiceProvider.php:


$loader = new \Twig\Loader\FilesystemLoader();
$loader->addPath(base_path() . '/resources/views/components', 'components');
\Twig::getLoader()->addLoader($loader);

where base_path() . '/resources/views/components' is the path to the component directory and components is how you will call it (i.e. @components). You can add multiple paths like this that point to different (or the same) directories.

If you need to add some custom functionality that is not provided by a built in filter or function, you can create it in a plain php file, something like app/Twig/Functions.php and inside you could add:

<?php
 
namespace App\Twig;
 
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
 
class Functions extends AbstractExtension
{
    public function getFunctions()
    {
        return [
            new TwigFunction('now', [$this, 'now']),
        ];
    }
 
    public static function now()
    {
      return date('d/m/Y H:i:s');
    }
}

Then, go to config/twigbridge.php and add 'App\Twig\Functions', to enabled extensions like:

'extensions' => [
    'enabled' => [
        // ... other enabled extensions ...
        'App\Twig\Functions'
    ]
]

And then in your views you can use your function like:

<p>{{ now() }}</p>

You can do the same with filters in app/Twig/Filters.php:

<?php
 
namespace App\Twig;

use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;

class Filters extends AbstractExtension
{
    public function getFilters()
    {
        return [
            new TwigFilter('count_words', [$this, 'countWords']),
        ];
    }
 
    public static function countWords($sentence)
    {
      return count(explode(' ', $sentence));
    }
}

then in config/twigbridge.php:

'extensions' => [
    'enabled' => [
        // ... other enabled extensions ...
        'App\Twig\Functions',
        'App\Twig\Filters'
    ]
]

and you will be able to use it like this in your views:

<p>{{ 'Laravel has wonderful, thorough documentation covering every aspect of the framework. Whether you are new to the framework or have previous experience with Laravel, we recommend reading all of the documentation from beginning to end.'|count_words }}</p>

And that pretty much covers lots of use cases you would need with Twig in Laravel. Hope this article helps you save some hours of searching for answers in StackOverflow and documentation. If you find any other use cases not covered in this post, let me know and I'll update it. Until the next post!

]]>