Arturo Estrada, Author at Perficient Blogs https://blogs.perficient.com/author/aestrada/ Expert Digital Insights Thu, 23 Feb 2023 16:37:56 +0000 en-US hourly 1 https://blogs.perficient.com/files/favicon-194x194-1-150x150.png Arturo Estrada, Author at Perficient Blogs https://blogs.perficient.com/author/aestrada/ 32 32 30508587 Should We Use Tailwind CSS? https://blogs.perficient.com/2022/05/26/should-we-use-tailwind-css/ https://blogs.perficient.com/2022/05/26/should-we-use-tailwind-css/#respond Thu, 26 May 2022 18:58:28 +0000 https://blogs.perficient.com/?p=322156

Tailwind CSS is a CSS framework described as follows:

“A utility-first CSS framework packed with classes like flex, pt-4, text-center and rotate-90 that can be composed to build any design, directly in your markup”.

– Tailwind CSS official documentation

Tailwind’s goal is to “Rapidly build modern websites without ever leaving your HTML” (tailwind css 2022). If you ask me, this is quite a strong statement. To put this to the test, I decided to use this CSS Framework, and from what I have seen, so far, you could potentially build an entire project without having to write much CSS (well, maybe a little… but really not much compared to an average web development project that I am used to, to put it in a broad way).

 

To be fair, I am just a developer among so many and, I too, have my own prejudices and expectations. Maybe to be able to offer a more educated opinion, I propose looking at what Tailwind offers, look at some trends in the market to see what other developers think about it and look at some pros and cons. With this I’m hoping to provide some pointers so you can see for yourself if this would make a wise choice for your project.

 

What does Tailwind CSS offer?

 

Some core concepts

 

The three concepts that I have found most useful so far are (1) Utility First, (2) Responsiveness and (3) State Management. There are of course more and you can read all about them here.

 

1. Utility First

Tailwind offers a wide range of utility classes that help you work within the constraints of your system, thereby reducing the amount of CSS code you would need to write for styling your web components. The sets of classes include sizing, coloring, shadows, fonts among others.

 

<div class=”chat-notification”>
<div class=”chat-notification-logo-wrapper”>
<img class=”chat-notification-logo” src=”/img/logo.svg” alt=”ChitChat Logo”>
</div>
<div class=”chat-notification-content”>
<h4 class=”chat-notification-title”>ChitChat</h4>
<p class=”chat-notification-message”>You have a new message!</p>
</div>
</div><style>
.chat-notification {
display: flex;
max-width: 24rem;
margin: 0 auto;
padding: 1.5rem;
border-radius: 0.5rem;
background-color: #fff;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.chat-notification-logo-wrapper {
flex-shrink: 0;
}
.chat-notification-logo {
height: 3rem;
width: 3rem;
}
.chat-notification-content {
margin-left: 1.5rem;
padding-top: 0.25rem;
}
.chat-notification-title {
color: #1a202c;
font-size: 1.25rem;
line-height: 1.25;
}
.chat-notification-message {
color: #718096;
font-size: 1rem;
line-height: 1.5;
}
</style>
<div class=”p-6 max-w-sm mx-auto bg-white rounded-xl shadow-lg flex items-center space-x-4″>
<div class=”shrink-0″>
<img class=”h-12 w-12″ src=”/img/logo.svg” alt=”ChitChat Logo”>
</div>
<div>
<div class=”text-xl font-medium text-black”>ChitChat</div>
<p class=”text-slate-500″>You have a new message!</p>
</div>
</div>

Figure 1: The left hand side represents a component with custom styles. The right hand side represents the same component using Tailwind CSS utility classes (taken from the official documentation)

 

2. Responsiveness

Tailwind focuses on responsive design and makes use of utility variants in the form of conditional classes that are applied at different breakpoints.

 

<img class=”w-16 md:w-32 lg:w-48″ src=”…”>

Figure 2: The above example is taken from the official documentation, and illustrates how different utilities can be used with prefixes in order to render elements differently depending on the media size.

 

3. State Management

Tailwind reduces the amount of work related to state management, such as how an html element should look like when the elements are hovered over or active.

 

<button class=”bg-sky-600 hover:bg-sky-700 …”>
Save changes
</button>

Figure 3: The above example, also taken from the official documentation, shows how different prefixes can be used as pseudoclasses.

 

Tailwind CSS is customizable

 

Tailwind offers a way to customize some features  such as theme colors, fonts, spacing and many more. This can be done by modifying a JavaScript configuration file that can be used to set values for default global variables. This is usually called tailwind.config.js

 

module.exports = {
content: [‘./src/**/*.{html,js}’],
theme: {
colors: {
‘blue’: ‘#1fb6ff’,
‘purple’: ‘#7e5bef’,
‘pink’: ‘#ff49db’,
‘orange’: ‘#ff7849’,
‘green’: ‘#13ce66’,
‘yellow’: ‘#ffc82c’,
‘gray-dark’: ‘#273444’,
‘gray’: ‘#8492a6’,
‘gray-light’: ‘#d3dce6’,
},
fontFamily: {
sans: [‘Graphik’, ‘sans-serif’],
serif: [‘Merriweather’, ‘serif’],
},
extend: {
spacing: {
‘8xl’: ’96rem’,
‘9xl’: ‘128rem’,
},
borderRadius: {
‘4xl’: ‘2rem’,
}
}
},
}

Figure 4: The above example from the official documentation, illustrates the usage of a configuration file to set or override global variables.

 

Grid and Flex Displays

 

Unlike some other frameworks that offer column rendering and a defined set of columns, Tailwind offers a more dynamic and flexible approach. You could render several columns using the flex display or you could use the grid display allowing you to control the gap between the columns and are not limited to a particular number of columns.

 

The trends

 

According to the State of CSS 2021, Tailwind became the fastest growing in popularity when compared to similar technologies. This is based on 4 measurements taken from responses to a survey from a wide variety of CSS developers across the world. These parameters include satisfaction, for which Tailwind scores at 78% (first place)interest, where Tailwind CSS gets a score of 58% (first place); usage, with a score of 39% (second place in this rubric after bootstrap); and finally awareness with an appalling 98% (second place after bootstrap). Bootstrap still remains the most used CSS framework and Tailwind seems to be closing the gap in the competition.

 

Figure 5: the image taken from “The state of CSS 2021” shows the satisfaction rate and the usage of Tailwind as of the release of the survey results.

 

Pros and cons

 

The following is a personal appraisal of what I have experienced using Tailwind CSS. By no means do I expect any reader to agree with me here. I’m just offering some conclusions based on my own experience with Tailwind

 

Pros

1. Greater Control and Customization

By default, similar frameworks have a theme that can be customized. With Tailwind CSS, you have a large set of utilities and are not constrained to work under the paradigms that Frameworks like Bootstrap or Materialize work under.

 

2. Responsiveness and faster styling process

As mentioned above, it offers responsive utilities as well as other cool features such as pseudo-class prefixes, so the overall time spent styling components is significantly reduced.

 

3. Well documented and good community support

I have found the documentation to be complete for the most part. There are already – since its launch in May 2019 – plenty of examples to draw from and answers provided by the community.

 

Cons

1. Feels like inline

A common complaint that I have seen in blog posts and articles, is that some developers find that using Tailwind CSS feels a lot like writing inline styles. This can be understood considering that a lot of class names are used to affect one CSS property at a time. To be fair, I believe this is a tradeoff of the “Utility First” approach, but I can live with that.

 

2. Steep learning curve and relies on having CSS experience

Given the large set of utilities provided by the framework, it does take some time and practice to feel comfortable using it even for an experienced developer.

 

Another thing to consider is that most classes are named after the property affected, e.g. flex, block, inline, etc. If a developer does not know CSS or is at a beginner level, then it may be difficult to make sense of the “Utility First” approach.

 

3. Does not have an out-of-the-box component library

Unlike Bootstrap, Materialize CSS, or other similar frameworks, Tailwind does not have a component library that could handle elements like popovers, modal dialogs, or toaster notifications or forms. This is not to mean that there are no implementations of these elements, but the approach is different. You may need to rely on other libraries or implement these yourself. However, there exist out-of-the-box component libraries that use Tailwinds such as Tailwind UI or Tailwind Component.

 

Note: The last 2 examples may require a premium version.

 

Wrap up

 

Tailwind is a CSS framework that uses a “Utility First” approach by offering a wide variety of classes available for styling components in a responsive manner. The use of this framework has grown significantly since its creation in 2019 and seems to be growing steadily. Tailwind is customizable, responsive, and well documented. As a trade-off, you also may have to deal with a learning curve that presupposes an above beginner CSS knowledge and does not have an out-of-the-box component library. With this information in mind, I would still conclude that it is worth trying and it can speed up development significantly once you get that hang of it.

 

References

]]>
https://blogs.perficient.com/2022/05/26/should-we-use-tailwind-css/feed/ 0 322156
Angular and Data-Driven Documents (D3.js) Part 2: Adding Interactivity https://blogs.perficient.com/2021/04/13/angular-and-data-driven-documents-d3-js-part-2-adding-interactivity/ https://blogs.perficient.com/2021/04/13/angular-and-data-driven-documents-d3-js-part-2-adding-interactivity/#respond Tue, 13 Apr 2021 18:18:23 +0000 https://blogs.perficient.com/?p=321781

In a previous article, I wrote a simple D3.js plot chart. The chart represents a simple line chart that displays visually the price of Bitcoin over time (here is a link to the example). This chart only includes the visualization but it does not have any interactivity. In this article, I will show how to add a simple tooltip and some interactivity to show how D3 and Angular can play along and achieve great results.

The Project So Far

As of now, the project structure looks as follows:

App

  • D3
    • charts
      • line-chart.component.ts
      • line-chart.component.html
    • d3-visualization.component.ts
    • d3-visualization.component.html
    • d3-visualization.service.ts
  • app.component.ts
  • app.component.html
  • app.module.ts

Adding lines and circle on hover

When we hover over the chart we want to display two lines that point to each axis for reference. In that intersection, we will display a circle. These lines will appear on the “mouseover” event, disappear on “mouseout” events, and update on “mousemove” event.

  1. Create a new method: setTooltip

setTooltip (): void {}

  1. We will add the following:

We will create a new container that will listen to mouse events. For this, we create a selection object as a component attribute.

focus: Selection<any, any, any, any>;

Inside setTooltip()we set the following property to focus:

this.focus = this.svg
.append(“g”)
.attr(“class”, “focus”)
.style(“display”, “none”);

We are appending a ‘g’ element to our existing svg element. Next, we create the lines and circle that will display on ‘mousemove’.

this.focus
.append(“line”)
.attr(“class”, “x-hover-line hover-line”)
.attr(“y1”, 0)
.attr(“y2”, this.height);

this.focus
.append(“line”)
.attr(“class”, “y-hover-line hover-line”)
.attr(“x1”, 0)
.attr(“x2”, this.width);

this.focus.append(“circle”).attr(“r”, 7.5);

Next, we append a rect element that measures the width and height of the chart. Here we add all the event listeners. For this, we will need to add the following styles (line-chart.component.css):

::ng-deep .line {
fill: none;
stroke-width: 2px;
}

::ng-deep .overlay {
fill: none;
pointer-events: all;
}

::ng-deep .focus circle {
fill: #f1f3f3;
stroke: #777;
stroke-width: 3px;
}

::ng-deep .hover-line {
stroke: #777;
stroke-width: 2px;
stroke-dasharray: 3, 3;
}

  1. Call setTooltip() inside ngOnInit().
ngOnInit(): void {
this.setSvgArea();
this.setAxes();
this.setTooltip();
this.displayLine();

}

At this point, if you hover over your chart you should see two sets of lines and a circle. The next thing we want to do is adding a tooltip that displays the date and value of the hovered element.

Adding a Tooltip

  1. Add tooltip styling

For our tooltip, we can use the following styles:

.d3-tooltip {
position: absolute;
top: 50;
left: 130px;
display: block;
width: auto;
height: auto;
padding: 0.5rem 1rem;
background-color: #777;
border: 1px solid #777;
color: #f1f3f3;
border-radius: 5px;
z-index: 8;
}

This can be added in line-chart.component.css.

  1. Create a new directive for a custom template

We will add a directive that we can use to display our data in a custom way and leverage the @angular/common pipes to display the date property and value as currency. So let’s go ahead and create it in the Angular CLI.

ng g directive D3/directives/d3-tooltip.directive

Our directive should look like this:

import { Directive, TemplateRef } from “@angular/core”;

@Directive({
selector: “[d3Tooltip]”,
})
export class D3TooltipDirective {
constructor(public tpl: TemplateRef<any>) {}
}

  1. Create tooltip template

Next, we can add out the tooltip template in the d3-visualization.component as a child element of app-line-chart.

<ng-container *ngIf=”data$ | async as data”>
<app-line-chart [data]=”data”>
<ng-template d3Tooltip let-d>
<ng-container *ngIf=”d”>
<p>
Date:
<span class=”font-weight-bold”>{{
d.date | date: “mediumDate”
}}</span>
</p>
<hr />
<p>
<span class=”font-weight-bold”>{{
d.value | currency: “USD”
}}</span>
<span class=”font-italic”> (USD)</span>
</p>
</ng-container>
</ng-template>
</app-line-chart>
</ng-container>
  1. Add tooltip reference

After creating the template we need to reference it in line-chart.component.html and line-chart.component.ts.

Edit line-chart.component.html to look like this:

<div class=”card”>
<div class=”card-header”>Example: Bitcoin Price Over Time</div>
<div class=”card-body” style=”position: relative”>
<div *ngIf=”tooltipTemplate && hovered” class=”d3-tooltip”>
<ng-container
*ngTemplateOutlet=”
tooltipTemplate?.tpl;
context: { $implicit: hovered }

></ng-container>
</div>
<figure #chartArea></figure>
</div>
</div>

Inside line-chart.component.ts we should add the template reference:

@ContentChild(D3TooltipDirective) tooltipTemplate: D3TooltipDirective;

  1. Make the tooltip interactive

We need a property that contains the hovered element:

hovered: { date: Date; value: number };

This needs to be updated every time we receive a “mousemove” event, so for this purpose, we will add this line inside setTooltip:

this.hovered = d;

setTooltip should look like this:

setTooltip(): void {
this.focus = this.svg
.append(“g”)
.attr(“class”, “focus”)
.style(“display”, “none”);

this.focus
.append(“line”)
.attr(“class”, “x-hover-line hover-line”)
.attr(“y1”, 0)
.attr(“y2”, this.height);

this.focus
.append(“line”)
.attr(“class”, “y-hover-line hover-line”)
.attr(“x1”, 0)
.attr(“x2”, this.width);

this.focus.append(“circle”).attr(“r”, 7.5);

this.svg
.append(“rect”)
.attr(“class”, “overlay”)
.attr(“width”, this.width)
.attr(“height”, this.height)
.on(“mouseover”, () => this.focus.style(“display”, null))
.on(“mouseout”, () => {
this.focus.style(“display”, “none”);
this.hovered = undefined;
})
.on(“mousemove”, (e) => {
const bisectDate = bisector((d: any) => d.date).left;
const x0 = this.x.invert(pointer(e)[0]);
const i = bisectDate(this.data, x0, 1);
const d0 = this.data[i – 1];
const d1 = this.data[i];
const d = (x0 as any) – d0.date > d1.date – (x0 as any) ? d1 : d0;

this.hovered = d;

this.focus.attr(
“transform”,
translate(${this.x(d.date)}, ${this.y(d.value)})
);

this.focus
.select(“.x-hover-line”)
.attr(“y2”, this.height – this.y(d.value));

this.focus.select(“.y-hover-line”).attr(“x2”, -this.x(d.date));
});
}

In Conclusion

With all the elements in place, the result will be what the image below shows.

The project structure with the new elements should be something similar to this.

App

  • D3
    • directives
      • d3-tooltip.directive.ts
    • charts
      • line-chart.component.css
      • line-chart.component.ts
      • line-chart.component.html
    • d3-visualization.component.ts
    • d3-visualization.service.ts
  • app.component.ts
  • app.component.html
  • app.module.ts

The files that have been edited or added should be the following

  1. d3-tooltip.directive
import { Directive, TemplateRef } from “@angular/core”;

@Directive({
selector: “[d3Tooltip]”,
})
export class D3TooltipDirective {
constructor(public tpl: TemplateRef<any>) {}
}

  1. line-chart.component.css
::ng-deep .line {
fill: none;
stroke-width: 2px;
}

::ng-deep .overlay {
fill: none;
pointer-events: all;
}

::ng-deep .focus circle {
fill: #f1f3f3;
stroke: #777;
stroke-width: 3px;
}

::ng-deep .hover-line {
stroke: #777;
stroke-width: 2px;
stroke-dasharray: 3, 3;
}

.d3-tooltip {
position: absolute;
top: 50;
left: 130px;
display: block;
width: auto;
height: auto;
padding: 0.5rem 1rem;
background-color: #777;
border: 1px solid #777;
color: #f1f3f3;
border-radius: 5px;
z-index: 8;
}

  1. line-chart.component.ts
import {
Component,
ContentChild,
ElementRef,
Input,
OnInit,
ViewChild,
} from “@angular/core”;
import {
Selection,
select,
scaleTime,
scaleLinear,
max,
extent,
axisBottom,
axisLeft,
line,
pointer,
ScaleTime,
ScaleLinear,
bisector,
} from “d3”;
import { D3TooltipDirective } from “../directives/d3-tooltip.directive”;

@Component({
selector: “app-line-chart”,
templateUrl: “./line-chart.component.html”,
styleUrls: [“./line-chart.component.css”],
})
export class LineChartComponent implements OnInit {
@Input() data: { date: any; value: number }[];
@ViewChild(“chartArea”, { static: true }) chartArea: ElementRef<HTMLElement>;
@ContentChild(D3TooltipDirective) tooltipTemplate: D3TooltipDirective;

margin = { top: 10, right: 20, bottom: 30, left: 60 };
width: number;
height = 400 – this.margin.top – this.margin.bottom;
svg: Selection<any, any, any, any>;
x: ScaleTime<any, any>;
y: ScaleLinear<any, any>;
focus: Selection<any, any, any, any>;
hovered: { date: Date; value: number };

ngOnInit(): void {
this.setSvgArea();
this.setAxes();
this.setTooltip();
this.displayLine();
}

setSvgArea(): void {
this.width =
this.chartArea.nativeElement.offsetWidth –
this.margin.left –
this.margin.right;

this.svg = select(this.chartArea.nativeElement)
.append(“svg”)
.attr(“width”, this.width + this.margin.left + this.margin.right)
.attr(“height”, this.height + this.margin.top + this.margin.bottom)
.append(“g”)
.attr(“transform”, translate(${this.margin.left}, ${this.margin.top}));
}

setAxes(): void {
this.x = scaleTime()
.domain(extent(this.data, (d) => d.date))
.range([0, this.width]);
this.y = scaleLinear()
.domain([0, max(this.data, (d) => d.value)])
.range([this.height, 0]);

this.svg
.append(“g”)
.attr(“transform”, translate(0, ${this.height}))
.call(axisBottom(this.x));
this.svg.append(“g”).call(axisLeft(this.y));
}

displayLine(): void {
this.svg
.append(“path”)
.attr(“class”, “line”)
.datum(this.data)
.attr(“fill”, “none”)
.attr(“stroke”, “steelblue”)
.attr(“strokewidth”, 1.5)
.attr(
“d”,
line<{ date: any; value: number }>()
.x((d) => this.x(d.date))
.y((d) => this.y(d.value))
);
}

setTooltip(): void {
this.focus = this.svg
.append(“g”)
.attr(“class”, “focus”)
.style(“display”, “none”);

this.focus
.append(“line”)
.attr(“class”, “x-hover-line hover-line”)
.attr(“y1”, 0)
.attr(“y2”, this.height);

this.focus
.append(“line”)
.attr(“class”, “y-hover-line hover-line”)
.attr(“x1”, 0)
.attr(“x2”, this.width);

this.focus.append(“circle”).attr(“r”, 7.5);

this.focus.append(“text”).attr(“x”, 15).attr(“dy”, “.31em”);

this.svg
.append(“rect”)
.attr(“class”, “overlay”)
.attr(“width”, this.width)
.attr(“height”, this.height)
.on(“mouseover”, () => this.focus.style(“display”, null))
.on(“mouseout”, () => {
this.focus.style(“display”, “none”);
this.hovered = undefined;
})
.on(“mousemove”, (e) => {
const bisectDate = bisector((d: any) => d.date).left;
const x0 = this.x.invert(pointer(e)[0]);
const i = bisectDate(this.data, x0, 1);
const d0 = this.data[i – 1];
const d1 = this.data[i];
const d = (x0 as any) – d0.date > d1.date – (x0 as any) ? d1 : d0;

this.hovered = d;

this.focus.attr(
“transform”,
translate(${this.x(d.date)}, ${this.y(d.value)})
);

this.focus
.select(“.x-hover-line”)
.attr(“y2”, this.height – this.y(d.value));

this.focus.select(“.y-hover-line”).attr(“x2”, -this.x(d.date));
});
}
}

  1. line-chart.component.html
<div class=”card”>
<div class=”card-header”>Example: Bitcoin Price Over Time</div>
<div class=”card-body” style=”position: relative”>
<div *ngIf=”tooltipTemplate && hovered” class=”d3-tooltip”>
<ng-container
*ngTemplateOutlet=”
tooltipTemplate?.tpl;
context: { $implicit: hovered }

></ng-container>
</div>
<figure #chartArea></figure>
</div>
</div>
  1. d3-visualization.component.ts
import { Component, OnDestroy, OnInit } from “@angular/core”;
import { Observable, Subject } from “rxjs”;
import { takeUntil } from “rxjs/operators”;
import { D3VisualizationService } from “../D3/d3-visualization.service”;

@Component({
selector: “app-d3-visualization”,
template: &lt;ng-container *ngIf="data$ | async as data"&gt;
&lt;app-line-chart [data]="data"&gt;
&lt;ng-template d3Tooltip let-d&gt;
&lt;ng-container *ngIf="d"&gt;
&lt;p&gt;
Date:
&lt;span class="font-weight-bold"&gt;{{
d.date | date: "mediumDate"
}}&lt;/span&gt;
&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;
&lt;span class="font-weight-bold"&gt;{{
d.value | currency: "USD"
}}&lt;/span&gt;
&lt;span class="font-italic"&gt; (USD)&lt;/span&gt;
&lt;/p&gt;
&lt;/ng-container&gt;
&lt;/ng-template&gt;
&lt;/app-line-chart&gt;
&lt;/ng-container&gt;
,
})
export class D3VisualizationComponent implements OnInit, OnDestroy {
data$: Observable<any>;
private unsubscribe$ = new Subject<void>();

constructor(private service: D3VisualizationService) {}

ngOnInit(): void {
this.data$ = this.service.fetchData().pipe(takeUntil(this.unsubscribe$));
}

ngOnDestroy(): void {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
}

A complete example can be found in Stackblitz for complete reference.

]]>
https://blogs.perficient.com/2021/04/13/angular-and-data-driven-documents-d3-js-part-2-adding-interactivity/feed/ 0 321781
Angular and Data-Driven Documents (D3.js) Part 1: Creating Chart https://blogs.perficient.com/2021/04/01/angular-and-data-driven-documents-d3-js-part-1-creating-chart/ https://blogs.perficient.com/2021/04/01/angular-and-data-driven-documents-d3-js-part-1-creating-chart/#respond Thu, 01 Apr 2021 17:40:20 +0000 https://blogs.perficient.com/?p=321779

