Archive

Archive for January, 2020

Water.css

January 11th, 2020 No comments

It’s notable that Water.css was the #1 clicked thing from Louis Lazaris’ Web Tools Weekly in 2019. It’s from a 13-year old developer named Felix!

It’s just a little bit of CSS you apply to class-free semantic HTML to give it nice basic responsive styles — the perfect kind of thing for a Pen when you are just tossing some markup together and want it to look nice.

Direct Link to ArticlePermalink

The post Water.css appeared first on CSS-Tricks.

Categories: Designing, Others Tags:

CSS-Only Carousel

January 10th, 2020 No comments

It’s kind of amazing how far HTML and CSS will take you when building a carousel/slideshow.

  1. Setting some boxes in a horizontal row with flexbox is easy.
  2. Showing only one box at a time with overflow and making it swipable with -webkit-overflow-scrolling is easy.
  3. You can make the “slides” line up nicely with scroll-snap-type.
  4. A couple of #jump-links is all you need to make navigation for it, which you can make all nice and smooth with scroll-behavior.

See the Pen
Real Simple Slider
by Chris Coyier (@chriscoyier)
on CodePen.

Christian Schaefer has taken it a little further with next and previous buttons, plus an auto-play feature that stops playing once interaction starts.

See the Pen
A CSS-only Carousel Slider
by Christian Schaefer (@Schepp)
on CodePen.

About that auto-play thing — it’s a bonafide CSS trick:

  1. First I slowly offset the scroll snap points to the right, making the scroll area follow along due to being snapped to them.
  2. After having scrolled the width of a whole slide, I deactivate the snapping. The scroll area is now untied from the scroll snap points.
  3. Now I let the scroll snap points jump back to their initial positions without them “snap-dragging” the scroll area back with them
  4. Then I re-engage the snapping which now lets the scroll area snap to a different snap point ?

Cool.

JavaScript-powered slideshows (e.g. with Flickty) can be real nice, too. There is just something neat about getting it done with so little code.

See the Pen
Flickity – wrapAround
by Dave DeSandro (@desandro)
on CodePen.

The post CSS-Only Carousel appeared first on CSS-Tricks.

Categories: Designing, Others Tags:

Things you can do with a browser in 2020

January 10th, 2020 No comments

I edit a good amount of technical articles about the web, and there is a tendency for authors to be super broad in their opening sentence, like “What we’re able to do on the web has expanded greatly over the years.”

I tend to remove stuff like that because it usually doesn’t serve the article well, even though I understand the sentiment.

Just look at Luigi De Rosa’s list here. I’d bet a lot of you didn’t know the browser could do all that stuff — push notifications! Native sharing menus! Picture-in-picture!

It’s mostly JavaScript stuff, a little CSS, and notably absent: anything in HTML.

Direct Link to ArticlePermalink

The post Things you can do with a browser in 2020 appeared first on CSS-Tricks.

Categories: Designing, Others Tags:

Everything You Need To Know About Green UX

January 10th, 2020 No comments

Climate change is taking its toll on the environment. The temperature is rising, the Glaciers are melting, sea levels are rising, and the fauna and flora ranges are shifting. According to the Intergovernmental Panel on Climate Change,

“Taken as a whole, the range of published evidence indicates that the net damage costs of climate change are likely to be significant and to increase over time.”

From the very start, all of these effects and many others account for the change have accounted for critical damage induced mainly by human activity in the form of CO2 emissions.

In the midst of it all, the internet is a prominent cause to blame. Sure, the virtual world is affecting the real world on a massive scale; how can a UX designer or developer be involved in the overall impact? Take an ecommerce site for example. As soon as the visitor approaches the website to buy their desired products, their actions initiate a chain of events that contribute to higher emissions.

It’s even reported that if the internet were a country, it would rank among the ones having a high carbon footprint, such as the USA, China, and India. Where the internet connects so many people, it’s also inviting other drastic measures for the evolving carbon footprint.

For most of the global climate change impacts, sustainable practices and resources have been brought in to combat and reduce the apparent damage shortly. Is there a way designers or web design companies worldwide can contribute?

Instead of digging in the solutions first, let’s start by identifying how UX is indirectly contributing to the environmental damage and what you should know about creating green UX projects.

How UX Contributes To Growing CO2 Emissions

Data Centers And E-Waste

Every activity you carry out on a website or any other online platform, you need storage for your projects, files, texts, documents, and other media. You may even contact a company that provides data storage of XYZ GBs. While you may be at ease for having saved your information, the data in cloud storage is negatively impacting the environment. According to Amanda Sopkins from Sustainable UX,

“Notifications replace daily newspaper deliveries, emails replace receipts, and virtual clouds replace boxes of photographs. At first, this seems like a natural way to build a more sustainable world. However, rather than leveraging our power to create minimal solutions for hard problems, we gorge ourselves on this seemingly limitless space.” She later adds, “The buildup of all this material has an impact beyond the carbon that is produced by the servers that stores it. It has an impact on us: we lose track of what matters and what we should save.”

With millions of people interacting on the internet daily, it won’t be wrong to say that the data is massively hurling towards hyper-emission. It’s something we don’t see every day!

Mobiles And Energy Consumption

Each day, a new smartphone is released. The growing number of these devices is serving the energy-hungry populations, becoming somewhat blind to the environment. The process of smartphone production and delivery to the end-consumer greatly impacts the CO2 emissions. For instance, an iPhone requires several hours to be created and assembled and takes up to hundreds of thousands of miles, fuel, and energy to reach the user.

The case gets worse with mobile screens becoming larger. The bigger it gets, the more materials it needs, and the more energy it consumes. How does this account for environmental damage? Every activity done on the phone requires energy and storage. As much data is stored in the data centers, more resources are exploited and greater damage is done.

Ways To Implement Green, Sustainable UX

While we mentioned just two examples of how UX is hurting the environment, we shouldn’t leave behind the measures to scale down the emission pressure. Here are a few ways we can think of.

1. Measure Your Carbon Foot Print

There’s no point in creating something if you can’t measure it. And when you can’t measure it, it will become impossible for you to manage it. When creating a sustainable user experience, one needs to review the existing research on the on-going web emissions and energy consumption. The sole purpose must base on understanding how this information can be used to estimate and cut website pollution.

For this purpose, websitecarbon.com launched a free tool to measure the amount of CO2 a website is emitting. Upon entering the website URL, the tool rolls out the information about how much CO2 your website is emitting, and if it is following a sustainable approach.

2. Make The Experience Accessible For All

Among the practices for a sustainable UX, usability comes as the first and foremost priority, which can be enhanced with factors like readability and navigation. If the content on the website is light, guides the user expertly, and helps them recognize value, the overall experience expands into an accessible dimension.

The website’s cleanliness also matters. Less clutter and speedy loading mean lower carbon emission. If the content is presented with great clarity and directions, it builds a connection between sustainability and user experience.

3. Optimize Performance

Sometimes, websites designed with a user-focused approach have performance issues that have adverse impacts on the user experience. By optimizing the site, you not only reduce data consumption and emissions but also engage the users with satisfaction.

So, what type of content needs optimization? Text, images, and videos make up your website’s content, and optimization of these means improving your website’s performance and data usage. By using clear, concise text, high-quality images and videos, your website cuts down on the page loading time without losing the overall quality.

4. Teach Sustainability Through Practice

Designing for a better environment receives returns when the users interact with the design. Users are generally unaware of how a clean and eco-friendly website looks like, while they might be concerned about the environment in the other way.

At this point, it’s your responsibility to drop hints about a sustainable design along with motivating them to achieve green goals in a better way. However, you need to address what you’re making it, how people will use it, and how it reflects your strategy to create a safer environment. Your intent should be evident in your message throughout your website.

Categories: Others Tags:

Understanding CSS Grid: Grid Lines

January 10th, 2020 No comments
Item is shown in position with the Firefox Grid Inspector highlighting the lines

Understanding CSS Grid: Grid Lines

Understanding CSS Grid: Grid Lines

Rachel Andrew

2020-01-10T11:30:00+00:002020-01-13T11:36:37+00:00

In the first article in this series, I took a look at how to create a grid container and the various properties applied to the parent element that make up your grid. Once you have a grid, you have a set of grid lines. In this article, you will learn how to place items against those lines by adding properties to the direct children of the grid container.

