Categories
JavaScript Vue

Redirect and Alias with Vue Router

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

Vue Router is a URL router that maps URLs to components.

In this article, we’ll look at how to add redirects and alias to our routes.

Redirect

We can add redirects from one route to another by adding the redirect property to a route.

For example, we can add a redirect as follows:

src/index.js :

const Bar = { template: "<div>bar</div>" };  
const routes = [  
  { path: "/foo", redirect: "/bar" },  
  { path: "/bar", component: Bar }  
];

const router = new VueRouter({  
  routes  
});

new Vue({  
  el: "#app",  
  router  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="[https://unpkg.com/vue/dist/vue.js](https://unpkg.com/vue/dist/vue.js)"></script>  
    <script src="[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js)"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then when we go to /#/bar in the browser, we get bar displayed.

A redirect can also target a named route. For example, we can write:

src/index.js :

const Bar = { template: "<div>bar</div>" };  
const routes = [  
  { path: "/foo", redirect: { name: "bar" } },  
  { path: "/bar", component: Bar, name: "bar" }  
];

const router = new VueRouter({  
  routes  
});

new Vue({  
  el: "#app",  
  router  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="[https://unpkg.com/vue/dist/vue.js](https://unpkg.com/vue/dist/vue.js)"></script>  
    <script src="[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js)"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then we get the same result.

We can also set the redirect property to a function. For instance, we can set it to a function that returns a route as follows:

src/index.js :

const Bar = { template: "<div>bar</div>" };  
const routes = [  
  { path: "/foo", redirect: to => "bar" },  
  { path: "/bar", component: Bar }  
];

const router = new VueRouter({  
  routes  
});

new Vue({  
  el: "#app",  
  router  
});

Then we get the same result as the previous examples.

Navigation guards aren’t applied on routes that redirect, only on its target.

Alias

A redirect means that when a user visits /foo then the URL will be replaced by /bar and then matched as /bar .

An alias means of /foo that’s set as /bar means that when the user visits /bar , the URL remains /bar , but it’ll be matched as if the user is visiting /foo .

For example, we can define a route with an alias as follows:

src/index.js :

const Foo = { template: "<div>foo</div>" };  
const routes = [{ path: "/foo", component: Foo, alias: "/bar" }];

const router = new VueRouter({  
  routes  
});new Vue({  
  el: "#app",  
  router  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then we get foo when we go to /#/foo or /#/bar .

An alias lets us map any route to any URL without being constrained by the nesting structure of the routes.

This is handy for mapping a URL that we want for a nested route.

For example, if we define an alias for a nested route as follows:

src/index.js :

const Foo = {  
  template: `<div>  
    foo  
    <router-view></router-view>  
  </div>`  
};  
const Bar = { template: "<div>bar</div>" };  
const routes = [  
  {  
    path: "/foo",  
    component: Foo,  
    children: [{ path: "/bar", component: Bar }]  
  }  
];

const router = new VueRouter({  
  routes  
});

new Vue({  
  el: "#app",  
  router  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then when we go to /#/foobar , we get:

foobar

displayed on the screen.

We also get the same thing displayed if we go to /#/foo/bar .

Therefore, we don’t have to follow the usual convention for nested route URLs if we use an alias.

Conclusion

Redirects and aliases are handy features of the Vue Router.

Redirects let us redirect from one route to another. Navigation guards aren’t run on redirect routes.

Aliases let us create a new URL for an existing route. When we go to the path or the alias, we get the same result when we map a path to an alias.

This is handy when we don’t want to follow the usual convention for nested route URLs for example.

Categories
JavaScript Vue

Vue.js Directives — Arguments and Values

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to make our directives accept arguments and values to directives and use them in our code.

Dynamic Directive Arguments

We can pass in arguments and values to a directive and then get these values from the binding parameter.

For example, we can make a directive that takes an argument and value as follows:

src/index.js :

Vue.directive("position", {  
  bind(el, binding, vnode) {  
    const validPositions = ["relative", "fixed", "absolute"];  
    if (validPositions.includes(binding.arg)) {  
      el.style.position = binding.arg;  
      el.style.top = `${binding.value}px`;  
    }  
  }  
});

new Vue({  
  el: "#app",  
  data: {  
    message: "Hello"  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="[https://cdn.jsdelivr.net/npm/vue/dist/vue.js](https://cdn.jsdelivr.net/npm/vue/dist/vue.js)"></script>  
  </head>  
  <body>  
    <div id="app">  
      <p v-position:absolute="50">{{message}}</p>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we defined a positiion directive that takes an argument, which is accessed in the bind hook by using the binding parameter.

The binding parameter has an arg property to get the argument, which is absolute in index.html .

binding.value gets us the value, which is 50 in index.html .

Then we get the element that the directive is bound to with el and then we set the positioning of the element via the values from the binding parameter.

Accepting arguments and values make directives much more flexible than without arguments.

Function Shorthand

If we only have code in the bind and update hooks, then we can just pass in a function with the same signature as those hooks.

For example, we can shorten the example above to:

Vue.directive("position", (el, binding, vnode) => {  
  const validPositions = ["relative", "fixed", "absolute"];  
  if (validPositions.includes(binding.arg)) {  
    el.style.position = binding.arg;  
    el.style.top = `${binding.value}px`;  
  }  
});

They both do the same thing.

Object Literals

We can pass in an object literal as the value of a directive if our directive needs to accept multiple values.

For example, we can write the following code to accept an object’s properties’ values in our directive:

src/index.js:

Vue.directive("position", (el, binding, vnode) => {  
  const validPositions = ["relative", "fixed", "absolute"];  
  const { position, top } = binding.value;  
  if (validPositions.includes(position)) {  
    el.style.position = position;  
    el.style.top = top;  
  }  
});

new Vue({  
  el: "#app",  
  data: {  
    message: "Hello"  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <p v-position="{ position: 'absolute', top: '50px' }">{{message}}</p>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The code above accepts an object with the position and top property and we take the object’s values and then set them as the style of the p element which the position directive is bound to.

A directive’s value can be any valid JavaScript expression.

Conclusion

Vue directives can take arguments and values. We can get argument values from the binding parameter of the bind hook via the binding.arg property.

To get the value passed to a directive, we can get it from the bind hook’s binding parameter via the binding.value property.

If our directive only has bind or update hooks, then we can shorten it to a function, which has the same signature as the bind or update hooks.

Categories
JavaScript

JavaScript Events Handlers — ondblclick and ondrag

In JavaScript, events are actions that happen in an app. They’re triggered by various things like inputs being entered, forms being submitted, and changes in an element like resizing, or errors that happen when an app is running, etc. We can assign event handler to handle these events. Events that happen to DOM elements can be handled by assigning an event handler to properties of the DOM object for the corresponding events. We will look at the ondblclick and ondrag event handlers and how to use them.

ondblclick

The ondblclick property of an HTML element let us assign an event handler function to handle the dblclick event, which is triggered when a user double clicks on an element. It fires after both clicks are completed. For example, we can use it to log the position where the double click was done. We first add the HTML code to display the position like in the following code:

<p id='dblclick-log'></p>

Then we add the JavaScript code with the event handler function assigned to the document.ondblclick property like we have in the code below:

const dblclickLog = document.getElementById('dblclick-log');

document.ondblclick = (e) => {  
  dblclickLog.textContent = `Double clicked on: (${e.clientX}, ${e.clientY})`;  
}

The e parameter above is a MouseEvent which has the following properties:

  • altKey — a boolean read-only property that returns true if the Qlt key was down when the mouse event was fired.
  • button — a read-only property that indicates the button number that was pressed (if applicable) when the mouse event was fired.
  • buttons — a read-only property that has the buttons being depressed (if any) when the mouse event was fired.
  • clientX — a read-only property that has the X coordinate of the mouse pointer in the document coordinates.
  • clientY — a read-only property that has the Y coordinate of the mouse pointer in the local document coordinates.
  • ctrlKey — a boolean read only that returns true if the control key was down when the mouse event was fired.
  • metaKey — a boolean read only that returns true if the meta key was down when the mouse event was fired. The meta key is the command or Apple key on Macintosh keyboards, and the Windows key on Windows keyboards.
  • movementX — a read only that has the X coordinate of the mouse pointer relative to the position of the last mousemove event.
  • movementY — a read only property that has the Y coordinate of the mouse pointer relative to the position of the last mousemove event.
  • offsetX — a read only property that has the X coordinate of the mouse pointer relative to the position of the padding edge of the target node
  • offsetY — a read only property that has the Y coordinate of the mouse pointer relative to the position of the padding edge of the target node
  • pageX — a read-only property that has the X coordinate of the mouse pointer relative to the whole document.
  • pageY — a read-only property the Y coordinate of the mouse pointer relative to the whole document.
  • region — a read-only property that returns the ID of the hit region affected by the event. If no hit region is affected, null is returned.
  • relatedTarget — a read-only property that has the secondary target for the event, if there is one.
  • screenX — a read-only property that has the X coordinate of the mouse pointer in global (screen) coordinates.
  • screenY — a read-only property that has the Y coordinate of the mouse pointer in global (screen) coordinates.
  • shiftKey — a boolean read-only property that returns true if the shift key was down when the mouse event was fired.
  • which — a read-only that has the button being pressed when the mouse event was fired.
  • mozPressure — a read-only that has the amount of pressure applied to a touch or tablet device when generating the event. This value ranges between 0.0 (minimum pressure) and 1.0 (maximum pressure). This is a deprecated (and non-standard) property. We should instead use the PointerEvent object’s pressure property.
  • mozInputSource — a read-only property that has the type of device that generated the event (one of the MOZ_SOURCE_* constants listed below). We can determine whether a mouse event was generated by an actual mouse or by a touch event, or detect other input sources the user is interacting with this property.
  • webkitForce — a read-only property that has the amount of pressure applied when clicking
  • x — a read-only property that is an alias for clientX.
  • y — a read-only property that is an alias for clientY.

Knowing that we can also track things like double-clicking with the Alt key down or double-clicking with the Windows key down. First, we add the following HTML code to display the results:

<p id='dblclick-log'></p>  
<p id='alt-pressed'></p>  
<p id='meta-pressed'></p>

Then in the JavaScript code, we add:

const dblclickLog = document.getElementById('dblclick-log');  
const altPressed = document.getElementById('alt-pressed');  
const metaPressed = document.getElementById('meta-pressed');

document.ondblclick = (e) => {  
  dblclickLog.textContent = `Double clicked on: (${e.clientX}, ${e.clientY})`;  
  altPressed.textContent = e.altKey ? 'Alt key pressed' : '';  
  metaPressed.textContent = e.metaKey ? 'Windows key pressed' : '';  
}

Then we can track whether the Alt or Windows key was down when we’re double clicking.

ondrag

The ondrag property lets us assign an event handler function that we define to handle the drag event, which is fired when an element that has the draggable attribute value set to true is being dragged on the browser screen. For example, we write the following HTML code to make an HTML box:

<div id='drag-box' draggable="true"></div>

Then we style it with CSS by making 100 pixels wide and 100 pixels tall and fill it with a red background:

#drag-box {  
  width: 100px;  
  height: 100px;  
  background-color: red;  
}

Finally, we can add the JavaScript code to track the dragging action of the box by assigning an event handler function to the ondrag property of the DOM element of our box that we created in the HTML code:

const dragBox = document.getElementById('drag-box');
dragBox.ondrag = (e) => {  
  console.log(e);  
}

Then when we start dragging, we should get the following output from the console.log when we drag the box:

altKey: false  
bubbles: true  
button: 0  
buttons: 1  
cancelBubble: false  
cancelable: true  
clientX: 90  
clientY: 68  
composed: true  
ctrlKey: false  
currentTarget: null  
dataTransfer: DataTransfer {dropEffect: "none", effectAllowed: "uninitialized", items: DataTransferItemList, types: Array(0), files: FileList}  
defaultPrevented: false  
detail: 0  
eventPhase: 0  
fromElement: null  
isTrusted: true  
layerX: 90  
layerY: 68  
metaKey: false  
movementX: 0  
movementY: 0  
offsetX: 82  
offsetY: 60  
pageX: 90  
pageY: 68  
path: (5) [div#drag-box, body, html, document, Window]  
relatedTarget: null  
returnValue: true  
screenX: 2250  
screenY: 520  
shiftKey: false  
sourceCapabilities: InputDeviceCapabilities {firesTouchEvents: false}  
srcElement: div#drag-box  
target: div#drag-box  
timeStamp: 5233.220000285655  
toElement: div#drag-box  
type: "drag"  
view: Window {parent: global, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}  
which: 1  
x: 90  
y: 68

The properties are the same ones that are from the MouseEvent and Event objects since the DragEvent object that we get from the parameter of the event handler function inherit from both. We can get the drag coordinate with x and y properties logged above.

The ondblclick property of an HTML element let us assign an event handler function to handle the dblclick event, which is triggered when a user double clicks on an element. We get a MouseEvent in the parameter of the event handler function so that we can use its properties to our liking. The ondrag property lets us assign an event handler function that we define to handle the drag event, which is fired when an element that has the draggable attribute value set to true is being dragged on the browser screen. We can get the drag coordinate with x and y properties from the DragEvent object.

Categories
JavaScript React

Built-in React Hooks — useCallback, useMemo and Refs

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

In this article, we look at the useCallback, useMemo, useRef, and useImperativeHandle hooks.

useCallback

We can use the useCallback hook to return a memoized callback.

It takes a callback function as the first argument, and an array of value that changes for the callback in the first argument to be called.

This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.

For example, we can use it as follows:

function App() {  
  const [count, setCount] = React.useState(0);  
  const memoizedIncrement = React.useCallback(() => {  
    setCount(count => count + 1);  
  }, [setCount]); 

  const memoizedDecrement = React.useCallback(() => {  
    setCount(count => count - 1);  
  }, [setCount]); 

  return (  
    <>  
      Count: {count}  
      <button onClick={memoizedDecrement}>Decrement</button>  
      <button onClick={memoizedIncrement}>Increment</button>  
    </>  
  );  
}

In the code above, we used the useCallback hook to create 2 new functions to let us update the count . We have setCount(count => count + 1) and setCount(count => count — 1) in the callback of useCallback .

And we cache the setCount function so that React won’t always return a new one in every render unless setCount changes.

Then in the onClick handlers, we use them as usual.

Every value referenced inside the callback should also appear in the dependencies array.

useMemo

useMemo caches values that are computed from a function. The first argument is a function that computes the value, and the second argument is an array with the dependencies that are used to compute the returned value.

useMemo runs during rendering. It’s used as a performance optimization. React may forget previously memoized value and recalculate them on the next render in cases it needs to free memory for offscreen components for example.

Therefore, we should write code that works with useMemo and then add it to optimize performance.

For example, we can use it as follows:

function App() {  
  const [count, setCount] = React.useState(0);  
  const doubleCount = React.useMemo(() => 2 * count, [count]); 

  return (  
    <>  
      Double Count: {doubleCount}  
      <button onClick={() => setCount(oldCount => oldCount - 1)}>  
        Decrement  
      </button>  
      <button onClick={() => setCount(oldCount => oldCount + 1)}>  
        Increment  
      </button>  
    </>  
  );  
}

In the code above, we use the useMemo hook by passing in a function that returns 2 times the count , and pass in an array with count inside as the 2nd argument.

useMemo caches the value of doubleCount until count changes.

Then we pass in functions to call setCount in the onClick props of the buttons.

Finally, we display the doubleCount value on the screen, which updates when we click on the buttons.

useRef

The useRef hook returns a mutable ref whose current property is initialized to the passed argument. The returned object will persist for the full lifetime of the component.

For example, we can use it as follows:

function App() {  
  const inputEl = React.useRef(null);  
  React.useEffect(() => {  
    inputEl.current.focus();  
  }, []);  
  return (  
    <>  
      <input ref={inputEl} type="text" />  
    </>  
  );  
}

In the code above, we created the inputEl ref by calling useRef with the value null .

Then once we pass inputEl as the ref of the input, we set inputEl.current to the input element’s DOM object.

Therefore, we can call focus on it to focus the input in the useEffect callback, which is only run during initial render, since we passed in an empty array to the second argument.

It can be used to keep any mutable value around. useRef creates a plain Javascript object.

The only difference between useRef() and creating a { current: ... } object ourselves is that useRef will give us the same ref object on every render.

useRef doesn’t notify us when the content changes.

useImperativeHandle

useImperativeHandle customizes the instance value that’s exposed to parent components when using a ref .

For example, we can use it as follows:

function Button(props, ref) {  
  const buttonRef = React.useRef();  
  React.useImperativeHandle(ref, () => ({  
    focus: () => {  
      buttonRef.current.focus();  
    }  
  }));  
  return <button ref={buttonRef}>Button</button>;  
}  
Button = React.forwardRef(Button);

function App() {  
  const buttonRef = React.useRef(null);  
  React.useEffect(() => {  
    buttonRef.current.focus();  
  }, []);  
  return (  
    <>  
      <Button ref={buttonRef} />  
    </>  
  );  
}

In the code above, we have:

React.useImperativeHandle(ref, () => ({  
    focus: () => {  
      buttonRef.current.focus();  
    }  
  }));

to customize the focus method to call buttonRef.current.focus(); .

Then we pass buttonRef in Button to the button ‘s ref as follows:

<button ref={buttonRef}>Button</button>;

Then to make the ref accessible to App , we run:

Button = React.forwardRef(Button);

Then in App , we run:

const buttonRef = React.useRef(null);

to create the ref and:

<Button ref={buttonRef} />

to set buttonRef to our exposed Button ‘s buttonRef after calling forwardRef to expose it to App .

Then we run:

React.useEffect(() => {  
    buttonRef.current.focus();  
  }, \[\]);

to focus the Button component’s button when App first renders.

Conclusion

We can use the useCallback hook to return a memoized callback, which we can call. It takes a callback as an argument and an array of dependencies that were referenced in the callback as the second argument.

useMemo caches values that are computed. It takes a function that returns a value as the first argument, and an array of values that the function depends on as the second argument.

useRef returns a mutable object whose current property is initialized to the initial value and can be passed into the ref prop of an element to set current to the DOM element.

useImperativeHandle customizes the behavior of the DOM element methods of the element that’s exposed via forwardRef to the parent component.

Categories
JavaScript Vue

Using Vue.js Single File Components

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to use single-file components.

Why do we need Single File Components?

We need single-file components because components defined by Vue.component can’t scale to larger projects.

If we want to use Vue.component on larger projects, we have to create an element to house them and also our templates to be strings on be in a script tag. Strings don’t have syntax highlighting support for templates.

They are both a plain if a component is bigger or has lots of nesting.

Also, all components are global when they’re defined with Vue.component . This forces all component names to be unique.

In addition, there’s no CSS support means that we have to add them to other places. It’s hard to modular CSS without single-file components.

We’re also stuck with HTML and ES5 JavaScript for templates rather than a preprocessor since there’s no build step to convert things from newer versions of JavaScript and non-HTML entities into HTML and ES5.

With single-file components, we get syntax highlighting, modules, and component-scoped CSS.

We can also use languages that we prefer like TypeScript, SCSS, CSS modules, etc, with them.

Note that separation of concerns isn’t the same as the separation of file types.

Defining and Using Single-File Components

We can use single-file components easily with the Vue CLI. To do this, we just create a folder, go into it, and then run:

npx vue create .

and select the default options.

Then we should get a starter app with single-file components that we can change.

The single-file components have the .vue extension.

When we create an app with Vue CLI, we get App.vue and components/HelloWorld.vue .

They have the structure:

<template>  
</template><script>  
</script><style>  
</style>

Where template has the HTML, script has the JavaScript code, and style has the styling code.

script can also be written in related languages like TypeScript.

For example, we can change App.vue to the following:

<template>  
  <div id="app">  
    <input v-model="message">  
    <p>{{message}}</p>  
    <button @click='showMessage'>Show Message</button>  
  </div>  
</template><script>  
export default {  
  name: "App",  
  data() {  
    return {  
      message: ""  
    };  
  },  
  methods: {  
    showMessage() {  
      alert(this.message);  
    }  
  }  
};  
</script><style>  
#app {  
  font-family: "Times", serif;  
}  
</style>

To run the project, we run:

npm run serve

Then we see an input where we can type in something, see the text we typed in in the p element, and have a button that shows an alert box with we typed in.

The script section has the same stuff as what we defined in the options object when we use Vue.component .

The data has the initial data as usual, and methods have methods that we can call from the template just like any other component.

style has the styling code in CSS or another language like SASS.

We can also separate the script and style code into their own files as follows:

src/app.js :

export default {  
  name: "App",  
  data() {  
    return {  
      message: ""  
    };  
  },  
  methods: {  
    showMessage() {  
      alert(this.message);  
    }  
  }  
};

src/app.css :

#app {  
  font-family: "Times", serif;  
}

App.vue :

<template>  
  <div id="app">  
    <input v-model="message">  
    <p>{{message}}</p>  
    <button @click="showMessage">Show Message</button>  
  </div>  
</template><script src='./app.js'></script><style src='./app.css'></style>

Conclusion

Single-file components make modularizing our app and using new technologies in them easier.

The code in a single-file component is separated into template , script and style sections.

The template section has the HTML code. script section has the logic in JavaScript or related languages like TypeScript. The style section has the styling code in CSS or related languages like SASS.

The script and style code can also be separated into their files and included with the src attribute.

We can make an app that uses single-file components easily with the Vue CLI.