Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Friday, December 31, 2021

Loading data asynchronously with Vue and Pinia

When working with Vue 3 Composition API and Pinia, I struggled a bit to put out an example of loading async data, where the request is handled by the Pinia store, which also owns the data.

It’s 2 AM and, finally, here is the store:

import {ref, reactive} from 'vue';
import {defineStore} from 'pinia';

const useStore = defineStore('fruits', () => {
	const fruits = reactive<string[]>([]);
	const loading = ref(false);

	return {
		fruits,
		loading,
		loadFruits(): Promise<void> {
			loading.value = true;
			return new Promise((resolve, _reject) => {
				setTimeout(() => {
					fruits.push('lemon', 'orange', 'peach', 'strawberry');
					loading.value = false;
					resolve();
				}, 2000);
			});
		},
	};
});

export default useStore;

And this is the single file component, which calls the store asynchronously:

<template>
	<div v-if="loading">Loading...<div>
	<div v-if="!loading">
		<div v-for="fruit of fruits" :key="fruit">
			{{fruit}}
		</div>
	</div>
</template>

<script setup lang="ts">
import {computed, onMounted} from 'vue';
import useStore from '@/model/store';

const store = useStore();
const loading = computed(() => store.loading);
const fruits = computed(() => store.fruits);

onMounted(() => {
	store.loadFruits();
});
</script>

Although I’m not seeing much advantage of Vue over React, Pinia is so much better than Redux insanity. Even with Redux Toolkit. I hope they don’t mess it up when Pinia becomes Vuex 5, as said.

Tuesday, December 28, 2021

Two-way binding input in React

As a follow-up to my big disappointment with Vue due to the lack of typed emits, even with TypeScript, I came back to think about a two-way binding input in React. Turns out, it’s pretty trivial to implement.

import { useState } from 'react';
	
interface TwoWayValue {
	value: string;
	setValue: (value: string) => void;
}

interface Props {
	data: TwoWayValue;
}

function TwoWayInput(props: Props) {
	return <input type="text" value={props.data.value}
		onChange={e => props.data.setValue(e.target.value)} />;
}

function useTwoWayState(initialValue: string): TwoWayValue {
	const [value, setValue] = useState(initialValue);
	return { value, setValue };
}

export { TwoWayValue, TwoWayInput, useTwoWayState };

Usage:

import { TwoWayInput, useTwoWayState } from 'two-way-input';

function App() {
	const nome = useTwoWayState('');

	return <div>
		<h1>{nome.value}</h1>
		<TwoWayInput type="text" data={nome} />
	</div>;
}

Now I’m still wondering about scoped CSS.

Monday, March 16, 2020

Choosing between useState and useReducer in React

Right now I’m designing a rather important architecture at work, and I’m using React for it. After writing a simple enough implementation with Redux, I came to the conclusion that, in fact, I don’t need Redux in this architecture, since I’m only storing auth information. Context API seems to be enough.

So, in order to have a mutable and reactive value in the application context, I must choose between useState and useReducer hooks.

While studying the matter, I found a rather good answer to this question:

  • When it’s just an independent element of state you’re managing: useState;
  • When one element of your state relies on the value of another element of your state in order to update: useReducer.

Although the examples given by the author are way too long, the idea is spot on. In my particular case, since the state is just a monolithic block of data, useState seems to fit my needs.

Friday, January 11, 2019

Node.js script to kill a JBoss instance

I’ve been dealing with consecutive JBoss restarts lately at work, and I needed a quick way to kill a running JBoss server instance. First I wrote a PHP script, but then I translated it to JavaScript, so it could run upon Node.js, which I’m using a lot lately.

I’m using it a lot.

Tuesday, January 8, 2019

Reading a file line by line in JavaScript and Node.js

While writing a small Node.js utility in JavaScript, I needed to read a text file from disk, line by line, into a string array. I wrote a small utility function to this task, which is async, returning a Promise.

Usage is pretty straightforward:

const readLines = require('./readLines');

async function foo() {
  const lines = await readLines('myFile.txt');
  console.log(lines.length);
}

