Categories
JavaScript

JavaScript Events Handlers — Mouse Move and Mouse Out

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 an 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. In this article, we’ll look at the mousemove and mouseout events.

**onmousemove**

The onmousemove property of a DOM element is a property that lets us assign a function to handle the mousemove element. The mousemove event is fired when the user moves the mouse.

For example, we can use it to make tooltips that appear when we hover our mouse pointer over an element. First, we add some HTML code to add some p elements that’ll show a tooltip when we hover over it as we do in the following code:

<p><a href="#" data-tooltip="Tooltip 1" id='tooltip-1'>Tooltip 1</a></p>  
<p><a href="#" data-tooltip="Tooltip 2" id='tooltip-2'>Tooltip 2</a></p>

Then in the CSS code, we put the styles for our tooltip:

.tooltip {  
  position: absolute;  
  z-index: 9999;  
  padding: 6px;  
  background: #ffd;  
  border: 1px #886 solid;  
  border-radius: 5px;  
}

The tooltip class makes the tooltip div overlap with existing elements. It should show up near the mouse pointer after we set the position in the JavaScript code. Also we set the background color and the border for out tooltip.

Finally, we add the JavaScript code to show the tooltip once we assign our own onmousemove event handler function to it. We do this by adding the following code:

document.onmousemove = (event) => {  
  document.querySelectorAll('.tooltip').forEach(e => e.parentNode.removeChild(e)); 
  const id = event.srcElement.id;  
  const node = document.createElement('div');  
  node.className = 'tooltip';  
  node.textContent = event.target.dataset.tooltip;  
  node.setAttribute('hidden', '');  
  node.style.left = event.clientX + 20 + 'px';  
  node.style.top = event.clientY + 10 + 'px';  
  document.body.appendChild(node); 
  if (id == 'tooltip-1' || id == 'tooltip-2') {  
    node.removeAttribute('hidden');  
  }  
}

In our event handler function for the mousemove event, which we assigned to the onmousemove property of document , we get all the existing elements with the class tooltip so that we can remove them from the screen.

Then we create the tooltip by creating a div element with the createElement method . Then we set it to the tooltip class by setting the className property of node . Next, we get the value of the data-tooltip attribute then set it to the textContent property of node . And then we set the hidden attribute of the div.tooltip element which we just created. Then we set the left and top position to clientX + 20 pixels and clientY + 10 pixels. clientX and clientY are the X and Y coordinates of the mouse pointer’s current location, so that we can use that to position the tooltip near our mouse pointer.

Then if the id that the mouse hovered over is of ID tooltip-1 or tooltip-2 , then we remove the hidden attribute from the div.tooltip element which we just created and show the tooltip.

Once we did that, we should see tooltips when we hover either of the links.

**onmouseout**

The onmouseout property is a property of a DOM element where we can assign an event handler to an event handler function to process mouseout events. The mouseout event fires when the mouse leaves an element, such as when the mouse moves off an image, then the mouseout event is fired for that image element.

The mouseout event is fired whether the mouse moves to another element no matter where it is in the hierarchy, while the mouseout event propagates down to the descendants of the originating element. Therefore, the mouseout event may be fired many times, which can cause significant performance problems. If we need to listen to events in the whole element tree when the mouse is over a big element with lots of descendants, then it’s better to use the mouseout event.

For example, we can use it to track whether the mouse is over an element or not. First, we add an HTML div element as we do in the following code:

<div id='log'></div>

Then we add some CSS to add a border on the div and resize it:

#log {  
  width: 300px;  
  height: 150px;  
  border: 1px solid black;  
}

Finally, we can add the following JavaScript code to set different text depending on whether the mouse is over the log div or not:

const log = document.getElementById('log');  
log.onmouseover = () => {  
  log.textContent = 'Mouse over'  
}

log.onmouseout = () => {  
  log.textContent = 'Mouse left'  
}

Once we did that, we should see the ‘Mouse over’ message in the box when our mouse is over the box and ‘Mouse left’ when our mouse pointer left the box.

Another example of using the mouseout event is for showing different images depending on whether our mouse is over an image element or not. We just have the change the HTML as we do in the following code:

<img src='https://images.unsplash.com/photo-1503066211613-c17ebc9daef0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80' >

Then the CSS we change to the following:

img {  
  width: 300px;  
}