We will cover:

  1. The placement properties grid-column-start, grid-column-end, grid-row-start, grid-row-end and their shorthands grid-column and grid-row.
  2. How to use grid-area to place by line number.
  3. How to place items according to line name.
  4. The difference between the implicit and explicit grid when placing items.
  5. Using the span keyword, with a bit of bonus subgrid.
  6. What to watch out for when mixing auto-placed and placed items.

Basic Concepts Of Line-Based Positioning

To place an item on the grid, we set the line on which it starts, then the line that we want it to end on. Therefore, with a five-column, five-row grid, if I want my item to span the second and third column tracks, and the first, second and third row tracks I would use the following CSS. Remember that we are targetting the line, not the track itself.

.item {
  grid-column-start: 2;
  grid-column-end: 4;
  grid-row-start: 1;
  grid-row-end: 4;
}

This could also be specified as a shorthand, the value before the forward slash is the start line,m the value after is the end line.

.item {
  grid-column: 2 / 4;
  grid-row: 1 / 4;
}

On CodePen you can see the example, and change the lines that the item spans.

See the Pen Grid Lines: placement shorthands by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Grid Lines: placement shorthands by Rachel Andrew (@rachelandrew) on CodePen.

Note that the reason our box background stretches over the entire area is because the initial values of the alignment properties align-self and justify-self are stretch.

If you only need your item to span one track, then you can omit the end line, as the default behavior is that items span one track. We see this when we auto place items as in the last article, each item goes into a cell – spanning one column and one-row track. So to cause an item to span from line 2 to 3 you could write:

.item {
  grid-column: 2 / 3;
}

It would also be perfectly correct to miss off the end line:

.item {
  grid-column: 2;
}

The grid-area Shorthand

You can also place an item using grid-area. We’ll encounter this property again in a future article, however, when used with line numbers it can be used to set all four lines.

.item {
  grid-area: 1 / 2 / 4 / 4;
}

The order of those line numbers is grid-row-start, grid-column-start, grid-row-end, grid-column-end. If working in a horizontal language, written left to right (like English), that’s top, left, bottom, right. You may have realized this is the opposite of how we normally specify shorthands such as margin in CSS – these run top, right, bottom, left.

The reason for this is that grid works in the same way no matter which writing mode or direction you are using, and we’ll cover this in detail below. Therefore, setting both starts then both ends makes more sense than mapping the values to the physical dimensions of the screen. I don’t tend to use this property for line-based placement, as I think the two-value shorthands of grid-column and grid-row are more readable when scanning through a stylesheet.

Lines On The Explicit Grid

I mentioned the explicit versus the implicit grid in my last article. The explicit grid is the grid that you create with the grid-template-columns andgrid-template-rows properties. By defining your column and row tracks, you also define lines between those tracks and at the start and end edges of your grid.

Those lines are numbered. The numbering starts from 1 at the start edge in both the block and inline direction. If you are in a horizontal writing mode, with sentences which begin on the left and run towards the right this means that line 1 in the block direction is at the top of the grid, and line 1 in the inline direction is the left-hand line.

Item is shown in position with the Firefox Grid Inspector highlighting the lines

The item placed on the grid

If you are working in a horizontal RTL language – as you might be if working in Arabic – then line 1 in the block direction is still at the top, but line 1 in the inline direction is on the right.

The item is now placed from the right-hand side of the grid

The same placement with direction: rtl

If you are working in a Vertical Writing Mode, and in the image below I have set writing-mode: vertical-rl, then line 1 will be at the start of the block direction in that writing mode, in this case on the right. Line 1 in the inline direction is at the top.

The entire grid is now rotated 90 degrees

The same placement in writing-mode: vertical-rl

Therefore, grid lines are tied to the writing mode and script direction of the document or component.

The end line of your explicit grid is number -1 and lines count back in from that point, making line -2 the second from the last line. This means that if you want to span an item across all tracks of the explicit grid you can do so with:

.item {
  grid-column: 1 / -1;
}

Lines On The Implicit Grid

If you have created implicit grid tracks then they also count up from 1. In the example below, I have created an explicit grid for columns, however, row tracks have been created in the implicit grid, where I am using grid-auto-rows to size these to 5em.

The item with a class of placed has been placed to span from row line 1 to row line -1. If we were working with an explicit grid for our two rows, then the item should span two rows. Because the row tracks have been created in the implicit grid, line -1 resolved to line 2, and not line 3.

See the Pen Grid Lines: explicit vs. implicit grid by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Grid Lines: explicit vs. implicit grid by Rachel Andrew (@rachelandrew) on CodePen.

There is currently no way to target the last line of the implicit grid, without knowing how many lines you have.

Placing Items Against Named Lines

In the last article I explained that in addition to line numbers, you can optionally name lines on your grid. You name the lines by adding a name or names inside square brackets between your tracks sizes.

.grid {
  display: grid;
  grid-template-columns: [full-start] 1fr [main-start] 2fr 2fr [main-end full-end];
}

Once you have some named lines, you can swap out the line number for a name when placing your items.

.item {
  grid-column: main-start / main-end;
}

See the Pen Grid Lines: naming lines by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Grid Lines: naming lines by Rachel Andrew (@rachelandrew) on CodePen.

If your line has several names, you can pick whichever one you like when placing your item, all of the names will resolve to that same line.

Note: There are some interesting things that happen when you name lines. Take a look at my article “Naming Things In CSS Grid Layout” for more.

What Happens If There Are Multiple Lines With The Same Name?

You get some interesting behavior if you have multiple lines that have the same name. This is a situation that could happen if you name lines within repeat() notation. In the example below I have an 8 column grid, created by repeating 4 times a pattern of 1fr 2fr. I have named the line before the smaller track sm and the larger track lg. This means that I have 4 lines with each name.

In this situation, we can then use the name as an index. So to place an item starting at the second line named sm and stretching to the third line named lg I use grid-column: sm 2 / lg 3. If you use the name without a number that will always resolve to the first line with that name.

See the Pen Grid Lines: naming lines by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Grid Lines: naming lines by Rachel Andrew (@rachelandrew) on CodePen.

Using The span Keyword

There are situations where you know that you want an item to span a certain number of tracks, however, you don’t know exactly where it will sit on the grid. An example would be where you are placing items using auto-placement, but want them to span multiple tracks rather than the default 1. In this case, you can use the span keyword. In the example below, my item starts on line auto, this is the line where auto-placement would put it, and it then spans 3 tracks.

.item {
  grid-column: auto / span 3;
}

See the Pen Grid Lines: span keyword by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Grid Lines: span keyword by Rachel Andrew (@rachelandrew) on CodePen.

This technique will become very useful once we have wide support of the subgrid value for grid-template-columns and grid-template-rows. For example, in a card layout where the cards have a header and main content area in which you want to align with each other, you can cause each card to span 2 rows, while still allowing for the usual auto-placement behavior. The individual cards will use subgrid for their rows (i.e. getting two rows each). You can see this in the below example if you use Firefox, and read my article CSS Grid Level 2: Here Comes Subgrid to learn more about subgrid.

See the Pen Grid Lines: span keyword and subgrid by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Grid Lines: span keyword and subgrid by Rachel Andrew (@rachelandrew) on CodePen.

/

A grid of cards with the Firefox Grid Inspector showing they each sit over two rows of the grid

The example in Firefox using the Grid Inspector

Layering Items With Line-Based Placement

Grid will auto-place items into empty cells on the grid, it won’t stack items into the same cell. However, by using line-based placement you can put items into the same grid cell. In this next example, I have an image that spans two-row tracks, and a caption which is placed in the second track and given a semi-transparent background.

See the Pen Grid Lines: card with layered elements by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Grid Lines: card with layered elements by Rachel Andrew (@rachelandrew) on CodePen.

Items will stack up in the order that they appear in the document source. So in the above example, the caption comes after the image and therefore displays on top of the image. If the caption had come first then it would end up displaying behind the image and we wouldn’t be able to see it. You can control this stacking by using the z-index property. If it was important for the caption to be first in the source, then you can use z-index, with a higher value for the caption than the image. This would force the caption to display on top of the image so that it can be read.

Mixing Line-Based And Auto-Placement

You need to take a little extra care if you are mixing placed items with auto-placed ones. When items are fully auto-placed in grid, they will place themselves sequentially onto the grid, each finding the next available empty space to put themselves into.

See the Pen Grid Lines: auto-placement by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Grid Lines: auto-placement by Rachel Andrew (@rachelandrew) on CodePen.

The default behavior is always to progress forwards, and to leave a gap if an item does not fit on the grid. You can control this behavior by using the property grid-auto-flow with a value of dense. In this case, if there is an item that fits a gap already left in the grid, it will be placed out of source order in order to fill the gap. In the example below using dense packing, item 3 is now placed before item 2.

