Learning Typescript with Web Components: Part 3

Part 3: Working with JSON ObjectsSo far in this series, we’ve looked at data binding, storing data in objects, and mapping over it with arrays. In this episode, we’ll look at how to slice-and-dice the data in Typescript objects and how to write and cal…


This content originally appeared on Level Up Coding - Medium and was authored by David Bethune

Part 3: Working with JSON Objects

So far in this series, we’ve looked at data binding, storing data in objects, and mapping over it with arrays. In this episode, we’ll look at how to slice-and-dice the data in Typescript objects and how to write and call functions that work with objects internally.

Quick Index
Part 1: Properties, Values, & Data Binding
Part 2: Working with Arrays
Part 3: Working with JSON Objects

Inside of a Function, It’s Too Dark to Read

If you’ve been with us so far, you’ve heard me say before that everything is an object. We’ll revisit that idea momentarily, but for the bulk of this discussion, we’ll be talking about JSON objects — the ones that hold data values under a key.

Walt Whitman, famous American poet and author of Leaves of Grass once said, “Outside of a dog, a book is a man’s best friend. Inside of a dog, it’s too dark to read.”

In Part 2, we used an object to store colors for a universal palette. Then we recalled values from that object with a key that was, itself, variable. This part is important.

The constant colors defined here…
…can be accessed by key with any variable from our model. Here, its this.color.

We’re going to use this technique a lot because, inside of a function, we are usually acting on a variable value. Something passed-in from the outside, a parameter, becomes the value of the variable. In our previous example, that was the color= property. Hold that thought…

Adding Objects to Objects

Earlier, we looked at how to walk through or iterate the keys in an array to get values from the corresponding object key. Let’s extend our model to add names for the colors. This will require re-thinking the architecture.

Each key in a JSON object can only have one value. But, that value can be an object with multiple values under its own keys! That gives us the ability to redefine each color as an object, rather than just a hex value.

Redefining red as an object lets us store two values inside it, each under its own key.

Here I changed each color to have two values, one under hex and one under name. Whenever we change the model, we also need to change the view, so let’s update dta-chip.ts:

We can get to the hex color, but that’s really ugly! There is a better way…
In Michael Crichton’s Sphere, scientists in the deep ocean find a structure within a structure from the future — which seems to have been placed there in the past! This is a lot like working with objects.

So to get “three layers deep” and find the actual hex value, we now have to write a long, yucky accessor. In pseudocode, we could say we:

  1. Take the object that is the value of this.color key from dta.colors.
  2. Return the value of the hex key from it.

This isn’t the worst accessor in history, but it is an example of code smell. That’s an early warning that this code could be problematic later. Here, the problem lies in the complexity of the steps. In effect, they constitute a “recipe,” and we can use that idea to refactor them into a function.

There’s More Than One Way…

In programming, there’s always more than one way to achieve the same thing. The best one to start with is the one that’s easiest for you to reason about. Then, you can refactor into something better. Let’s look at a couple of ways we could move the recipe for “getting a color’s hex value” into a function.

Using a Function Object

In Typescript, the other major type of object (aside from the JSON data we’ve been storing) is a function object. The difference between functions and every other object is that a function does something.

JSON data does not “do” anything. It sits around like a granary and waits for you to put wheat in and take it out. Functions are the do-ers and they follow our recipes. In Part 2, we looked at arrow functions that “do something” while we map() over an array.

In this case, we need a standalone function so that we can apply its recipe from any component. You may have noticed we’ve already written several function calls like map() that end with parenthesis. This is how you (and Typescript) differentiate between JSON data objects and function objects. Data doesn’t end with parens. Functions do.

On line 44, we are accessing data only. There are no parens after the object. Line 45 is a function call with a parameter in parentheses. The (identical) results are shown to the right of each.

Here we can see the difference in syntax between accessing data from the model (on line 44) and running or calling a function with a parameter in parenthesis (on line 45). In our particular model, these are two ways of getting at the same (fixed) piece of data.

How could we call a function to get a hex value from a variable key? In dta-chip.ts, Let’s replace the background color dta.colors[this.color].hex with a call to a new function dta.getColor(thisColor), which we haven’t defined yet.

Refactoring the background color to use a function we haven’t written yet.

Defining and Exporting a Function

Let’s write the function definition. The ideal place is the colors.ts module that we already made and imported. If we export the function definition, it will be available we import it. Here is the recipe:

  1. Given a color (key)…
  2. Return its hex value from our existing colors object.
Our new function in yellow takes in a color key (in blue). On line 31, it returns the dta.colors hex value from that same key.

In order to access the dta.colors object from our model, we’ll need to import it (or the module loader) on line 4.

This screenshot also shows a VSCode trick… You can collapse a big section of code (like export const colors) with the > right angle symbol on line 6.

Defining and Using the Parameter Value