Finally, in the JavaScript code, we change the src of the img element in the onmouseover and onmouseout event handler functions as we do in the following code:

const img = document.querySelector('img');  
img.onmouseover = () => {  
  img.src = 'https://images.unsplash.com/photo-1503066211613-c17ebc9daef0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80'  
}

img.onmouseout = () => {  
  img.src = 'https://images.unsplash.com/photo-1546182990-dffeafbe841d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=740&q=80'
}

After that, we’ll get different images as we hover our mouse over or leave the img element.

The onmousemove property of a DOM element is a property that lets us assign a function to handle the mousemove element. The mousemove event is fired when the user moves the mouse.

The onmouseout property is a property of a DOM element where we can assign an event handler to an event handler function to process mouseout events. The mouseout event fires when the mouse leaves an element, such as when the mouse moves off an image, then the mouseout event is fired for that image element.

The mouseout event is fired whether the mouse moves to another element no matter where it is in the hierarchy, while the mouseout event propagates down to the descendants of the originating element. Therefore, the mouseout event may be fired many times, which can cause significant performance problems. If we need to listen to events in the whole element tree when the mouse is over a big element with lots of descendants, then it’s better to use the mouseout event.

Categories
JavaScript

JavaScript Events Handlers — LoadStart and Mouse Events

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 an 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. In this article, we’ll look at how to use the onloadstart , onlostpointercapture and onmouseup event handlers.

**onloadstart**

The onloadstart property is part of an XmlHttpRequest object. It lets us assign an event handler function to it to handle the loadstart event which is fired when the data has just begun loading.

For example, we can assign an event handler to the onloadstart event as we do in the following code:

const loadButtonSuccess = document.querySelector('.load.success');  
const loadButtonError = document.querySelector('.load.error');  
const loadButtonAbort = document.querySelector('.load.abort');  
const log = document.querySelector('.event-log');

function handleLoadStart(e) {  
  console.log(e);  
  log.textContent = log.textContent + `${e.type}: ${e.loaded} bytes transferred\n`;  
}

function addListeners(xhr) {  
  xhr.onloadstart = handleLoadStart;  
}

function get(url) {  
  log.textContent = '';  
  const xhr = new XMLHttpRequest();  
  addListeners(xhr);  
  xhr.open("GET", url);  
  xhr.send();  
  return xhr;  
}  
loadButtonSuccess.addEventListener('click', () => {  
  get('https://jsonplaceholder.typicode.com/todos/1');  
});  

loadButtonError.addEventListener('click', () => {  
  get('https://somewhere.org/i-dont-exist');  
});  

loadButtonAbort.addEventListener('click', () => {  
  get('https://jsonplaceholder.typicode.com/todos/1').abort();
});

In the code above, we have 3 buttons that run the click event handler whenever each button is clicked. The ‘Load (success)’ button will run the get function when it’s clicked. We’ll pass in a valid URL in the call for the get function. The click handling for the ‘Load (success)’ button is done by the following block:

loadButtonSuccess.addEventListener('click', () => {  
    get('https://jsonplaceholder.typicode.com/todos/1');  
});

The JSONPlaceholder has a test API that can serve any URL since it hosts a fake API so we can load it and not get any errors. Likewise, we have buttons for load a URL that’ll give an error, and another button to load a valid URL but then we abort the request. Once the XmlHttpRequest is finished, then the function we assigned to the onloadstart event handler, which is the handleLoadStart function, will be run.

The handleLoadStart function has one parameter, which is an Event object with some data about the request that’s finished. In the function, we get the value of the type property, which has the event type that’s fired, which should be loadstart . Also, we get the value of the loaded property which has the number of bytes of data that’s been loaded. It should always be 0 since nothing has been loaded yet when the loadstart event is triggered.

Then in the HTML code, we add the elements listed in the querySelector calls above:

<div class="controls">  
  <input class="load success" type="button" name="xhr" value="Load Start (success)" />  
  <br>  
  <input class="load error" type="button" name="xhr" value="Load Start (error)" />  
  <br>  
  <input class="load abort" type="button" name="xhr" value="Load Start (abort)" />  
</div>  
<textarea readonly class="event-log"></textarea>

We have 3 buttons to click on to load the successful HTTP request, an HTTP request with a non-existent URL, and an aborted HTTP request respectively. Then we display the event that’s fired and the number of bytes loaded.

onlostpointercapture

The onlostpointercapture property of a DOM element lets us assign an event handler function to handle the lostpointercapture event. The lostpointercapture event is fired when a captured pointer is released. Capturing a pointer means that a particular pointer event is to be re-targeted to a particular element instead of the normal element which will respond to the pointer event. It’s used to ensure that an element continues to receive pointer events even if the pointer device moved off the element.

For example, we can use it to log the lostpointercapture event as we do in the following code:

const para = document.querySelector('p');para.onlostpointercapture = () => {  
  console.log('Pointer been released!')  
};

para.onpointerdown = (event) => {  
  para.setPointerCapture(event.pointerId);  
};

Then in the corresponding HTML code, we put:

<p>Touch and Release</p>

In the code above, when we click on the p element, the pointerdown event is fired first, so the event handler that we assigned to the onpointerdown property of the para object, which is the DOM object of the p element, runs. In the event handler function, we run the the setPointerCapture method on the para object, which targets the mouse pointer events towards the p element.

Then the captured pointer is released when the mouse button is released, so the lostpointercapture method is fired. Then event handler we assigned to the onlostpointercapture event is run, which logs the ‘Pointer been released!’ message that we added.

onmousedown

The event handler function that we assign to the onmousedown property of a DOM element is run when the mousedown event is fired. The mousedown event is fired when the pointing device button is pressed while the pointer is inside the element. This is different from the click event as the click is fired after the full click action occurs. This means that the mouse button is both pressed and then released while the pointer remains inside the same element. mousedown is fired when the mouse button is initially pressed.

For example, we can use the onmousedown event handler to create a peeping effect where when we click on parts of the screen, then the part of the image that’s been clicked on will be revealed.

First we add the HTML code for the image and a black div element to hide the image as we do in the following code:

<div class='container'>  
  <div class="view" hidden></div>  
  <img src='https://images.unsplash.com/photo-1503066211613-c17ebc9daef0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80' ></div>

Then we add the CSS code for the HTML elements to make the .view div element have a black background:

.container {  
  background: black;  
  width: 500px;  
}

.view {  
  position: absolute;  
  width: 200px;  
  height: 200px;  
  background: white;  
  border-radius: 50%;  
}

img {  
  mix-blend-mode: darken;  
  width: 500px;  
}

Then in the JavaScript code, we add the onmousedown event handler to let us reveal the part of the image that the mouse pointer has clicked on:

const img = document.querySelector('img');  
const view = document.querySelector('.view');  
const container = document.querySelector('.container');
const showView = (event) => {  
  view.removeAttribute('hidden');  
  view.style.left = event.clientX - 50 + 'px';  
  view.style.top = event.clientY - 50 + 'px';  
  event.preventDefault();  
}

const moveView = (event) => {  
  view.style.left = event.clientX - 50 + 'px';  
  view.style.top = event.clientY - 50 + 'px';  
}

const hideView = (event) => {  
  view.setAttribute('hidden', '');  
}

container.onmousedown = showView;  
container.onmousemove = moveView;  
document.onmouseup = hideView;

The onmousedown property of the .container div element is set to the showView function, which is runs when the mouse button is down. Inside the function, we remove the hidden attribute from the .view div element to reveal the image underneath the div . From the event parameter, which has the Event object, we get the clientX and clientY properties, which has the mouse coordinates of the click location. We set that to the position of the view DOM object which represents the .view element. Then we called event.preventDefault() to stop the default action since we already did the revealing with the code before it.

The onmousemove event handler is set to the moveView function, which handles the mousemove event. The event is fired when the mouse moves. In the function, we set the .view element to the position of where the mouse pointer is currently located, again with the clientX and clientY properties of the event parameter, which is the MouseEvent object.

Then when the mouse button is released, the mouseup is event is fired, and the hideView function, which we assigned to the onmouseup property of the document object is called. We set the hidden attribute of the .view element to hide the .view element.

The onloadstart property is part of an XmlHttpRequest object. It lets us assign an event handler function to it to handle the loadstart event which is fired when the data has just begun loading.

The onlostpointercapture property of a DOM element lets us assign an event handler function to handle the lostpointercapture event. The lostpointercapture event is fired when a captured pointer is released.

The event handler function that we assign to the onmousedown property of a DOM element is run when the mousedown event is fired. The mousedown event is fired when the pointing device button is pressed while the pointer is inside the element. This is different from the click event as the click is fired after the full click action occurs. This means that the mouse button is both pressed and then released while the pointer remains inside the same element. mousedown is fired when the mouse button is initially pressed.

Categories
JavaScript JavaScript Basics

Lodash Features that are Available in Plain JavaScript

In recent years, new features in JavaScript have been rolling out at a rapid pace. The deficiencies that are filled in by other libraries before have become built-in features of plain JavaScript.

In this article, we’ll look at the methods in Lodash that are now available in plain JavaScript.

Map

Lodash has a map method to map each array entry to another by a function that we specify. It takes in an array and a function to transform the entries as arguments.

For example, we can call Lodash’s map method by writing:

const arr = _.map([1, 2, 3], n => n ** 2);

Then we get back:

[1, 4, 9]

JavaScript’s arrays now have the map method. We can use it as follows:

const arr = [1, 2, 3].map(n => n ** 2);

It also returns a new array but takes one less argument since it’s a method in an array.

Filter

The filter method in Lodash returns a new array that has the filtered entries of the original array. It takes an array and a function that returns the condition for the filter as arguments.

For example, we can write:

const arr = _.filter([1, 2, 3], n => n % 2 === 0);

to return an array with only even numbers.

With JavaScript array’s filter method, we can simplify the code a bit since it’s called on the array itself. We can rewrite it as:

const arr = [1, 2, 3].filter(n => n % 2 === 0);

In both cases, we get [2] back.

Reduce

Like the other 2 array methods, Lodash’s reduce method now has a plain JavaScript equivalent with the same name. Both of them let us combine our array entries into one thing.

For example, to get the sum of all the entries in an array, we can use the Lodash reduce method as follows:

const arr = _.reduce([1, 2, 3], (total, n) => total + n, 0);

The Lodash version took 3 arguments, which is the array to combine, the function used to combine the values, and the initial value for the combined value.

With JavaScript array’s reduce method, it’s similar except that we call it on the array directly as follows:

const arr = [1, 2, 3].reduce((total, n) => total + n, 0);

In both cases, we get 6 as a result.

Head

Lodash’s head method returns the first element of an array. For example:

const first = _.head([3, 4, 5]);

will get us 3 as the value of first .

We can do this with JavaScript by writing the following:

[3, 4, 5][0]

or:

const [first] = [3, 4, 5];

Tail

The Lodash’s tail method will get us everything but the first entry of an array.

For example, we can use it as follows:

const rest = _.tail([3, 4, 5]);

Then we get [4, 5] as the value of rest.

We can write the following to get everything but the first element of an array by writing:

const rest = [3, 4, 5].slice(1);

or:

const [first, ...rest] = [3, 4, 5];

The spread operator will give us the rest of the elements stored in the rest variable as the array, so we get the same thing.

Rest

Lodash’s rest method gets the extra arguments that are passed into the callback function that we pass into it that are in excess of the number of parameters and returns a function that let us do something with those extra arguments.

For example, if we have:

const restFn = _.rest((first, rest) => rest);

Then calling restFn(“foo”, “bar”, “baz”); will return [“bar”, “baz”] .

We have the rest operator to do the same thing in JavaScript’s rest operator:

const restFn = (first, ...rest) => rest

Spread

In Lodash, the spread method will return a function that takes a callback function, and then return a function where we pass in an array which we use as the arguments of the callback function we passed in.

For example, if we have:

const spreadFn = _.spread((foo, bar) => `${foo} ${bar}`);

Then when we call spreadFn as follows:

spreadFn(["foo", "bar", "baz"])

We get back:

'foo bar'

It’ll take each entry of the array passed into spreadFn , and pass it as the arguments of the function that we passed into the spread method.

This is the same as calling a function as is in JavaScript:

const spreadFn = function(foo, bar) {  
  return `${foo} ${bar}`;  
}

Then we can use the apply method on the function to do the same thing:

spreadFn.apply(null, ["foo", "bar", "baz"])

And we get the same result.

As we can see, lots of methods in Lodash have equivalents in plain JavaScript, especially array methods. So to reduce the reliance of libraries, we can use more plain JavaScript replacements here to replace existing uses of Lodash methods when they make code cleaner and lighter.

Categories
Angular JavaScript

How to Add Rich Text Editor to an Angular App with Localization

Adding a rich text editor to your Angular app is easy since there are libraries to do so already. It supports lots of features like changing fonts, adding or removing underline, adding lists, bolding text, changing default fonts, add pictures, adding placeholders, etc. Almost anything you can think of can be added to an editor. CKEditor is the rich text editor with the most comprehensive options available.

To use CKEditor in our Angular app, we use an Angular version of the editor available at https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/frameworks/angular.html, available as a Node package. This allows us to bind our input content to our model without writing code to do that ourselves.

We install the package by running:

npm i ng2-ckeditor

Then we include the following in the imports array of your module:

import { CKEditorModule } from 'ng2-ckeditor';  
import { FormsModule } from '@angular/forms';  
   
@NgModule({  
  // ...  
  imports: [CKEditorModule, FormsModule],  
  // ...  
})  
export class AppModule {}

In index.html , we add:

<script src="https://cdn.ckeditor.com/4.5.11/full-all/ckeditor.js"></script>

This includes all the plugins for CKEditor.

Then we add the following to our component, we add:

import { Component } from '@angular/core';  
   
@Component({  
  selector: 'sample',  
  template: `  
  <ckeditor  
    [(ngModel)]="content"  
    [config]="config"  
    [readonly]="false"  
    (change)="onChange($event)"  
    (editorChange)="onEditorChange($event)"   
    (ready)="onReady($event)"  
    (focus)="onFocus($event)"  
    (blur)="onBlur($event)"  
    (contentDom)="onContentDom($event)"  
    (fileUploadRequest)="onFileUploadRequest($event)"  
    (fileUploadResponse)="onFileUploadResponse($event)"  
    (paste)="onPaste($event)"  
    (drop)="onDrop($event)"  
    debounce="500">  
  </ckeditor>  
  `,  
})  
export class Editor{  
  content: string = '<p>Some html</p>'; 
  config: any = {  
    allowedContent: true,  
    toolbar: [['Bold', 'Italic', 'Underline', '-', 'NumberedList', 'BulletedList', 'Link', '-', 'CreatePlaceholder']],  
    removePlugins: 'elementspath',  
    resize_enabled: false,  
    extraPlugins: 'font,divarea,placeholder',  
    contentsCss: ["body {font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;}"],  
    autoParagraph: false,  
    enterMode: 2  
  };  
}

In the config , we define the options that are displayed in the editor. We can define the buttons shown in the toolbar with the toolbar property. We put all the options in the array. Full list of controls that you can display in the toolbar is at https://ckeditor.com/latest/samples/old/toolbar/toolbar.html. We have one array per toolbar row. We can also remove plugins that we don’t need with the removePlugins option, and add more plugins with the extraPlugins options. Plugins have to be installed separately if the full installation is not used like I did above.

It can handle model changes with the change handler. You can run code when the editor is loaded with the ready event. paste handler allows you to run code after something is pasted.

Now to change the language of the editor, we can add the language option to the config object. Use the standard language codes listed at https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes. For example, to set the language to Spanish, we put the following in the config object:

language: 'es',

It is very simple.

Another thing you might want to do with your editor is to customize the content of some of the elements. To do this, we can make an Angular directive and add some event handlers to the CKEDITOR object to set the content you want. For example, if you want to set labels of the inputs of the Placeholder plugin that we have above, we run:

ng g directive changeEditorOptions

Then we get:

import { Directive } from '@angular/core';  
@Directive({  
  selector: '[appChangeEditorOptions]'  
})  
export class ChangeEditorOptionsDirective { 
  constructor() {}
}

Next, we reference the CKEDITOR global object we got from putting <script src=”https://cdn.ckeditor.com/4.5.11/full-all/ckeditor.js"></script> in index.html and hook some event handlers to the CKEDITOR object and put that into the constructor. After that we get the following:

import { Directive } from '@angular/core';  
  
declare let CKEDITOR: any;  
declare let $: any;
@Directive({  
  selector: '[appChangeEditorOptions]'  
})  
export class ChangeEditorOptionsDirective{ 

  constructor() { 
    CKEDITOR.on(  
      'instanceReady',  
      (ev) => {  
  
      }  
    ); 

    CKEDITOR.on('dialogDefinition', (event) => {  
      if ('placeholder' === event.data.name) {  
  
      }  
    }); 
}}

instanceReady event is fired when the editor is loaded, and dialogDefinition is loaded when the user opens a dialog box from the editor.

To add a drop down to our Placeholders dialog box and change the placeholders available in the placeholder plugin we can add it into the dialogDefinition event handler. We can make our changes by adding the following:

if ('placeholder' === event.data.name) {  
  const input = event.data.definition.getContents('info').get('name');  
  const dialog = event.data.definition;  
  input.type = 'select';  
  input.items = [  
    ['First Name', 'first_name'],  
    ['Last Name', 'last_name'],  
    ['Link', 'link'],  
  ];}

The code works like this: after opening the dialog, we get the name element in the info section by calling event.data.definition.getContents(‘info’).get(‘name’); Then we change the input to select by setting that to input.type and we populate the input by assigning the array to input.items . The first item in the array is what is displayed and the second is the value.

At the end, we have:

import { Directive } from '@angular/core';  
  
declare let CKEDITOR: any;  
declare let $: any;

@Directive({  
  selector: '[appChangeEditorOptions]'  
})  
export class ChangeEditorOptionsDirective{ 
  constructor() { 
    CKEDITOR.on(  
      'instanceReady',  
      (ev) => {  
  
      }  
    ); 

    CKEDITOR.on('dialogDefinition', (event) => {  
      if ('placeholder' === event.data.name) {  
        const input = event.data.definition.getContents('info').get('name');  
        const dialog = event.data.definition;  
        input.type = 'select';  
        input.items = [  
          ['First Name', 'first_name'],  
          ['Last Name', 'last_name'],  
          ['Link', 'link'],  
        ];  
      }  
    }); }  
}

To run script with the editor loads, we put the following in the instanceReady handler:

const $script = document.createElement('script'),  
$editor_instance = CKEDITOR.instances[ev.editor.name];
$script.src = '//path/to/your/script';  
$script.onload = () => {  
  //run code after load  
}};

$editor_instance.document.getHead().$.appendChild($script);

Now we have:

import { Directive } from '@angular/core';  
  
declare const CKEDITOR: any;
@Directive({  
  selector: '[appChangeEditorOptions]'  
})  
export class ChangeEditorOptionsDirective{ 
  constructor() { 
    CKEDITOR.on(  
      'instanceReady',  
      (ev) => {  
         const $script = document.createElement('script'),  
         $editor_instance = CKEDITOR.instances[ev.editor.name]; 
         $script.src = '//path/to/your/script';  
         $script.onload = () => {  
           //run code after load  
         }}; $editor_instance.document.getHead().$.appendChild($script);  
      }  
    ); 

    CKEDITOR.on('dialogDefinition', (event) => {  
      if ('placeholder' === event.data.name) {  
        const input = event.data.definition.getContents('info').get('name');  
        const dialog = event.data.definition;  
        input.type = 'select';  
        input.items = [  
          ['First Name', 'first_name'],  
          ['Last Name', 'last_name'],  
          ['Link', 'link'],  
        ];  
      }  
    }); }  
}

Since CKEDITOR is global, we need to put declare keyword before it so that the compiler won’t think it’s undefined.

Apply this directive to your editor by adding:

import { Component } from '@angular/core';  
   
@Component({  
  selector: 'sample',  
  template: `  
  <div appChangeEditorOptions>    
    <ckeditor  
      [(ngModel)]="content"  
      [config]="config"  
      [readonly]="false"  
      (change)="onChange($event)"  
      (editorChange)="onEditorChange($event)"   
      (ready)="onReady($event)"  
      (focus)="onFocus($event)"  
      (blur)="onBlur($event)"  
      (fileUploadRequest)="onFileUploadRequest($event)"  
      (fileUploadResponse)="onFileUploadResponse($event)"  
      (paste)="onPaste($event)"  
      (drop)="onDrop($event)"  
      debounce="500">  
    </ckeditor>  
  </div>  
  `,  
})  
export class Editor {  
  content: string = '<p>Some html</p>';
  config: any = {  
    allowedContent: true,  
    toolbar: [['Bold', 'Italic', 'Underline', '-', 'NumberedList', 'BulletedList', 'Link', '-', 'CreatePlaceholder']],  
    removePlugins: 'elementspath',  
    resize_enabled: false,  
    extraPlugins: 'font,divarea,placeholder',  
    contentsCss: ["body {font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;}"],  
    autoParagraph: false,  
    enterMode: 2  
  };  
}

Also include, ChangeEditorOptionsDirective in the app.module.ts into the declaration section of the module if it’s not there already.

Categories
Angular JavaScript

Defining and Using Angular Attribute Directives

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at how to create and use attribute directives with Angular.

Build a Simple Attribute Directive

We can create a simple directive by using the Angular CLI.

To create one, we run:

ng generate directive highlight

to create the skeleton code for our directive.

Once we did that, we can write some code for our directive. We write the following code:

highlight.directive.ts :

import { Directive, ElementRef } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  constructor(el: ElementRef) {  
    el.nativeElement.style.backgroundColor = 'pink';  
  }  
}

The code above takes an ElementRef of the element that the directive is applied to.

Then we set the backgroundColor of it to 'pink' .

The bracket makes our directive an attribute directive.

We should prefix our directive names with something like app so that they don’t clash with HTML attribute names.

app.module.ts :

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { HighlightDirective } from './highlight.directive';

@NgModule({  
  declarations: [  
    AppComponent,  
    HighlightDirective  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }

We added HighlightDirective to our AppModule so we can use it.

Then we can use it in app.component.html as follows:

<div appHighlight>foo</div>

We then should see a pink div with ‘foo’ inside it.

Respond to User-Initiated Events

We can also use directives to detect and act on user-initialed events.

For example, we can write the change the HighlightDirective we had above as follows:

import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  constructor(private el: ElementRef) {    } 

  @HostListener('mouseenter') onMouseEnter() {  
    this.highlight('pink');  
  } 

  @HostListener('mouseleave') onMouseLeave() {  
    this.highlight(null);  
  } 

  private highlight(color: string){  
    this.el.nativeElement.style.color = color;  
  }  
}

We added the HostListener decorators to listen to the mouseenter and mouseleave events of the element that our highlight directive is applied to.

Therefore, when we hover over our text, we’ll see it turn pink. Otherwise, it’ll go back to its normal color.

Pass Values Into the Directive with an @Input Data Binding

We can use the @Input decorator as we do with components to pass data to directives.

For example, we can write the following:

highlight.directive.ts :

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  @Input() highlightColor: string;  
  constructor(private el: ElementRef) { } 

  ngOnInit() {  
    this.el.nativeElement.style.color = this.highlightColor;  
  }  
}

app.component.html :

<div appHighlight highlightColor='pink'>foo</div>

In the code above, we added the highlightColor input and then pass in the color string in app.component.html to set the color.

We’ll then get pink text since we set highlightColor to pink.

Bind to an @Input Alias

We can make the code above shorter if we change the @Input alias to have the same name as the directive name.

For example, we can write:

highlight.directive.ts :

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  @Input('appHighlight') highlightColor: string;  
  constructor(private el: ElementRef) { } 

  ngOnInit() {  
    this.el.nativeElement.style.color = this.highlightColor;  
  }  
}

Then we can use it as follows in app.component.html ;

<div appHighlight='pink'>foo</div>

As we can see, we don’t have to specify highlightColor separately anymore.

Bind to a Second Property

We can have multiple @Input s to bind to multiple properties.

For example, we can write the following:

highlight.directive.ts :

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  @Input('appHighlight') highlightColor: string;  
  @Input() defaultColor: string;  
  constructor(private el: ElementRef) { } 

  ngOnInit() {  
    this.el.nativeElement.style.color = this.highlightColor || this.defaultColor;  
  }  
}

In the code above, we have 2 @Inputs instead of one.

Now we can use it as follows in app.component.html :

<div appHighlight='pink' defaultColor='yellow'>foo</div>  
<div appHighlight defaultColor='green'>bar</div>

In the code above, we have appHighlight directive applied without any color string set in the second div.

Therefore, since defaultColor is set to 'green' , it’ll be applied as we specified in highlight.directive.ts ‘s ngOnInit method.

Conclusion

We define attribute directives by running ng g directive and name them with brackets outside the name.

Then we can pass in properties with @Input s and listen to events with @HostListener .

Also, we get the element that the directive is applied to and then apply whatever styles we want to it.