See the Pen Grid Lines: auto-placement and dense packing by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Grid Lines: auto-placement and dense packing by Rachel Andrew (@rachelandrew) on CodePen.

Note that this behavior can cause problems for users who are tabbing through the document as the visual layout will be out of sync with the source order that they are following.

Auto-placement works slightly differently if you have already placed some items. The placed items will be positioned first, and auto-placement will then look for the first available gap to start placing items. If you have left some whitespace at the top of your layout by way of an empty grid row, then introduce some items which are auto-placed, they will end up in that track.

To demonstrate in this final example I have placed with the line-based positioning properties, items 1 and 2 leaving the first row empty. Later items have moved up to fill the gaps.

See the Pen Grid Lines: auto-placement mixed with placed items by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Grid Lines: auto-placement mixed with placed items by Rachel Andrew (@rachelandrew) on CodePen.

This behavior is worth understanding, as it can mean that items end up in strange places if you introduce some new elements to your layout which haven’t been given a placement on the grid.

Wrapping Up

That is pretty much all you need to know about grid lines. Remember that you always have numbered lines, no matter how else you are using grid you can always place an item from one line number to another. The other methods we will look at in future articles are alternate ways to specify your layout, but are based on the grid created by numbered lines.

(il)
Categories: Others Tags:

Is it better to use ems/rems than px for font-size?

January 9th, 2020 No comments

The answer used to be absolutely yes because, if you used px units, you prevented the text from being resized by the user at all.

But browser zoom is the default method for making everything bigger (including text) these days and it works great even if you use px.

But… Kathleen McMahon really digs into this and finds that it’s still worth setting all your type (both font-size and line-height) in relative units because:

  1. setting type in px prevents browser settings from making font size adjustments (which some people definitely use) and
  2. setting type in relative units maintains greater design fidelity as users use browser zoom (which a lot of people definitely use).

Direct Link to ArticlePermalink

The post Is it better to use ems/rems than px for font-size? appeared first on CSS-Tricks.

Categories: Designing, Others Tags:

Our Learning Partner: Frontend Masters

January 9th, 2020 No comments

I’d like to think there is a lot to learn on CSS-Tricks. But we don’t really offer much by the way of courses. You’re probably reading this because you just generally read this site, and you land on CSS-Tricks otherwise mostly because you are looking for an answer to some front-end question.

Courses are a really great way to learn though. I’ve done many over my years as a developer, particularly when learning something fairly outside my bubble of things I already know. For example, I don’t reach for TypeScript yet because I don’t really know it and am definitely not comfortable with it. You better believe I’ll be taking a course on it when the time comes that I want to use it on a project.

Where will I find that course? Frontend masters, of course. They are clearly the most high-quality in-depth courses on all things front-end development. They’ve got a course on TypeScript, of course.

So I’m so so glad to announce that Frontend Masters is our official learning partner. I like having an official place to send people to when it’s clear their learning style is video courses, like so many people’s is.

One not-so-little aspect I love about Frontend Masters: the courses are taught in a live environment. So it’s not a head talking into a screen, the courses have the natural energy of a live teaching situation, because they are.

How you’ll see our course recommendations here on CSS-Tricks is through some contextual ads next to articles. Say you’re reading Geoff’s recent article about the dense keyword as part of CSS grid layout. Well, that article is about layout, and Frontend Masters has a comprehensive course on modern CSS layout from Jen Kramer. So as you scroll down that post, you’ll see our course recommendation there, as well as at the bottom of the article.

Try it free

Access to all of Frontend Masters is a paid membership. Access to literally everything they have is $39 a month or $390 a year, with special pricing for teams. But, they have some free stuff if you want to dip your toes and see for yourself if it’s any good.

On-the-go

I feel like this is worth mentioning too. Frontend Masters itself is a website you log into to watch the videos and that’s a great experience. But it’s not the only way. They also have great native mobile apps (iOS / Android), if you prefer that experience.

The post Our Learning Partner: Frontend Masters appeared first on CSS-Tricks.

Categories: Designing, Others Tags:

Understanding Async Await

January 9th, 2020 No comments

When writing code for the web, eventually you’ll need to do some process that might take a few moments to complete. JavaScript can’t really multitask, so we’ll need a way to handle those long-running processes.

Async/Await is a way to handle this type of time-based sequencing. It’s especially great for when you need to make some sort of network request and then work with the resulting data. Let’s dig in!

Promise? Promise.

Async/Await is a type of Promise. Promises in JavaScript are objects that can have multiple states (kind of like the real-life ones ??). Promises do this because sometimes what we ask for isn’t available immediately, and we’ll need to be able to detect what state it is in.

Consider someone asks you to promise to do something for them, like help them move. There is the initial state, where they have asked. But you haven’t fulfilled your promise to them until you show up and help them move. If you cancel your plans, you rejected the promise.

Similarly, the three possible states for a promise in JavaScript are:

  • pending: when you first call a promise and it’s unknown what it will return.
  • fulfilled: meaning that the operation completed successfully
  • rejected: the operation failed

Here’s an example of a promise in these states:

Here is the fulfilled state. We store a promise called getSomeTacos, passing in the resolve and reject parameters. We tell the promise it is resolved, and that allows us to then console log two more times.

const getSomeTacos = new Promise((resolve, reject) => {
  console.log("Initial state: Excuse me can I have some tacos");

  resolve();
})
  .then(() => {
    console.log("Order some tacos");
  })
  .then(() => {
    console.log("Here are your tacos");
  })
  .catch(err => {
    console.error("Nope! No tacos for you.");
  });
> Initial state: Excuse me can I have some tacos
> Order some tacos
> Here are your tacos

See the Pen
Promise States
by Sarah Drasner (@sdras)
on CodePen.

If we choose the rejected state, we’ll do the same function but reject it this time. Now what will be printed to the console is the Initial State and the catch error:

const getSomeTacos = new Promise((resolve, reject) => {
  console.log("Initial state: Excuse me can I have some tacos");

  reject();
})
  .then(() => {
    console.log("Order some tacos");
  })
  .then(() => {
    console.log("Here are your tacos");
  })
  .catch(err => {
    console.error("Nope! No tacos for you.");
  });
> Initial state: Excuse me can I have some tacos
> Nope! No tacos for you.

And when we select the pending state, we’ll simply console.log what we stored, getSomeTacos. This will print out a pending state because that’s the state the promise is in when we logged it!

console.log(getSomeTacos)
> Initial state: Excuse me can I have some 🌮s
> Promise {<pending>}
> Order some 🌮s
> Here are your 🌮s

What then?

But here’s a part that was confusing to me at first. To get a value out of a promise, you have to use .then() or something that returns the resolution of your promise. This makes sense if you think about it, because you need to capture what it will eventually be — rather than what it initially is — because it will be in that pending state initially. That’s why we saw it print out Promise {} when we logged the promise above. Nothing had resolved yet at that point in the execution.

Async/Await is really syntactic sugar on top of those promises you just saw. Here’s a small example of how I might use it along with a promise to schedule multiple executions.

async function tacos() {
  return await Promise.resolve("Now and then I get to eat delicious tacos!")
};

tacos().then(console.log)

Or a more in-depth example:

// this is the function we want to schedule. it's a promise.
const addOne = (x) => {
  return new Promise(resolve => {
    setTimeout(() => { 
      console.log(`I added one! Now it's ${x + 1}.`)
      resolve()
    }, 2000);
  })
}

// we will immediately log the first one, 
// then the addOne promise will run, taking 2 seconds
// then the final console.log will fire
async function addAsync() {
  console.log('I have 10')
  await addOne(10)
  console.log(`Now I'm done!`)
}

addAsync()
> I have 10
> I added one! Now it's 11.
> Now I'm done!

See the Pen
Async Example 1
by Sarah Drasner (@sdras)
on CodePen.

One thing (a)waits for another

One common use of Async/Await is to use it to chain multiple asynchronous calls. Here, we’ll fetch some JSON that we’ll use to pass into our next fetch call to figure out what type of thing we want to fetch from the second API. In our case, we want to access some programming jokes, but we first need to find out from a different API what type of quote we want.

The first JSON file looks like this- we want the type of quote to be random:

{
  "type": "random"
}

The second API will return something that looks like this, given that random query parameter we just got:

{
  "_id":"5a933f6f8e7b510004cba4c2",
  "en":"For all its power, the computer is a harsh taskmaster. Its programs must be correct, and what we wish to say must be said accurately in every detail.",
  "author":"Alan Perlis",
  "id":"5a933f6f8e7b510004cba4c2"
}

We call the async function then let it wait to go retrieve the first .json file before it fetches data from the API. Once that happens, we can do something with that response, like add it to our page.

async function getQuote() {
  // get the type of quote from one fetch call, everything else waits for this to finish
  let quoteTypeResponse = await fetch(`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`)
  let quoteType = await quoteTypeResponse.json()

    // use what we got from the first call in the second call to an API, everything else waits for this to finish
  let quoteResponse = await fetch("https://programming-quotes-api.herokuapp.com/quotes/" + quoteType.type)
  let quote = await quoteResponse.json()

  // finish up
  console.log('done')
}

We can even simplify this using template literals and arrow functions:

async function getQuote() {
  // get the type of quote from one fetch call, everything else waits for this to finish
  let quoteType = await fetch(`quotes.json`).then(res => res.json())

    // use what we got from the first call in the second call to an API, everything else waits for this to finish
  let quote = await fetch(`programming-quotes.com/${quoteType.type}`).then(res => res.json())

  // finish up
  console.log('done')
}

getQuote()

Here is an animated explanation of this process.

See the Pen
Animated Description of Async Await
by Sarah Drasner (@sdras)
on CodePen.

Try, Catch, Finally

Eventually we’ll want to add error states to this process. We have handy try, catch, and finally blocks for this.

try {
  // I'll try to execute some code for you
}
catch(error) {
  // I'll handle any errors in that process
} 
finally {
  // I'll fire either way
}

Let’s restructure the code above to use this syntax and catch any errors.

async function getQuote() {
  try {
    // get the type of quote from one fetch call, everything else waits for this to finish
    let quoteType = await fetch(`quotes.json`).then(res => res.json())

      // use what we got from the first call in the second call to an API, everything else waits for this to finish
    let quote = await fetch(`programming-quotes.com/${quoteType.type}`).then(res => res.json())

    // finish up
    console.log('done')
  }

  catch(error) {
    console.warn(`We have an error here: ${error}`)
  }
}

getQuote()

We didn’t use finally here because we don’t always need it. It is a block that will always fire whether it is successful or fails. Consider using finally any time you’re duplicating things in both try and catch. I usually use this for some cleanup. I wrote an article about this, if you’re curious to know more.

You might eventually want more sophisticated error handling, such as a way to cancel an async function. There is, unfortunately, no way to do this natively, but thankfully, Kyle Simpson created a library called CAF that can help.

Not always blocking

Not all code within an async function will pause execution, the await keyword is the key to this. To explore this, let’s go over a pared-down example so you can see what the syntax looks like, as well as what the await is letting us do.

We’ll need the aid of a couple of helpers to illustrate the concept.

First, we’ll create a promise with a setTimeout. This little helper will basically just say, “Hey, wait around for the amount of time we pass in.” It will be called waitBy. This is very similar to the promise we used in the second example, written as a one-liner.

const waitBy = (duration) => new Promise(resolve => setTimeout(resolve, duration))

Second, we’ll make a small animation of a box that will last one second. Unlike our promised setTimeout helper, this little box animation will not pause any execution by default even though it happens over time.

function moveBox() {
  gsap.to('.box', {
    duration: 1,
    x: 300,
    rotation: 360,
    ease: 'back'
  })
}

The main Async function

Now let’s make our main async function. We’ll use the helper to wait three seconds and then log the output to the console.

Next, we’ll run our animation. Note how even though the animation doesn’t delay the final timing of the last waitBy function, even though it lasts for one second. That’s because it wasn’t await-ed.

The final waitBy function will still occur after another three seconds and then logs to the console.

async function mainDemo() {
  // uses the helper above to wait 3 seconds
  await waitBy(3000)
  console.log('I will log after 3 seconds')
  
  // moves the box for a second, but doesn't pause the execution
  moveBox()
  
  // uses the helper to wait for another 3 seconds, and then logs again
  await waitBy(3000)
  console.log('I will log after 6 seconds')
}

mainDemo()
> I will log after 3 seconds
> I will log after 6 seconds

See the Pen
Explain Async Await Visually
by Sarah Drasner (@sdras)
on CodePen.

Further Reading

It’s common for explanations of Async/Await to begin with callbacks, then promises, and use those explanations to frame Async/Await. Since Async/Await is well-supported these days, we didn’t walk through all of these steps. It’s still pretty good background, especially if you need to maintain older codebases. Here are some of my favorite resources out there:

The post Understanding Async Await appeared first on CSS-Tricks.

Categories: Designing, Others Tags:

let vs. const

January 9th, 2020 No comments

There are multiple ways to declare variables in JavaScript. We had var, and while that still works like it always has, it is generally said that let and const are replacements to the point we rarely (if ever) need var anymore. This doodle explanation does a pretty good job, if you need a refresher.

What is up for debate is the general coding style of when you should pick one or the other. There are situations where you have to use let, like when you need to redeclare the variable since const doesn’t let you do that. But does that mean you should use const in every single situation where you don’t?

Dan Abramov covers the “controversy”. It’s a very well articulated point and counterpoint of both sides with literal lists that compare the two.

My favorite is the first point on both.

The argument that prefers const when possible:

One Way to Do It: It is mental overhead to have to choose between let and const every time. A rule like “always use const where it works” lets you stop thinking about it and can be enforced by a linter.

The argument that prefers let when possible:

Loss of Intent: If we force const everywhere it can work, we lose the ability to communicate whether it was important for something to not be reassigned.

All five points on both sides are worth a read.

I love Dan’s conclusion: “I don’t care.” This is something that can be linted and auto-fixed. You can have an opinion if you want, just like tabs vs. spaces, but it’s something that automation handles in the day-to-day.

Direct Link to ArticlePermalink

The post let vs. const appeared first on CSS-Tricks.

Categories: Designing, Others Tags:

How To Create And Deploy Angular Material Application

January 9th, 2020 No comments

How To Create And Deploy Angular Material Application

How To Create And Deploy Angular Material Application

Shubham

2020-01-09T12:30:00+00:002020-01-13T11:36:37+00:00

Angular is one of the popular choices while creating new web applications. Moreover, “Material Design” specs have become a go-to choice for creating minimal and engaging experience today. Thus, any new “Angular” project mostly uses the “Angular Material Design Library” to use the components which follow the material design specifications. From smooth animations to proper interaction feedback, all of this is already available as part of the official material design library for angular.

After the web application is developed, the next step is to deploy it. That is where “Netlify” comes into the picture. With its very easy to use interface, automatic deployment, traffic splitting for A/B testing and various other features, Netlify is surely a great tool.

The article will be a walkthrough of creating an Angular 8 web application using the official Angular Material Design library. We will be creating a QR Code generator web application completely based on Angular while hosted on Netlify.

Files for this tutorial can be found on GitHub and a demo version is deployed here.

Getting Started

  1. Install Angular 8,
  2. Create a GitHub account,
  3. Install Git on your computer,
  4. Create a Netlify account.

Note: I will be using VSCode and Microsoft Windows as the preferred IDE and OS, though the steps would be similar for any other IDE on any other OS.

After the above prerequisites are complete, let’s begin!

Mocks & Planning

Before we begin creating the project, it would be beneficial to plan ahead: What kind of UI would we want in our application? Will there be any reusable pieces? How will the application interact with external services?

First, check the UI mocks.

Homepage (Large preview)

Creating a QR Page (Large preview)

History page (Large preview)

These are the three different pages which will be contained in the application. The homepage will be the starting point of our application. Creating a QR page should deal with the creation of a new QR code. The History page will show all the saved QR codes.

The mockups not only provide an idea of the look and feel of the application, but they also segregate the responsibility of each page.

One observation (from the mocks) is that it seems that the top navigation bar is common across all the pages. Thus, the navigation bar can be created as a reusable component and reused.

Now that we have a fair bit of an idea as to how the application will look and what can be reused, let’s start.

Creating A New Angular Project

Launch VSCode, then open a terminal window in VSCode to generate a new Angular project.

Terminal in VSCode (Large preview)

The terminal will open with a default path as shown in the prompt. You can change to a preferred directory before proceeding; in the case of Windows, I will use the cd command.

Navigating to the preferred path (Large preview)

Moving forward, angular-cli has a command to generate new projects ng new . Just use any fancy project name you like and press enter, e.g. ng new qr.