The first line of our recipe says we are “given a color key.” Anything we are given becomes a parameter. On line 29, we define the name of that parameter to use inside this function. This name is meaningless in any other context. It only applies inside this recipe. This code says the parameter is named color.

When we access the same color variable later in the recipe, on line 31, we get whatever value was passed-in on a particular function call. So if we call this function with getColor(“green”) we get back dta.colors[green].hex.

Returns of the Day

Typescript functions can return a value, although that’s not required. If a function has a return statement like the one on line 31, whatever is returned becomes the value that substitutes for the function call where it was used. Another way to say is that the return value replaces the function call in the same position in the code.

The return value from the function call on line 45 is bound to the contents of the <div>.

The net effect is that our new getColor function will take in a key and give back the hex color we need, thus plugging correctly into the background-color value we put on the chip. Here it is again for reference:

Calling our new function with this component’s color property as a parameter.

The browser “sees” the background-color property as though only the return value of the function is present. The function itself does not appear anywhere. If we look out our output, it should be unchanged!

The result of a successful refactoring should be… nothing!

A successful refactoring or improvement of the existing code (as opposed to a new feature) should result in no changes to the existing output. In other words, this new way of getting the hex code should work exactly like the old way. Under-the-hood, we’ve added a new name for each color (a new feature), so let’s add that to the view.

Adding New Data from the Model

To get to the color name that’s now in our model, let’s write another function in colors.ts.

The new getName function takes the same color key and returns the name.

I can access the name in the component view by calling my new function. Here, I’ve put it inside a <div> inside the chip.

Adding a <div> to show the color name by calling our new function.

Lay it on the Line!

With some CSS on the chip component, I can display the color names inside the chips.

The less-than-perfect rendering below is brought to you by this CSS in dta-chip.ts.
I also updated the <dta-page> component to use one of my custom colors by referencing dta.getColor(‘seaGreen’) in the styling of the word “palette” in the Pages constant.

This example also demonstrates using the color elsewhere in your output, as with the word palette here to which I applied dta.getColor(“seaGreen”).

So Many Functions

It might seem as though we’re creating a lot of functions for simple things. After all, we already have two functions just to get two different things out of the same object. This brings up an important point in your architectural designs. Only you know how many functions are too many and only you can decide when you should refactor to a better way of accomplishing the same thing. Before I move on, let’s just think of a few ways this design could be changed:

  1. A single function could return the name or the hex value, depending on a parameter.
  2. A single function could return the color object at the key’d location and let us take the keys off of it that we need.
  3. We could access dta.colors directly when we need it, albeit with multiple layers of keys.

Does any of these appeal to you more than what we have now? Feel free to think about it or code it up as you work through your own designs.

If you decide to refactor your code, aim for no changes in output first to prove your refactoring works — before you add new features. The code we have so far could be refactored in any of these ways (and several others) without changing the output. Choose the design that’s easiest for you to understand.

Schoolhouse Rock’s Conjunction Junction asked, “What’s your function?” The answer? “Hooking up words and phrases and clauses.” While that’s not an exact definition of functions in programming, it’s not far off!

Creating a Gradient Component

Let’s see if we can build something more advanced out of our palette. As an exercise in working with objects, lets build a gradient component that shows a range of colors between two color keys that we pass it. As usual, I’ll code how I want to use the component before I write its functionality.

Adding a <dta-gradient> with the start and end colors I want.

In my new dta-gradient.ts file, I need to add the two properties that will receive data from these two attributes.

Creating a dta-gradient component and adding properties to it that match the attributes I’m passing.

In my <dta-gradient> element, I want to pass just the color keys for start= and end=. But to draw the gradient, I’ll need to dig deeper and get the hex values. Let’s look at how we can “mine” an object to create variables out of its values.

Few people know that the Maya pyramids, like this one at Cobá, were mostly found in ruins. The front sides were reconstructed by guessing. The back sides, not at all! With destructuring in TS, you can take apart complex objects without losing track of the parts.

Destructuring Objects

Here, the key name (yellow) will go into a variable called startName (green). It’s value will be the color object at the key this.start (blue).

This code may look strange, but it demonstrates one of Typescript’s most powerful features: destructuring assignment. That’s a mouthful, like this code, but in practice it’s very simple and powerful.

Having passed in the start and end keys, I want the name and hex colors of each to go into separate variables. However, these are all on the same keys in each color object! In other words, both the start and end hex color are under the hex key.

In a destructuring assignment, I tell Typescript how to break it down. Line 30 says to grab the key called name and assign it to a new variable called startName, and to take the key hex and assign it to a new variable startHex. “The key called name off of what?, you might ask.” And the answer is on the other side of the assignment statement, after the = equal sign.

By assigning dta.colors[this.start] on the right side to a destructing argument on the left side, we are telling TS to take the object at dta.colors[this.start] and divide it up as we outlined. The end values are processed the same way on line 31.

Here’s what the same assignment statements look like without destructuring:

This is perfectly acceptable but longer than destructuring with more chances for errors. You make the call.

Both of these are recipes for Igor, but destructuring is a more compact form with less repeated text and fewer chances for errors. This raises an important principle in Typescript. Often, the shortest code is the best because every piece of code has potential errors — even simply typos or things in the wrong order that are hard to spot. Anytime we can conceive of a design with less code, that’s less code that might be in error. Ultimately, Typescript is for humans to read (the computer uses machine language), so write the code that is easiest for you to read and reason about.

If we don’t need to make up a new name for the variables, we can write an even shorter version of object destructuring:

To create variables with the same names as the object keys, just destructure the keys you want.

If we just write the key name, we create a variable of that same name. You can take as few or as many keys from the object as you need. The :newName syntax (as on lines 30–31) is only needed if you want a different name for the new variable. Destructuring has many other powerful features which we’ll examine in another post.

Guests to the Eiffel Tower today ride the same elevators engineered by Elisha Otis for its opening at the 1899 World’s Fair. Amazingly, these beautiful cars run on a curved, inclined track. CSS is also capable of some serious “heavy lifting” in web component design.

Let CSS Do the Heavy Lifting

CSS has an amazing number of abilities under the hood. One of them is gradients, and MDN has a great intro.

Let’s have our new gradient component output two of the color chips — for the start and end colors. In between, we’ll draw the gradient box.

The <dta-chip> elements at the top and bottom (in yellow) take the start and end properties. The <div> in the center draws the gradient from the ${startHex} and ${endHex} (in blue).

We need the start and end keys (for the chips) and the hex values (for the gradient). We can plug-in those variables, thanks to the destructuring assignment. The gradient itself is fairly easy to read, even if we don’t know the CSS.

This ease of reading is another benefit of destructuring (and good naming in general). We may not know the order of parameters in the CSS gradient on line 35, but we can read ${startHex} and know what it represents.

Half of this new component is our other component, reused. The purpose is obvious from the name: <dta-chip>. It’s easier for humans to read ${startHex} and <dta-chip> than it is to read the code they represent. As your programs become more complicated, this idea will become even more of a benefit.

With a touch of CSS, the gradient will render nicely between the start and end chips.

And It Works, Too!

Our gradient and palette components use the same dta.colors object, and both render multiple instances of our custom <dta-chip>.

I can refactor the single gradient into a palette page by adding that key and some content in dta-page.ts. The content is just a few instances of my new gradient component! Then, I can visit that page separately in the browser.

Adding a new palette page with three gradients.
Visiting the palette page in the browser with the /palette URL shows the three gradients defined in the code.

That’s All Folks!

So what have we accomplished? In this series, we’ve seen several ways to approach learning Typescript with web components. All of them are built around the idea of designing a data model (a combination of JSON objects and arrays) first. Then, we pass around parts of our model with properties and attributes to get access to data from inside the component views. By combining special properties of arrays and objects in Typescript and Lit, we can create complex sets of UI elements with a minimum of repeated code. Finally, we can define custom functions both inside and outside of those components to make our code easier to read and debug.

Until next time, thanks for reading!

— D

Level Up Coding

Thanks for being a part of our community! More content in the Level Up Coding publication.
Follow: Twitter, LinkedIn, Newsletter
Level Up is transforming tech recruiting 👉 Join our talent collective


Learning Typescript with Web Components: Part 3 was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by David Bethune


Print Share Comment Cite Upload Translate Updates
APA

David Bethune | Sciencx (2022-07-18T00:33:42+00:00) Learning Typescript with Web Components: Part 3. Retrieved from https://www.scien.cx/2022/07/18/learning-typescript-with-web-components-part-3/

MLA
" » Learning Typescript with Web Components: Part 3." David Bethune | Sciencx - Monday July 18, 2022, https://www.scien.cx/2022/07/18/learning-typescript-with-web-components-part-3/
HARVARD
David Bethune | Sciencx Monday July 18, 2022 » Learning Typescript with Web Components: Part 3., viewed ,<https://www.scien.cx/2022/07/18/learning-typescript-with-web-components-part-3/>
VANCOUVER
David Bethune | Sciencx - » Learning Typescript with Web Components: Part 3. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/07/18/learning-typescript-with-web-components-part-3/
CHICAGO
" » Learning Typescript with Web Components: Part 3." David Bethune | Sciencx - Accessed . https://www.scien.cx/2022/07/18/learning-typescript-with-web-components-part-3/
IEEE
" » Learning Typescript with Web Components: Part 3." David Bethune | Sciencx [Online]. Available: https://www.scien.cx/2022/07/18/learning-typescript-with-web-components-part-3/. [Accessed: ]
rf:citation
» Learning Typescript with Web Components: Part 3 | David Bethune | Sciencx | https://www.scien.cx/2022/07/18/learning-typescript-with-web-components-part-3/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.