In this 2 part article, I will discuss D3 along with Angular. D3 is a powerful JavaScript library that enables you to develop interactive data visualizations. Visualizations often include charts, network diagrams, maps, among others. There are many libraries available for Angular that have nice and easy to work with components and directives that you can use to build different kinds of charts. However, let’s suppose that you would like to depict in a map the areas where you would be more likely to find single people, or make an interactive network diagram of beneficiaries of a certain organization. In those cases is where D3 comes in handy.

 

A common misconception about D3 is that it is conceived as a charting library. While it is true that you can build any kind of chart using D3, its scope goes way beyond that. “D3.js is a JavaScript library for manipulating documents based on data” (D3.js 2021). This means that you can build not only charts but also create any kind of data visualization.

 

In my experience, there are times when a project requirement needs to be a custom visualization such as a map, a venue, or a network diagram. Often, these requirements need that you build that customization. Let’s look at a basic example of how this can be done in an Angular project.

 

Set up project

1. Create New Project

The first thing will be to start a new project.

ng new d3-example

 

2. Install dependencies

Once the new project is created we add the dependencies that we will need for this example. For this particular project, we will use Bootstrap for styling and D3 to build a line chart.

npm install bootstrap d3 –save

Line Chart Example

The visualization example is a simple line chart that depicts the market value of Bitcoin over a period of time.

 

1. Edit app.component.html

Let’s go ahead and replace the following code in app.component.html

<nav class=”navbar navbar-expand-lg navbar-light bg-light”>
<div class=”container”>
<a class=”navbar-brand” href=”#”>Angular D3 Example</a>
</div>
</nav>
<div class=”container mt-3″>
<!– TODO: chart container goes here –>
</div>

This should leave us with a nice navigation bar and the container where the chart will be.

 

2. Create a new service.

Let’s type into our console the following:

ng g service D3/d3-visualization

Edit the service with the following lines of code:

private dataUrl = “https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/3_TwoNumOrdered_comma.csv”;

fetchData(): Observable<{ date: Date; value: number }[]> {
const parseTime = timeParse(“%Y-%m-%d”);
return from(csv(this.dataUrl)).pipe(
map((res: any[]) =>
res.map(d => ({
…d,
date: parseTime(d.date),
value: +d.value
}))
)
);
}

We are getting this data from https://www.d3-graph-gallery.com/line which is a community-supported gallery with different examples of visualizations.

We are using ‘d3.csv’ to convert our response to a standard Javascript object, we wrap this in a ‘from’ function which in turn will convert the promise object returned from d3.csv to an observable. Since the data we receive is formatted as a string, we will need to map it so that our date value is a Date type and our value is a numeric type.

Note: It should be noted that the function d3.csv is imported individually. This is particularly recommended for tree shaking when building your application for a production environment. Either way, this would work if used as d3.csv in which case it should be imported as follows

import * as d3 from ‘d3’

or optimally just

import { csv } from ‘d3’

 

3. Create visualization components

Let’s use the CLI and create the component that will connect to the service and display the data.

ng g component D3/d3-visualization

This should generate these two files:

  • d3-visualization.component.html
  • d3-visualization.component.ts

We will also want to create a separate component that will receive the data where we will build the chart using D3 so we should go ahead and generate the component as well.

ng g component D3/charts/line-chart

The result should be these files:

  • line-chart.component.ts
  • line-chart.component.html

4. Edit d3-visualization.component files

 

d3-visualization.component.ts should look like this:

export class D3VisualizationComponent implements OnInit, OnDestroy {
data$: Observable<any>;
private unsubscribe$ = new Subject<void>();constructor(private service: D3VisualizationService) {}ngOnInit(): void {
this.data$ = this.service.fetchData().pipe(takeUntil(this.unsubscribe$));
}

ngOnDestroy(): void {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
}

We want to have an observable stream that we can subscribe to and be able to unsubscribe when the component is destroyed.

d3-visualization.component.html should look like this:

<ng-container *ngIf=”(data$ | async) as data”>
<app-line-chart [data]=”data”> </app-line-chart>
</ng-container>

5. Edit line-chart.component files

 

Our line-chart.component.html file should be pretty simple:

<div class=”card”>
<div class=”card-header”>Example: Bitcoin Price Over Time</div>
<div class=”card-body”>
<figure #chartArea></figure>
</div>
</div>

Also at this point, we can go back to app.component.html and add the reference to our visualization.

<nav class=”navbar navbar-expand-lg navbar-light bg-light”>
<div class=”container”>
<a class=”navbar-brand” href=”#”>Angular D3 Example</a>
</div>
</nav>
<div class=”container mt-3″>
<app-d3-visualization></app-d3-visualization>
</div>

The fun part begins now that we are going to build our visualization. Let’s edit line-chart.component.ts

This component receives as input our formatted data as such.

@Input() data: { date: any; value: number }[];

We will also need to use the ViewChild decorator to be able to select the container and build the chart inside.

@ViewChild(“chartArea”, { static: true }) chartArea: ElementRef<HTMLElement>;

Next, we declare the following properties:

margin = { top: 10, right: 30, bottom: 30, left: 60 };
width: number;
height = 400 – this.margin.top – this.margin.bottom;
svg: Selection<any, any, any, any>;
x: any;
y: any;

In our ngOnInit method we will call 3 functions:

  • setSvgArea
  • setAxes
  • displayLine

So this should look like this:

ngOnInit(): void {
this.setSvgArea();
this.setAxes();
this.displayLine();
}

The setSvgArea function should look like this:

setSvgArea(): void {
this.width =
this.chartArea.nativeElement.offsetWidth –
this.margin.left –
this.margin.right;this.svg = select(this.chartArea.nativeElement)
.append(“svg”)
.attr(“width”, this.width + this.margin.left + this.margin.right)
.attr(“height”, this.height + this.margin.top + this.margin.bottom)
.append(“g”)
.attr(“transform”, `translate(${this.margin.left}, ${this.margin.top})`);
}

Here we are setting the height of our svg to 400 pixels and the width to be equal to the container.

The setAxes function should look like this:

setAxes(): void {
this.x = scaleTime()
.domain(extent(this.data, (d) => d.date))
.range([0, this.width]);
this.y = scaleLinear()
.domain([0, max(this.data, (d) => d.value)])
.range([this.height, 0]);this.svg
.append(“g”)
.attr(“transform”, `translate(0, ${this.height})`)
.call(axisBottom(this.x));
this.svg.append(“g”).call(axisLeft(this.y));
}

In the above function, we are scaling our dimensions to fit the container we established above. Once this is set up we proceed to display.

Finally, we want to draw the line that will represent our data

displayLine(): void {
this.svg
.append(“path”)
.attr(“class”, “line”)
.datum(this.data)
.attr(“fill”, “none”)
.attr(“stroke”, “steelblue”)
.attr(“strokewidth”, 1.5)
.attr(
“d”,
line<{ date: any; value: number }>()
.x((d) => this.x(d.date))
.y((d) => this.y(d.value))
);
}

The result is the following:

 

In Summary

The structure of the project should look like this

  • app
    • D3
      • charts
        • line-chart.component.html
        • line-chart.component.ts
      • d3-visualization.component.ts
      • d3-visualization.service.ts
    • app.component.html
    • app.component.ts

 

  1. app.component.html
<nav class=”navbar navbar-expand-lg navbar-light bg-light”>
<div class=”container”>
<a class=”navbar-brand” href=”#”>Angular D3 Example</a>
</div>
</nav>
<div class=”container mt-3″>
<app-d3-visualization></app-d3-visualization>
</div>

 

  1. app.component.ts
import { Component } from “@angular/core”;

@Component({
selector: “my-app”,
templateUrl: “./app.component.html”
})
export class AppComponent {}

 

  1. d3-visualization.component.ts
import { Component, OnDestroy, OnInit } from “@angular/core”;
import { Observable, Subject } from “rxjs”;
import { takeUntil } from “rxjs/operators”;
import { D3VisualizationService } from “../D3/d3-visualization.service”;@Component({
selector: “app-d3-visualization”,
template: `
<ng-container *ngIf=”(data$ | async) as data”>
<app-line-chart [data]=”data”> </app-line-chart>
</ng-container>
`,
styleUrls: [“./d3-visualization.component.css”]
})
export class D3VisualizationComponent implements OnInit, OnDestroy {
data$: Observable<any>;
private unsubscribe$ = new Subject<void>();constructor(private service: D3VisualizationService) {}

ngOnInit(): void {
this.data$ = this.service.fetchData().pipe(takeUntil(this.unsubscribe$));
}

ngOnDestroy(): void {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
}

 

  1. d3-visualization.service
import { Inject } from “@angular/core”;
import { from, Observable } from “rxjs”;
import { csv, timeParse } from “d3”;
import { map } from “rxjs/operators”;@Inject({})
export class D3VisualizationService {
private dataUrl =
“https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/3_TwoNumOrdered_comma.csv”;fetchData(): Observable<{ date: Date; value: number }[]> {
const parseTime = timeParse(“%Y-%m-%d”);
return from(csv(this.dataUrl)).pipe(
map((res: any[]) =>
res.map(d => ({
…d,
date: parseTime(d.date),
value: +d.value
}))
)
);
}
}

 

  1. line-chart.component.html
<div class=”card”>
<div class=”card-header”>Example: Bitcoin Price Over Time</div>
<div class=”card-body”>
<figure #chartArea></figure>
</div>
</div>

 

  1. line-chart.component.ts
import { Component, ElementRef, Input, OnInit, ViewChild } from “@angular/core”;
import {
Selection,
select,
scaleTime,
scaleLinear,
max,
extent,
axisBottom,
axisLeft,
line,
} from “d3”;@Component({
selector: “app-line-chart”,
templateUrl: “./line-chart.component.html”,
styleUrls: [“./line-chart.component.css”],
})
export class LineChartComponent implements OnInit {
@Input() data: { date: any; value: number }[];
@ViewChild(“chartArea”, { static: true }) chartArea: ElementRef<HTMLElement>;margin = { top: 10, right: 30, bottom: 30, left: 60 };
width: number;
height = 400 – this.margin.top – this.margin.bottom;
svg: Selection<any, any, any, any>;
x: any;
y: any;

ngOnInit(): void {
this.setSvgArea();
this.setAxes();
this.displayLine();
}

setSvgArea(): void {
this.width =
this.chartArea.nativeElement.offsetWidth –
this.margin.left –
this.margin.right;

this.svg = select(this.chartArea.nativeElement)
.append(“svg”)
.attr(“width”, this.width + this.margin.left + this.margin.right)
.attr(“height”, this.height + this.margin.top + this.margin.bottom)
.append(“g”)
.attr(“transform”, `translate(${this.margin.left}, ${this.margin.top})`);
}

setAxes(): void {
this.x = scaleTime()
.domain(extent(this.data, (d) => d.date))
.range([0, this.width]);
this.y = scaleLinear()
.domain([0, max(this.data, (d) => d.value)])
.range([this.height, 0]);

this.svg
.append(“g”)
.attr(“transform”, `translate(0, ${this.height})`)
.call(axisBottom(this.x));
this.svg.append(“g”).call(axisLeft(this.y));
}

displayLine(): void {
this.svg
.append(“path”)
.attr(“class”, “line”)
.datum(this.data)
.attr(“fill”, “none”)
.attr(“stroke”, “steelblue”)
.attr(“strokewidth”, 1.5)
.attr(
“d”,
line<{ date: any; value: number }>()
.x((d) => this.x(d.date))
.y((d) => this.y(d.value))
);
}
}

If you would like to see the finished example you can check it out in Stackblitz. At this point, we have the chart. The next steps will be adding a tooltip and some interactivity.

]]>
https://blogs.perficient.com/2021/04/01/angular-and-data-driven-documents-d3-js-part-1-creating-chart/feed/ 0 321779
That Time I Flunked my JavaScript Technical Interview: Some Tips from Personal Experience https://blogs.perficient.com/2020/09/23/that-time-i-flunked-my-javascript-technical-interview-some-tips-from-personal-experience/ https://blogs.perficient.com/2020/09/23/that-time-i-flunked-my-javascript-technical-interview-some-tips-from-personal-experience/#respond Wed, 23 Sep 2020 20:11:21 +0000 https://blogs.perficient.com/?p=321931

As the title suggests, this article is about those times that I did not land the job that I was applying for because I did not know the answer to a particular question.

It did not mean that I was bad at my job or that I did not know JavaScript. I have been using it almost every day since I became a software engineer. So, what went wrong? In this article, I will tackle some of those problems where I have struggled as well as some common questions asked to candidates applying for JavaScript related positions (Front-end, Back-end, or Full Stack).

Knowledge of Concepts

The way companies get a feel for how experienced a candidate may be is by asking very specific questions about core concepts of the programming language that the open position requires. Below are some concepts that I have been asked about during interviews.

Closure, Timeouts, and Scoping

According to MDN web docs (2020), “A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment)”. In simple terms, this means that variables and properties inside a function are exclusive to that function, but the function can have access to outer properties.

Simple enough, right? But the real challenge comes when the interviewer asks you something like this: in the code below, what would the output be for the following?

for (var i = 0; i < 3; i++) {

setTimeout(function() {

console.log(i);

}, 3000);

}

 

]]>
https://blogs.perficient.com/2020/09/23/that-time-i-flunked-my-javascript-technical-interview-some-tips-from-personal-experience/feed/ 0 321931