This will trigger the angular-cli magic; it will provide a few options to configure some aspects of the project, for instance, adding angular routing. Then, based on the selected options, it will generate the whole project skeleton which can be run without any modification.

For this tutorial, enter Yes for routing and select CSS for styling. This will generate a new Angular project:

Creating a new Angular project (Large preview)

We now have got ourselves a fully working Angular project. In order to make sure everything is working properly, we can run the project by entering this command in the terminal: ng serve. Uh oh, but wait, this results in an error. What could have happened?

ng serve error (Large preview)

Don’t worry. Whenever you create a new project using angular-cli, it generates the whole skeleton inside a folder named after the project name specified in the command ng new qr. Here, we will have to change the current working directory to the one just created. In Windows, use the command cd qr to change directory.

Now, try running the project again with the help of ng serve:

Project running (Large preview)

Open a web browser, go to the URL http://localhost:4200 to see the project running. The command ng serve runs the application on port 4200 by default.

TIP: To run it on a different port, we use the command ng serve --port for instance, ng serve --port 3000.

This ensures that our basic Angular project is up and running. Let’s move on.

We need to add the project folder to VSCode. Go to the “File” menu and select “Open Folder” and select the project folder. The project folder will now be shown in the Explorer view on the left.

Adding Angular Material Library

To install the Angular material library, use the following command in the terminal window: ng add @angular/material. This will (again) ask some questions such as which theme you want, whether you want default animations, whether touch support is required, among others. We will just select the default Indigo/Pink theme, Yes to adding HammerJS library and browser animations.

Adding Angular material (Large preview)

The above command also configures the whole project to enable support for the material components.

  1. It adds project dependencies to package.json,
  2. It adds the Roboto font to the index.html file,
  3. It adds the Material Design icon font to your index.html,
  4. It also adds a few global CSS styles to:
    • Remove margins from the body,
    • Set height: 100% into the HTML and body,
    • Set Roboto as the default application font.

Just to be sure that everything is fine you can run the project again at this point, though you will not notice anything new.

Adding Home Page

Our project skeleton is now ready. Let’s start by adding the homepage.

(Large preview)

We want to keep our homepage simple, just like the above picture. This home page uses a few angular material components. Let’s dissect.

  1. The top bar is a simple HTML nav element which contains material style button, mat-button, with an image and a text as its child. The bar color is the same as the primary color which was selected while adding Angular material library;
  2. A centered image;
  3. Another, mat-button, with just a text as its child. This button will allow users to navigate to the history page;
  4. A count badge, matBadge, attached to the above button, showing the number of QR codes saved by the user;
  5. A floating action button, mat-fab, at the bottom right corner having the accent color from the selected theme.

Digressing a little, let’s add other required components and services first.

Adding Header

As planned previously, the navigation bar should be reused, let’s create it as a separate angular component. Open terminal in VSCode and type ng g c header (short for ng generate component header) and press Enter. This will create a new folder named “header” which will contain four files:

  • header.component.css: used to provide styling for this component;
  • header.component.html: for adding HTML elements;
  • header.component.spec.ts: for writing test cases;
  • header.component.ts: to add the Typescript-based logic.

Header component (Large preview)

To make the header look like as it was in the mocks, add the below HTML in header.component.html:

<nav class="navbar" [class.mat-elevation-z8]=true>
   <div>
       <button *ngIf="showBackButton" aria-hidden=false mat-icon-button routerLink="/">
           <mat-icon style="color: white;">
               <i class="material-icons md-32">arrow_back</i>
           </mat-icon>
       </button>
       <span style="padding-left: 8px; color: white;">{{currentTitle}}</span>
   </div>
   <button *ngIf="!showBackButton" aria-hidden=false mat-button class="button">
       <img src="../../assets/qr-icon-white.png" style="width: 40px;">
       <span style="padding-left: 8px;">QR Generator</span>
   </button>
   <button *ngIf="showHistoryNav" aria-hidden=false mat-button class="button" routerLink="/history">
       <span style="padding-left: 8px;">History</span>
   </button>
</nav>

TIP: To add elevation for any material component use [class.mat-elevation-z8]=true, the elevation value can be changed by changing z value, in this case it is z8. For instance, to change the elevation to 16, use [class.mat-elevation-z16]=true.

In the above HTML snippet, there are two Angular material elements being used: mat-icon and mat-button/mat-icon-button. Their usage is very simple; first, we need to add those two as modules in our app.module.ts as shown below:

Module import for mat-icon and mat-button (Large preview)

This will allow us to use these two Angular material elements anywhere in any component.

For adding material buttons, the following HTML snippet is used:

<button mat-button>
Material Button
</button>

There are different types of material button elements available in the Angular material library such as mat-raised-button, mat-flat-button, mat-fab and others; just replace the mat-button in the above code snippet with any other type.

Types of material buttons (Large preview)

The other element is mat-icon which is used to show icons available in the material icon library. When the Angular material library was added in the beginning, then a reference to the material icon library was added as well, which enabled us to use icons from the vast array of icons.

The usage is as simple as:

<mat-icon style="color: white;">
<i class="material-icons md-32">arrow_back</i>
</mat-icon>

The nested tag can be used to change the icon size (here it’s md-32) which will make the icon size 32px in height and width. This value can be md-24, md-48, and so on. The value of the nested tag is the name of the icon. (The name can be found here for any other icon.)

Accessibility

Whenever icons or images are used, it is imperative that they provide sufficient information for accessibility purposes or for a screen-reader user. ARIA (Accessible Rich Internet Applications) defines a way to make web content and web applications more accessible to people with disabilities.

One point to note is that the HTML elements which do have their native semantics (e.g. nav) do not need ARIA attributes; the screenreader would already know that nav is a navigation element and read it as such.

The ARIA specs is split into three categories: roles, states and properties. Let’s say that a div is used to create a progress bar in the HTML code. It does not have any native semantics; ARIA role can describe this widget as a progress bar, ARIA property can denote its characteristic such as it can be dragged. ARIA state will describe its current state such as the current value of the progress bar. See the snippet below:

<div id="percent-loaded" role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100"> </div>

Similarly, a very commonly used aria attribute: aria-hidden=true/false is used. The value true makes that element invisible to screen-readers.

Since most of the UI elements used in this application have native semantic meaning, the only ARIA attributes used are to specify ARIA visibility states.For detailed information, refer to this.

The header.component.html does contain some logic to hide and show back button depending on the current page. Moreover, the Home button also contains an image/logo which should be added to the /assets folder. Download the image from here and save it in the /assets folder.

For styling of the navigation bar, add the below css in header.component.css:

.navbar {
   position: fixed;
   top: 0;
   left: 0;
   right: 0;
   z-index: 2;
   background: #3f51b5;
   display: flex;
   flex-wrap: wrap;
   align-items: center;
   padding: 12px 16px;
}
.button {
   color: white;
   margin: 0px 10px;
}

As we want to keep the header component reusable across other components, thus to decide what should be shown, we will require those as parameters from other components. This requires usage of @Input() decorator which will bind to the variables we used in header.component.html.

Add these lines in the header.component.ts file:

// Add these three lines above the constructor entry.
 @Input() showBackButton: boolean;
 @Input() currentTitle: string;
 @Input() showHistoryNav: boolean;

 constructor() { }

The above three bindings will be passed as a parameter from other components which the header component will be using. Its usage will be more clear once we move forward.

Moving on, we need to create a homepage that can be represented by an Angular component. So let’s start by creating another component; type ng g c home in the terminal to auto-generate the home component. As previously, a new folder named “home” will be created containing four different files. Before proceeding to modify those files, let’s add some routing information to angular routing module.

Adding Routing

Angular provides a way to map URL to a specific component. Whenever some navigation happens, the Angular framework monitors the URL and based on the information present in the app-routing.module.ts file; it initializes the mapped component. This way different components are does not need to shoulder the responsibility of initializing other components. In our case, the application has three pages navigable by clicking on different buttons. We achieve this by leveraging the routing support provided by the Angular framework.

The home component should be the starting point of the application. Let’s add this information to the app-routing.module.ts file.

Routing Home Component (Large preview)

The path property is set as an empty string; this enables us to map the application URL to the homepage component, something like google.com which shows the Google homepage.

TIP: Path value never starts with a/”, but instead uses an empty string even though path can be like search/coffee.

Moving back to the homepage component, replace the content of home.component.html with this:

<app-header [showBackButton]="false" [currentTitle]=""></app-header>
<app-profile></app-profile>