Wednesday, December 26, 2018

Hot reloading in Node.js

While experimenting with Node.js, I was writing a small webserver to see how the things assembled together. Then I noticed that, each time I made a change in any file, I had to stop the Node server with Ctrl+C, and then restart it back again.

Obviously there’s an easier way. The nodemon utility is a direct replace for node command, and watches all files to reload the Node server automatically. Huge time saver, and very well done.

I installed it as a global NPM package, so I can promptly use it anywhere.

Thursday, December 6, 2018

Wikipedia Templates, using React and TypeScript

I’ve been testing out React recently with great joy. The ability to grow a web application with components is really good, so much I was able to refactor an old application, a Wikipedia template generator, making it easier to mantain while increasing its complexity, becoming multi-language.

First I started writing ES6 JavaScript, but soon TypeScript caught my eye. My previous experience with TypeScript was a bit traumatic, since it involved the awful Angular 2, but after an initial struggling to make everything work, I can say that I can’t imagine a large scale project written without the aid of TypeScript.

I struggled with some things like HTML5 routing, which seems to be a pain to deploy, so I just used HashRouter. Also, the deployment to gh-pages branch is neatly made with gh-pages utility. Also, I used Redux in a very simple way; I’ve seen overengineered examples that put me off at first, but Redux itself is very simple, and I ended up choosing it instead of MobX. I used hooks from the upcoming React 16.7, and I believe it will completely change the way React components are written, it’s really different.

The Wikipedia Templates project is open source and it’s hosted on GitHub. The project isn’t ready yet, but the basics are done, and I plan to grow it as I have time.

Thursday, July 5, 2018

Vue.js builds for non-root web directories

The React’s lack of scoped CSS annoyance made me want to try Vue.js, which solves this problem right out of the box. And so far, I’m liking this framework quite a lot, I must admit. The idea of single file components pleases me greatly.

This morning I stomped over a problem: I was generating a build release to run under a non-root directory in my web server, and the application was getting lost by searching the files at the server root.

Fortunately I’m using vue-cli v3, actually 3.0.0-rc.3, which has quick solution, as I found here.

Wednesday, October 18, 2017

React Material-UI: a FAB with an optional tooltip

I’m using Material UI library to have Material Design components on a rather complex React project. Despite being good, this library unfortunately doesn’t provide a tooltip option to the Float Action Button, something I need at the moment.

I found some useful information on this project issue, and after digging into the code of IconButton – which has a tooltip option – and Tooltip itself, I wrote a component that provides a Float Action Button with an optional tooltip:

The icon is ready to be used with Font Awesome project, which is what I’m using. Other minor adjustments I made, like font size, can just be removed too. And my project requirements are web only, so that’s all I tested.

Wednesday, October 11, 2017

Extending native objects in create-react-app

Despite being considered a bad practice by some, I like to extend native objects to add some syntatic sugar. However, when working in a React project with create-react-app, I receive the following ESLint error:

Array prototype is read only, properties should not be added  no-extend-native

Fortunately this warning can be disabled by adding a comment in the file where you extend the native object. Putting it all together, this is what I’ve got:

/*eslint no-extend-native: ["error", { "exceptions": ["Array"] }]*/
Object.defineProperty(Array.prototype, 'last', {
  get: function() {
    return this[this.length - 1];
  }
});
Object.defineProperty(Array.prototype, 'empty', {
  get: function() {
    return this.length === 0;
  }
});

Syntatic sugar added, clear console.

Monday, October 9, 2017

Absolute imports with create-react-app

When developing React using create-react-app with custom-react-scripts, I wanted to use absolute paths for import, and I found this: just adding NODE_PATH=src/ to “.env” file. It worked on Windows, but not on Linux.

After a while, I sorted it out: echo $NODE_PATH told me that my Linux had NODE_PATH being set somewhere. Then, being conservative, I just added unset NODE_PATH to “~/.bashrc” file, and it finally worked.

JavaScript development is a nightmare these days.

Tuesday, August 22, 2017

Simple minimalist JavaScript metronome

These days, when practicing guitar, I needed a metronome. Whey you type “metronome” on Google Search, it actually brings out a functional metronome – however it has no keyboard shortcut keys. So I decided to write a simple metronome, with keyboard keys, from scratch, to suit my needs.

Knowing the imprecision of JavaScript’s setTimeout function, I implemented a timer that adjusts itself each call, thus having a fairly reliable precision.

The code is so simple I decided not to create a GitHub repository, so I’m just sharing the whole source code here:

Tuesday, February 28, 2017

A graphic string tension calculator for guitar

I was assembling a custom string set for my guitar, and for that I needed to calculate the tensions of the string gauges, according to the scale length. Although I found a couple string tension calculators online, pretty much all of them had terrible usability. So I decided to write my own string tension calculator, with the best usability I could think of as an user.

It’s hosted on GitHub Pages: https://rodrigocfd.github.io/string-tension-calc/

I also made it open source on my GitHub account, and I hope to make further improvements.

Wednesday, January 29, 2014

JavaScript anonymous closures

Today I was going to write about my fruitful experiments in JavaScript using anonymous closures, scope and namespacing in general. But I stumbled across two articles that are so good, that I just felt the need to publish the links to both, because they explain everything I was about to write in great detail:
As of today, 3+ years old articles, which are still contemporary and relevant to JavaScript development. Highly recommended reading.

Saturday, August 31, 2013

Plotting icons in OpenStreetMap with GPS coordinates

I’ve been working on a project with GPS data, where a map plotting would be handy. At first I thought about using Google Maps, but it’s always better go open source, and since I’m an enthusiast of OpenStreetMap, I started searching a way to do it, and I found the OpenLayers project, which is insanely cool.

My goal: plot some small images on the map (icons), and when the cursor goes over an image, a popup would appear. On my way to make it happen, I had to develop some interesting things, that I share now with everyone, in the hope that it can be useful.

The first difficulty I found was to make OpenLayers understand GPS coordinates without much fuss. To do so, you must have a coordinate transformation, which is not exactly trivial. Below is a function to make this conversion trivial, yes, so that you’ll never need to worry about it again. The map argument is the map object you’re working upon, and lon and lat are floating point numbers.
function Geo(map, lat, lon) {
	return new OpenLayers.LonLat(lon, lat)
		.transform(new OpenLayers.Projection('EPSG:4326'),
			map.getProjectionObject());
}

// Example usage:
var map = new OpenLayers.Map('myMapDivId');
map.addLayer(new OpenLayers.Layer.OSM());
var coor = Geo(map, -5.773274, -35.204948);
map.setCenter(coor, 8);
Second, the image (icon) plotting. When you plot an image at a given point, this image is centered at that point. I wanted to plot an image of a pushpin, which points at the wrong location if centered – the very end of the pushpin will point to a location below the center of the image. It had to be moved above, so that the bottom of the image was exactly over the point.

The function below creates the image and plots it with a bottom offset, if needed. The img argument is a valid image URL, cx and cy are the size of the image in pixels, and bottomOffset is a boolean value. The function is asychronous and waits the image to be loaded; use the onDone callback, which receives the new marker as argument.
function PlotMarker(map, img, lat, lon, bottomOffset, onDone) {
	var imgObj = new Image();
	imgObj.src = img;
	imgObj.onload = function() {
		var sz = new OpenLayers.Size(imgObj.width, imgObj.height);
		var off = new OpenLayers.Pixel(-(sz.w / 2),
			bottomOffset ? -sz.h : -(sz.w / 2));
		var ico = new OpenLayers.Icon(img, sz, off);
		if(onDone !== undefined && onDone !== null)
			onDone(new OpenLayers.Marker(Geo(map, lat, lon), ico));
	};
}

// Example usage:
var map = new OpenLayers.Map('myMapDivId');
map.addLayer(new OpenLayers.Layer.OSM());
var markers = new OpenLayers.Layer.Markers('Markers');
map.addLayer(markers);
PlotMarker(map, 'pushpin.png', -5.773274, -35.204948,
	true, // it's a pushpin, I want it bottom-aligned
	function(mk) {
		markers.addMarker(mk);
	});
Below you have a fully functional web application composed of three files, which displays an OpenStreetMap map at the whole page, loads points from a separated JSON file, and renders it accordingly. There’s no server-side processing involved, but the JSON file could easily be something processed by the server, which would load the data from some sort of database. I used jQuery library to handle the mouseover event which shows the popup over the images, but it’s not really necessary if you want to use something else.

1) index.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8"/>
	<title>Rodrigo's map</title>
	<style>
	* { -moz-box-sizing:border-box; -webkit-box-sizing:border-box; box-sizing:border-box; }
	body { margin:0; font:10pt Arial; color:#242424; }
	#mapArea { position:absolute; width:100%; height:100%; }
	</style>
	<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script>
	<script src="http://openlayers.org/api/2.13.1/OpenLayers.js"></script>
	<script src="index.js"></script>
</head>
<body>
	<div id="mapArea"></div>
</body>
</html>
2) index.js
function PlotData(divId, data) {
	function Geo(map, lat, lon) {
		return new OpenLayers.LonLat(lon, lat)
			.transform(new OpenLayers.Projection('EPSG:4326'), map.getProjectionObject());
	}
	function PlotMarker(map, img, lat, lon, bottomOffset, onDone) {
		var imgObj = new Image();
		imgObj.src = img;
		imgObj.onload = function() {
			var sz = new OpenLayers.Size(imgObj.width, imgObj.height);
			var off = new OpenLayers.Pixel(-(sz.w / 2),
				bottomOffset ? -sz.h : -(sz.w / 2));
			var ico = new OpenLayers.Icon(img, sz, off);
			if(onDone !== undefined && onDone !== null)
				onDone(new OpenLayers.Marker(Geo(map, lat, lon), ico));
		};
	}

	var map = new OpenLayers.Map(divId);
	map.addLayer(new OpenLayers.Layer.OSM());
	map.setCenter(Geo(map, data.center[0], data.center[1]), data.zoom);

	var markers = new OpenLayers.Layer.Markers('Markers');
	map.addLayer(markers);

	var $map = $('#'+divId),
	$det = $('<div></div>').css({
			'position':'absolute',
			'padding':'0 4px',
			'display':'none',
			'box-shadow':'2px 2px 3px #999',
			'background-color':'rgba(255,255,255,.85)',
			'border':'1px solid #AAA'
		}).appendTo('body');
	$.each(data.points, function(i, pt) {
		PlotMarker(map, pt.icon, pt.coor[0], pt.coor[1], true, function(mk) {
			markers.addMarker(mk);
			mk.events.on({
				mouseover: function(ev) {
					$det.html(pt.text).css({
						left: (ev.pageX < $(document).width() / 2) ?
							ev.pageX+'px' : (ev.pageX - $det.outerWidth())+'px',
						top: (ev.pageY < $(document).height() / 2) ?
							ev.pageY+'px' : (ev.pageY - $det.outerHeight())+'px',
						display: 'block'
					});
					$map.css('cursor', 'pointer');
				},
				mouseout: function(ev) {
					$det.empty().css('display', 'none');
					$map.css('cursor', 'auto');
				}
			});
		});
	});
}

$(document).ready(function() {
	$.getJSON('data.json', function(data) {
		PlotData('mapArea', data);
	});
});
3) data.json – assumes image “pushpin.png” exists
{
	"center": [ -5.797942,-35.211782 ],
	"zoom": 14,
	"points": [
		{
			"coor": [ -5.799500,-35.21951 ],
			"icon": "pushpin.png",
			"text": "First point<br/>Anything here"
		},
		{
			"coor": [ -5.790982,-35.19409 ],
			"icon": "pushpin.png",
			"text": "Other <b>point</b> here"
		},
		{
			"coor": [ -5.802083,-35.20877 ],
			"icon": "pushpin.png",
			"text": "Something else<br/>placed here"
		}
	]
}
Enjoy.

Sunday, April 21, 2013

jQuery facepunches old IE versions

The jQuery team announced that they dropped support for Internet Explorer 6, 7 and 8 on the v2.0.0 of the library. That’s one huge and important move. First, because jQuery is widely used worldwide, and even endorsed by some big companies like Microsoft and Google. Second, because these versions of Internet Explorer still have a considerable percentage of the market share as of today. The jQuery guys announced that v1.9.x will still support these old IE versions, but who knows for how long.

This made me think the primary reason that jQuery was conceived for: a thin layer of cross-browser compatibility. If the library is not so widely compatible anymore, isn’t it losing its main reason to exist? Or maybe it’s heading towards becoming a framework like many others, just providing some useful and funny tricks.

I’m not against this decision, but given the wide adoption of jQuery in the wild, maybe it’s too early to force the migration to the latest IE version. Although when I think of the real pain that is to develop to those terrible browsers, I just raise my hands and scream hallelujah, they finally did it!

Wednesday, January 16, 2013

jQuery with requestAnimationFrame

The requestAnimationFrame specification is being cooked to replace the over-used setTimeout call when doing JavaScript animations. By specifically targeting animations, it’s highly optimized for this purpose. And since JavaScript animations are becoming more and more widespread, I believe it’s a great initiative, aiming to smooth all moving things inside your browser window. Concept and usage are well explained on this article.

If you use jQuery, however, you are not taking benefit of this new specification. It was used on a previous version, but it was dropped since then. I believe it should have been kept, misuse of the animation features shouldn’t have guided this decision. Anyway, when I was developing a jQuery plugin to embed requestAnimationFrame inside jQuery animation again, I just found out that someone had already done that. It’s available on GitHub and it works very well. The author has also made some claims on jQuery forums to make it default inside the library, with no success so far.

So, this is what you need to do (the file you need is within dist directory):
<script type="text/javascript" src="jquery-1.8.3.min.js"></script>
<script type="text/javascript" src="jquery.requestAnimationFrame.min.js"></script>
Animations instantly became smoother on Firefox on Linux. Highly recommended.

Friday, November 23, 2012

A JavaScript and HTML5 tree graph

Following the previous post, I needed a graphical representation of a tree graph to put on an HTML page. I thought it would be a great opportunity to learn this HTML5 thingy, which everyone is talking about these days. I ended up writing a full implementation of a tree graph using only HTML5 and pure JavaScript, thus avoiding any third-party JavaScript libraries.

However, I faced some problems. I had to plot more than 2,000 nodes, although not all at the same time, sometimes I had hundreds of them being shown. So I had to run into a series of optimizations of the algorithm, not only to speed up the internal calculations, but also the page rendering itself. On my first implementation, each node was a DIV, and the lines were drawn upon a canvas. Then at some point I decided to purely draw the whole monster on canvas, and I rewrote everything, taking the opportunity to add animation effects.

Since I consider it to be good and clean code, and believing that it could be useful to someone else, I published the whole code on GitHub at rodrigocfd/html5-tree-graph, along with an example file.

A hard work and a great experience, indeed.

Monday, November 12, 2012

A jQuery popup modal dialog

These days I needed to display a form in HTML. The simplest way to do this would be just go to another page, where the form would be displayed, then go back to the previous page after the submit. But the form had only a couple fields to use a whole page, and also it felt too cumbersome to go back and forth with the pages. Or I could use an ordinary window popup, but since it’s a modeless window, problems would easily arise.

So, with the aid of jQuery, I wrote a small jQuery plugin to use an ordinary hidden DIV as a popup modal dialog, so that I’d just have to keep the form into a DIV, and it would be shown without leave the current page. At some point, it saw that the OK and Cancel buttons could be automated themselves, so my form would need just the fields.

I also took the opportunity and used the same engine to generate automatic OK and OK/Cancel dialogs, thus beautifying the alert and confirm JavaScript stock functions. Worth mentioning that the popup can be stacked, that is, a modal popup can open another modal popup, and so on.

It ended up helping me a lot, so I published it on GitHub at rodrigocfd/jquery-modalForm, along with an example file.