<!-- FAB Fixed -->
<button mat-fab class="fab-bottom-right" routerLink="/create">
   <mat-icon>
       <i class="material-icons md-48">add</i>
   </mat-icon>
</button>

There are three parts to the home component:

  1. The reusable header component ,
  2. Profile component ,
  3. The floating action button at the bottom right.

The above HTML snippet shows how the reusable header component is used in other components; we just use the component selector and pass in the required parameters.

Profile component is created to be used as the body for the homepage — we will create it soon.

The floating action button with the + icon is a kind of Angular material button of type mat-fab on the bottom right of the screen. It has the routerLink attribute directive which uses the route information provided in the app-routing.module.ts for navigation. In this case, the button has the route value as /create which will be mapped to create component.

To make the create button float on bottom right, add the below CSS code in home.component.css:

.fab-bottom-right {
   position: fixed;
   left: auto;
   bottom: 5%;
   right: 10%;
}

Since profile component is supposed to manage home page body, we will leave home.component.ts intact.

Adding Profile Component

Open terminal, type ng g c profile and press enter to generate profile component. As planned earlier, this component will handle the main body of the home page. Open profile.component.html and replace its content with this:

<div class="center profile-child">
   <img class="avatar" src="../../assets/avatar.png">
   <div class="profile-actions">
       <button mat-raised-button matBadge="{{historyCount}}"    matBadgeOverlap="true" matBadgeSize="medium" matBadgeColor="accent"
           color="primary" routerLink="/history">
           <span>History</span>
       </button>
   </div>
</div>

The above HTML snippet shows how to use the matBadge element of the material library. To be able to use it here, we need to follow the usual drill of adding MatBadgeModule to app.module.ts file. Badges are small pictorial status descriptor for UI elements such as buttons or icons or texts. In this case, it is used with a button to show count of QR saved by the user. Angular material library badge has various other properties such as setting the position of the badge with matBadgePosition, matBadgeSize to specify size, and matBadgeColor to set the badge color.

One more image asset needs to be added to the assets folder: Download. Save the same to the /assets folder of the project.

Open profile.component.css and add this:

.center {
   top: 50%;
   left: 50%;
   position: absolute;
   transform: translate(-50%, -50%);
}


.profile-child {
   display: flex;
   flex-direction: column;
   align-items: center;
}


.profile-actions {
   padding-top: 20px;
}


.avatar {
   border-radius: 50%;
   width: 180px;
   height: 180px;
}

The above CSS will achieve the UI as planned.

Moving on, we need some kind of logic to update the history count value as it will reflect in the matBadge used earlier. Open profile.component.ts and add the following snippet appropriately:

export class ProfileComponent implements OnInit {

 historyCount = 0;
 constructor(private storageUtilService: StorageutilService) { }

 ngOnInit() {
   this.updateHistoryCount();
 }

 updateHistoryCount() {
   this.historyCount = this.storageUtilService.getHistoryCount();
 }
}

We have added StorageutilService but we have not created such a service untill now. Ignoring the error, we have completed our profile component which also finishes off our homepage component. We will revisit this profile component after creating our storage utility service. Okay, then let’s do so.

Local Storage

HTML5 provides web storage feature which can be used to store data locally. This provides much more storage compared to cookies — at least 5MB vs 4KB. There are two types of web storage with different scope and lifetime: Local and Session. The former can store data permanently while the latter is temporary and for a single session. The decision to select the type can be based on the use case, in our scenario we want to save across sessions, so we will go with Local storage.

Each piece of data is stored in a key/value pair. We will use the text for which the QR is generated as the key and the QR image encoded as a base64 string as the value. Create an entity folder, inside the folder create a new qr-object.ts file and add the code snippet as shown:

QR entity model (Large preview)

The content of the class:

export class QR {

   text:           string;
   imageBase64:    string;

   constructor(text: string, imageBase64: string) {
       this.imageBase64 = imageBase64;
       this.text = text;
   }

}

Whenever the user saves the generated QR, we will create an object of the above class and save that object using the storage utility service.

Create a new service folder, we will be creating many services, it’s better to group them together.

Services Folder (Large preview)

Change the current working directory to services, cd services, to create a new service use ng g s . This is a shorthand to ng generate service , type ng g s storageutil and press enter

This will create two files:

  • storageutil.service.ts
  • storageutil.service.spec.ts

The latter is for writing unit tests. Open storageutil.service.ts and add this:

private historyCount: number;
 constructor() { }

 saveHistory(key : string, item :string) {
   localStorage.setItem(key, item)
   this.historyCount = this.historyCount + 1;
 }

 readHistory(key : string) : string {
   return localStorage.getItem(key)
 }

 readAllHistory() : Array<QR> {
   const qrList = new Array<QR>();

   for (let i = 0; i 

Import the qr-object class to correct any errors. To use the local storage feature, there is no need to import anything new just use the keyword localStorage to save or get value based on a key.

Now open the profile.component.ts file again and import the StorageutilService class to properly finish off the profile component.

Running the project, we can see the homepage is up as planned.

Adding Create QR Page

We have our homepage ready, though the create/add button does not do anything. Worry not, the actual logic was already written. We used a routerLink directive to change the base path of the URL to /create but there was no mapping added to the app-routing.module.ts file.

Let’s create a component which will deal with the creation of new QR codes, type ng g c create-qr and press enter to generate a new component.

Open the app-routing.module.ts file and add the below entry to the routes array:

{ path: 'create', component: CreateQrComponent },

This will map the CreateQRComponent with the URL /create.

Open create-qr.components.html and replace the contents with this:

<app-header [showBackButton]="showBackButton" [currentTitle]="title" [showHistoryNav]="showHistoryNav"></app-header>


<mat-card class="qrCard" [class.mat-elevation-z12]=true>
   <div class="qrContent">

       <!--Close button section-->
       <div class="closeBtn">
           <button mat-icon-button color="accent" routerLink="/" matTooltip="Close">
               <mat-icon>
                   <i class="material-icons md-48">close</i>
               </mat-icon>
           </button>
       </div>

       <!--QR code image section-->
       <div class="qrImgDiv">
           <img *ngIf="!showProgressSpinner" style="padding: 5px 5px;" src={{qrCodeImage}} width="200px" height="200px">
           <mat-spinner *ngIf="showProgressSpinner"></mat-spinner>
           <div class="actionButtons" *ngIf="!showProgressSpinner">
               <button mat-icon-button color="accent" matTooltip="Share this QR" style="margin: 0 5px;">
                   <mat-icon>
                       <i class="material-icons md-48">share</i>
                   </mat-icon>
               </button>
               <button mat-icon-button color="accent" (click)="saveQR()" matTooltip="Save this QR" style="margin: 0 5px;">
                   <mat-icon>
                       <i class="material-icons md-48">save</i>
                   </mat-icon>
               </button>
           </div>
       </div>

       <!--Textarea to write any text or link-->
       <div class="qrTextAreaDiv">
           <mat-form-field style="width: 80%;">
               <textarea matInput [(ngModel)]="qrText" cdkTextareaAutosize cdkAutosizeMinRows="4" cdkAutosizeMaxRows="4"
                   placeholder="Enter a website link or any text..."></textarea>
           </mat-form-field>
       </div>

       <!--Create Button-->
       <div class="createBtnDiv">
           <button class="createBtn" mat-raised-button color="accent" matTooltip="Create new QR code" matTooltipPosition="above"
               (click)="createQrCode()">Create</button>
       </div>
   </div>
</mat-card>

The above snippet uses many of the Angular material library elements. As planned, it has one header component reference wherein the required parameters are passed. Next up is the main body of the create page; it consists of one Angular material card or mat-card centered and elevated up to 12px as [class.mat-elevation-z12]=true is used.

The material card is just another kind of container that can be used as any other div tag. Although the material library provides some properties to lay out well-defined information in a mat-card such as image placement, title, subtitle, description and action as can be seen below.

Card example (Large preview)

In the above HTML snippet, we have used mat-card just as any other container. Another material library element used is matTooltip; it is just another tooltip with ease of use, displayed when the user hovers over or longpresses an element. Just use the snippet below to show tooltip:

matTooltip="Any text you want to show"

It can be used with icon buttons or any other UI element to convey extra information. In application context, it is displaying information about the close icon button. To change the placement of the tooltip, matTooltipPosition is used:

matTooltip="Any text you want to show" matTooltipPosition="above"

Besides matTooltip, mat-spinner is used to show loading progress. When the user clicks on the “Create” button, a network call is made. This is when the progress spinner is shown. When the network call returns with result, we just hide the spinner. It can be used simply like this:

<mat-spinner *ngIf="showProgressSpinner"></mat-spinner>

showProgressSpinner is a Boolean variable which is used to show/hide the progress spinner. The library also provides some other parameters like [color]='accent' to change color, [mode]='indeterminate' to change the progress spinner type. An indeterminate progress spinner will not show the progress of the task while a determinate one can have different values to reflect task progress. Here, an indeterminate spinner is used as we do not know how long the network call will take.

The material library provides a variant of textarea conforming to the material guideline but it can only be used as a descendent of mat-form-field. Usage of material textarea is just as simple as the default HTML one, like below:

<mat-form-field>
   <textarea matInput placeholder="Hint text"></textarea>
</mat-form-field>

matInput is a directive which allows native input tag to work with mat-form-field. The placeholder property allows adding any hint text for the user.

TIP: Use the cdkTextareaAutosize textarea property to make it auto-resizable. Use cdkAutosizeMinRows and cdkAutosizeMaxRows to set rows and columns and all three together to make textarea auto-resize till it reaches the max rows and columns limit set.

To use all these material library elements, we need to add them in the app.module.ts file.

Creating QR module imports (Large preview)

There is a placeholder image being used in the HTML. Download and save it to the /assets folder.

The above HTML also requires CSS styling, so open the create-qr.component.ts file and add the following:

.qrCard {
   display: flex;
   flex-direction: column;
   align-items: center;
   position: absolute;
   top: 50%;
   left: 50%;
   transform: translate(-50%, -50%);
   width: 20%;
   height: 65%;
   padding: 50px 20px;
}

.qrContent {
   display: flex;
   flex-direction: column;
   align-items: center;
   width: 100%;
}

.qrTextAreaDiv {
   width: 100%;
   display: flex;
   flex-direction: row;
   justify-content: center;
   padding: 0px 0px;
   position: absolute;
   bottom: 10%;
}

.createBtn {
   left: 50%;
   transform: translate(-50%, 0px);
   width: 80%;
}

.createBtnDiv {
   position: absolute;
   bottom: 5%;
   width: 100%;
}


.closeBtn {
   display: flex;
   flex-direction: row-reverse;
   align-items: flex-end;
   width: 100%;
   margin-bottom: 20px;
}

.closeBtnFont {
   font-size: 32px;
   color: rgba(0,0,0,0.75);
}

.qrImgDiv {
   top: 20%;
   position: absolute;
   display: flex;
   flex-direction: column;
   align-items: center;
   justify-content: center;
   width: 100%;
}
.actionButtons {
   display: flex;
   flex-direction: row;
   padding-top: 20px;
}

Let’s wire up the UI with logic. Open the create-qr.component.ts file and add the below code, leaving those lines which are already present:

export class CreateQrComponent implements OnInit {

 qrCodeImage = '../../../assets/download.png';
 showProgressSpinner = false;
 qrText: string;
 currentQR;
 showBackButton = true;
 title = 'Generate New QR Code';
 showHistoryNav = true;

 constructor(private snackBar: MatSnackBar,
     private restutil: RestutilService,
     private storageService: StorageutilService) { }

 ngOnInit() {
 }

 createQrCode() {
   //Check if any value is given for the qr code text
   if (!!this.qrText) {
     //Make the http call to load qr code
     this.loadQRCodeImage(this.qrText);
   } else {
     //Show snackbar
     this.showSnackbar('Enter some text first')
   }
 }

 public loadQRCodeImage(text: string) {
   // Show progress spinner as the request is being made
   this.showProgressSpinner = true;
   // Trigger the API call
   this.restutil.getQRCode(text).subscribe(image =>{
     // Received the result - as an image blob - require parsing
     this.createImageBlob(image);
   }, error => {
     console.log('Cannot fetch QR code from the url', error)
     // Hide the spinner - show a proper error message
     this.showProgressSpinner = false;
   });
 }

 private createImageBlob(image: Blob) {
   // Create a file reader to read the image blob
   const reader = new FileReader();
   // Add event listener for "load" - invoked once the blob reading is complete
   reader.addEventListener('load', () => {
     this.qrCodeImage = reader.result.toString();
     //Hide the progress spinner
     this.showProgressSpinner = false;
     this.currentQR = reader.result.toString();
   }, false);
   // Read image blob if it is not null or undefined
   if (image) {
     reader.readAsDataURL(image);
   }
 }

 saveQR() {
   if (!!this.qrText) {
     this.storageService.saveHistory(this.qrText, this.currentQR);
     this.showSnackbar('QR saved')
   } else {
     //Show snackbar
     this.showSnackbar('Enter some text first')
   }

 }

 showSnackbar(msg: string) {
   //Show snackbar
   this.snackBar.open(msg, '', {
     duration: 2000,
   });
 }
}

To provide users contextual information, we also use MatSnackBar from the material design library. This shows up as a popup from below the screen and stays for a few seconds before disappearing. This is not an element but rather a service that can be invoked from the Typescript code.

The above snippet with the method name showSnackbar shows how to open up a snackbar, but before it can be used, we need to add the MatSnackBar entry in the app.module.ts file just like we did for other material library elements.

TIP: In recent Angular material library versions, there is no straightforward way to change the snackbar styling. Instead, one has to make two additions to the code.

First, use the below CSS to alter background and foreground colors:

::ng-deep snack-bar-container.snackbarColor {
   background-color: rgba(63, 81, 181, 1);
}
::ng-deep .snackbarColor .mat-simple-snackbar {
   color: white;
 }

Second, use a property called panelClass to set the style to the above CSS class:

this.snackBar.open(msg, '', {
     duration: 2000,
     panelClass: ['snackbarColor']
   });

The above two combinations will allow custom styling to the material design library snackbar component.

This completes the steps on how to create a QR page, but there is one piece still missing. Checking the create-qr.component.ts file, it will show an error regarding the missing piece. The missing piece to this puzzle is RestutilService which is responsible for fetching the QR code image from the third-party API.

In the terminal, change the current directory to services by typing in ng g s restutil and pressing Enter. This will create the RestUtilService files. Open the restutil.service.ts file and add this snippet:

private edgeSize = '300';
 private BASE_URL = 'https://api.qrserver.com/v1/create-qr-code/?data={data}!&size={edge}x{edge}';

 constructor(private httpClient: HttpClient) { }

 public getQRCode(text: string): Observable {
   // Create the url with the provided data and other options
   let url = this.BASE_URL;
   url = url.replace("{data}", text).replace(/{edge}/g, this.edgeSize);
   // Make the http api call to the url
   return this.httpClient.get(url, {
     responseType: 'blob'
   });
 }

The above service fetches the QR image from the third-party API and since the response is not of JSON type, but an image, so we specify the responseType as 'blob' in the above snippet.

Angular provides HttpClient class to communicate with any HTTP supporting server. It provides many features like filtering the request before it is fired, getting back the response, enabling the processing of the response via callbacks and others. To use the same, add an entry for the HttpClientModule in app.module.ts file.

Finally, import this service into the create-qr.component.ts file to complete creating the QR code.

But wait! There is a problem with the above create QR logic. If the user uses the same text to generate the QR again and again, it will result in a network call. One way to redress this is caching the request based, thus serving the response from the cache if the request text is same.

Caching Request

Angular provides a simplified way of making HTTP calls, HttpClient, along with HttpInterceptors to inspect and transform HTTP requests or responses to and from servers. It can be used for authentication or caching and many such things, multiple interceptors can be added and chained for further processing. In this case, we are intercepting requests and serving the response from the cache if the QR text is same.

Create an interceptor folder, then create a file cache-interceptor.ts:

Cache interceptor (Large preview)

Add the below code snippet to the file:

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { tap } from 'rxjs/operators';
import { of, Observable } from 'rxjs';

@Injectable({
 providedIn: 'root'
})
export class RequestCachingService implements HttpInterceptor {
 private cacheMap = new Map<string, HttpResponse<any>>();

 constructor() { }

 intercept(req: HttpRequest, next: HttpHandler): Observable<HttpEvent<any>> {
   const cachedResponse = this.cacheMap.get(req.urlWithParams);

   if (cachedResponse) {
     return of(cachedResponse);
   }

   return next.handle(req).pipe(tap(event => {
     if (event instanceof HttpResponse) {
       this.cacheMap.set(req.urlWithParams, event);
     }
   }))

 }
}

In the above code snippet, we have a map with the key being the request URL, and the response as the value. We check if the current URL is present in the map; if it is, then return the response (the rest is handled automatically). If the URL is not in the map, we add it.

We are not done yet. An entry to the app.module.ts is required for its proper functioning. Add the below snippet:

import { HttpClientModule, HTTP_INTERCEPTORS  } from '@angular/common/http';
import { CacheInterceptor } from './interceptor/cache-interceptor';


providers: [
   { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true }
 ],

This adds the caching feature to our application. Let’s move on to the third page, the History page.

Adding The History Page

All the saved QR codes will be visible here. To create another component, open terminal type ng g c history and press Enter.

Open history.component.css and add the below code:

.main-content {
   padding: 5% 10%;
}
.truncate {
   width: 90%;
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
}
.center-img {
   position: absolute;
   top: 50%;
   left: 50%;
   transform: translate(-50%, -50%);
   display: flex;
   flex-direction: column;
   align-items: center;
}

Open history.component.html and replace the content with this:

<app-header [showBackButton]="showBackButton" [currentTitle]="title" [showHistoryNav]="showHistoryNav"></app-header>

<div class="main-content">
   <mat-grid-list cols="4" rowHeight="500px" *ngIf="historyList.length > 0">
       <mat-grid-tile *ngFor="let qr of historyList">
           <mat-card>
               <img mat-card-image style="margin-top: 5px;" src="{{qr.imageBase64}}">
               <mat-card-content>
                   <div class="truncate">
                       {{qr.text}}
                   </div>
               </mat-card-content>
               <mat-card-actions>
                       <button mat-button (click)="share(qr.text)">SHARE</button>
                       <button mat-button color="accent" (click)="delete(qr.text)">DELETE</button>
               </mat-card-actions>
           </mat-card>
       </mat-grid-tile>
   </mat-grid-list>
   <div class="center-img" *ngIf="historyList.length == 0">
       <img src="../../assets/no-see.png" width="256" height="256">
       <span style="margin-top: 20px;">Nothing to see here</span>
   </div>
</div>

As usual, we have the header component at the top. Then, the rest of the body is a grid list that will show all the saved QR codes as individual mat-card. For the grid view, we are using mat-grid-list from the Angular material library. As per the drill, before we can use it, we have to first add it to the app.module.ts file.

Mat grid list acts as a container with multiple tile children called mat-grid-tile. In the above HTML snippet, each tile is created using mat-card using some of its properties for generic placement of other UI elements. We can provide the number of columns and rowHeight, which is used to calculate width automatically. In the above snippet, we are providing both the number of columns and the rowHeight value.

We are using a placeholder image when the history is empty, download it and add to the assets folder.

To implement the logic for populating all this information, open the history.component.ts file and add the below snippet into the HistoryComponent class:

showBackButton = true;
 title = 'History';
 showHistoryNav = false;
 historyList;

 constructor(private storageService: StorageutilService,
 private snackbar: MatSnackBar ) { }

 ngOnInit() {
   this.populateHistory();
 }

 private populateHistory() {
   this.historyList = this.storageService.readAllHistory();
 }

 delete(text: string) {
   this.storageService.deleteHistory(text);
   this.populateHistory();
 }

 share(text: string) {
   this.snackbar.open(text, '', {duration: 2000,})
 }

The above logic just fetches all the saved QR and populates the page with it. Users can delete the saved QR which will delete the entry from the local storage.

So this finishes off our history component… or does it? We still need to add the route mapping for this component. Open app-routing.module.ts and add a mapping for the history page as well:

{ path: 'history', component: HistoryComponent },

The whole route array should look like this by now:

const routes: Routes = [
 { path: '', component: HomeComponent },
 { path: 'create', component: CreateQrComponent },
 { path: 'history', component: HistoryComponent },
];

Now is a good time to run the application to check the complete flow, so open terminal and type ng serve and press Enter. Then, go to localhost:4200 to verify the working of the application.

Add To GitHub

Before proceeding to the deployment step, it would be good to add the project to a GitHub repository.

  1. Open GitHub.
  2. Create a new repository.
  3. GitHub new repository (Large preview)
  4. In VS Code, use the terminal and follow the first set of commands mentioned in the quick start guide to push all the project files.
  5. Adding a project in GitHub (Large preview)

Just refresh the page to check if all the files are visible. From this point on, any git changes (such as commit, pull/push) will be reflected in this newly created repository.

Netlify And Deployment

Our application runs in our local machine, but to enable others to access it, we should deploy it on a cloud platform and register it to a domain name. This is where Netlify comes into play. It provides continuous deployment services, integration with GitHub, and many more features to benefit from. Right now, we want to enable global access to our application. Let’s get started.

  1. Sign-up on Netlify.
  2. From the dashboard, click on the New site from Git button.
  3. Netlify new site (Large preview)
  4. Click on GitHub in the next screen.
  5. Netlify select git provider (Large preview)
  6. Authorize Netlify to be able to access your GitHub repositories.
  7. Netlify GitHub authorization (Large preview)
  8. Search for and select the newly created qr repository.
  9. Netlify GitHub repository selection (Large preview)
  10. Netlify, in the next step, allows us to choose the GitHub repository branch for deployments. Normally one uses the master branch but one can also have a separate release branch which contains only release related and stable features.
  11. Netlify build and deploy (Large preview)

Since this is an Angular web application, add ng build --prod as the build command. Published directories will be dist/qr as mentioned in the angular.json file.

Angular build path (Large preview)

Now click on the Deploy site button which will trigger a project build with the command ng build --prod and will output the file to dist/qr.

Since we provided the path information to Netlify, it will automatically pick up the correct files for servicing the web application. Netlify adds a random domain to our application by default.

Netlify site deployed (Large preview)

You can now click on the link provided in the above page in order to access the application from anywhere. Finally, the application has been deployed.

Custom Domain

In the above image, the URL for our application is shown while the sub-domain is randomly generated. Let’s change that.

Click on the Domain settings button then in the Custom Domains section click on the 3-dot menu and select Edit site name.

Custom domain (Large preview)

This will open a popup wherein a new site name can be entered; this name should be unique across the Netlify domain. Enter any site name that is available and click Save.

Site name (Large preview)

Now the link to our application will be updated with the new site name.

Split Testing

Another cool feature offered by Netlify is split testing. It enables traffic splitting so that different sets of users will interact with different application deployments. We can have new features added to a different branch and split the traffic to this branch deployment, analyze traffic and then merge the feature branch with the main deployment branch. Let’s configure it.

The prerequisite to enabling split testing is a GitHub repository with at least two branches. Head on over to the app repository in GitHub that was created earlier, and create a new branch a.

Create new branch (Large preview)

The repository will now have a master branch and a branch. Netlify needs to be configured to do branch deployments, so open the Netlify dashboard and click on Settings. On the left side, click on Build & Deploy, then Continuous Deployment, then on the right side in the Deploy contexts section, click on Edit settings.

Branch deployments (Large preview)

In the Branch deploys sub-section, select the option “Let me add individual branches”, and enter the branch names and save it.

Deploying brances is another useful feature provided by Netlify; we can select which GitHub repository branches to deploy, and we can also enable previews for every pull request to the master branch before merging. This is a neat feature enabling developers to actually test their changes out live before adding their code changes to the main deployment branch.

Now, click on Split Testing tab option at the top of the page. The split testing configurations will be presented here.

Split testing (Large preview)

We can select the branch (other than the production branch) — in this case a. We can also play around with the settings of splitting traffic. Based on the traffic percentage each branch has been allotted, Netlify will re-route some users to the application deployed using the a branch and others to the master branch. After configuring, click on the Start test button to enable traffic splitting.

TIP: Netlify may not recognize that the connected GitHub repository has more than one branch and may give this error:

Split testing error (Large preview)

To resolve this, just reconnect to the repository from the Build & Deploy options.

Netlify provides a lot of other features as well. We just went through some of its useful features to demonstrate the ease of configuring different aspects of Netlify.

This brings us to the end of our journey. We have successfully created an Angular Material design based on a web application and deployed it on Netlify.

Conclusion

Angular is a great and popular framework for web application development. With the official Angular material design library, it is much easier to create applications which adhere to the material design specs for a very natural interaction with the users. Moreover, the application developed with a great framework should use a great platform for deployment, and Netlify is just that. With constant evolution, great support and with a plethora of features, it surely is a great platform to bring web applications or static sites to the masses. Hopefully, this article will provide help in getting started with a new Angular project from just a thought to deployment.

Further Reading

(dm, yk, il)
Categories: Others Tags: