Tag: Back

Rendering External API Data in WordPress Blocks on the Back End

This is a continuation of my last article about “Rendering External API Data in WordPress Blocks on the Front End”. In that last one, we learned how to take an external API and integrate it with a block that renders the fetched data on the front end of a WordPress site.

The thing is, we accomplished this in a way that prevents us from seeing the data in the WordPress Block Editor. In other words, we can insert the block on a page but we get no preview of it. We only get to see the block when it’s published.

Let’s revisit the example block plugin we made in the last article. Only this time, we’re going to make use of the JavaScript and React ecosystem of WordPress to fetch and render that data in the back-end Block Editor as well.

Where we left off

As we kick this off, here’s a demo where we landed in the last article that you can reference. You may have noticed that I used a render_callback method in the last article so that I can make use of the attributes in the PHP file and render the content.

Well, that may be useful in situations where you might have to use some native WordPress or PHP function to create dynamic blocks. But if you want to make use of just the JavaScript and React (JSX, specifically) ecosystem of WordPress to render the static HTML along with the attributes stored in the database, you only need to focus on the Edit and Save functions of the block plugin.

  • The Edit function renders the content based on what you want to see in the Block Editor. You can have interactive React components here.
  • The Save function renders the content based on what you want to see on the front end. You cannot have the the regular React components or the hooks here. It is used to return the static HTML that is saved into your database along with the attributes.

The Save function is where we’re hanging out today. We can create interactive components on the front-end, but for that we need to manually include and access them outside the Save function in a file like we did in the last article.

So, I am going to cover the same ground we did in the last article, but this time you can see the preview in the Block Editor before you publish it to the front end.

The block props

I intentionally left out any explanations about the edit function’s props in the last article because that would have taken the focus off of the main point, the rendering.

If you are coming from a React background, you will likely understand what is that I am talking about, but if you are new to this, I would recommend checking out components and props in the React documentation.

If we log the props object to the console, it returns a list of WordPress functions and variables related to our block:

Console log of the block properties.

We only need the attributes object and the setAttributes function which I am going to destructure from the props object in my code. In the last article, I had modified RapidAPI’s code so that I can store the API data through setAttributes(). Props are only readable, so we are unable to modify them directly.

Block props are similar to state variables and setState in React, but React works on the client side and setAttributes() is used to store the attributes permanently in the WordPress database after saving the post. So, what we need to do is save them to attributes.data and then call that as the initial value for the useState() variable.

The edit function

I am going to copy-paste the HTML code that we used in football-rankings.php in the last article and edit it a little to shift to the JavaScript background. Remember how we created two additional files in the last article for the front end styling and scripts? With the way we’re approaching things today, there’s no need to create those files. Instead, we can move all of it to the Edit function.

Full code
import { useState } from "@wordpress/element"; export default function Edit(props) {   const { attributes, setAttributes } = props;   const [apiData, setApiData] = useState(null);     function fetchData() {       const options = {         method: "GET",         headers: {           "X-RapidAPI-Key": "Your Rapid API key",           "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",         },       };       fetch(         "https://api-football-v1.p.rapidapi.com/v3/standings?season=2021&league=39",           options       )       .then((response) => response.json())       .then((response) => {         let newData = { ...response }; // Deep clone the response data         setAttributes({ data: newData }); // Store the data in WordPress attributes         setApiData(newData); // Modify the state with the new data       })       .catch((err) => console.error(err));     }     return (       <div {...useBlockProps()}>         <button onClick={() => getData()}>Fetch data</button>         {apiData && (           <>           <div id="league-standings">             <div               className="header"               style={{                 backgroundImage: `url($ {apiData.response[0].league.logo})`,               }}             >               <div className="position">Rank</div>               <div className="team-logo">Logo</div>               <div className="team-name">Team name</div>               <div className="stats">                 <div className="games-played">GP</div>                 <div className="games-won">GW</div>                 <div className="games-drawn">GD</div>                 <div className="games-lost">GL</div>                 <div className="goals-for">GF</div>                 <div className="goals-against">GA</div>                 <div className="points">Pts</div>               </div>               <div className="form-history">Form history</div>             </div>             <div className="league-table">               {/* Usage of [0] might be weird but that is how the API structure is. */}               {apiData.response[0].league.standings[0].map((el) => {                                  {/* Destructure the required data from all */}                 const { played, win, draw, lose, goals } = el.all;                   return (                     <>                     <div className="team">                       <div class="position">{el.rank}</div>                       <div className="team-logo">                         <img src={el.team.logo} />                       </div>                       <div className="team-name">{el.team.name}</div>                       <div className="stats">                         <div className="games-played">{played}</div>                         <div className="games-won">{win}</div>                         <div className="games-drawn">{draw}</div>                         <div className="games-lost">{lose}</div>                         <div className="goals-for">{goals.for}</div>                         <div className="goals-against">{goals.against}</div>                         <div className="points">{el.points}</div>                       </div>                       <div className="form-history">                         {el.form.split("").map((result) => {                           return (                             <div className={`result-$ {result}`}>{result}</div>                           );                         })}                       </div>                     </div>                     </>                   );                 }               )}             </div>           </div>         </>       )}     </div>   ); }

I have included the React hook useState() from @wordpress/element rather than using it from the React library. That is because if I were to load the regular way, it would download React for every block that I am using. But if I am using @wordpress/element it loads from a single source, i.e., the WordPress layer on top of React.

This time, I have also not wrapped the code inside useEffect() but inside a function that is called only when clicking on a button so that we have a live preview of the fetched data. I have used a state variable called apiData to render the league table conditionally. So, once the button is clicked and the data is fetched, I am setting apiData to the new data inside the fetchData() and there is a rerender with the HTML of the football rankings table available.

You will notice that once the post is saved and the page is refreshed, the league table is gone. That is because we are using an empty state (null) for apiData‘s initial value. When the post saves, the attributes are saved to the attributes.data object and we call it as the initial value for the useState() variable like this:

const [apiData, setApiData] = useState(attributes.data);

The save function

We are going to do almost the same exact thing with the save function, but modify it a little bit. For example, there’s no need for the “Fetch data” button on the front end, and the apiData state variable is also unnecessary because we are already checking it in the edit function. But we do need a random apiData variable that checks for attributes.data to conditionally render the JSX or else it will throw undefined errors and the Block Editor UI will go blank.

Full code
export default function save(props) {   const { attributes, setAttributes } = props;   let apiData = attributes.data;   return (     <>       {/* Only render if apiData is available */}       {apiData && (         <div {...useBlockProps.save()}>         <div id="league-standings">           <div             className="header"             style={{               backgroundImage: `url($ {apiData.response[0].league.logo})`,             }}           >             <div className="position">Rank</div>             <div className="team-logo">Logo</div>             <div className="team-name">Team name</div>             <div className="stats">               <div className="games-played">GP</div>               <div className="games-won">GW</div>               <div className="games-drawn">GD</div>               <div className="games-lost">GL</div>               <div className="goals-for">GF</div>               <div className="goals-against">GA</div>               <div className="points">Pts</div>             </div>             <div className="form-history">Form history</div>           </div>           <div className="league-table">             {/* Usage of [0] might be weird but that is how the API structure is. */}             {apiData.response[0].league.standings[0].map((el) => {               const { played, win, draw, lose, goals } = el.all;                 return (                   <>                   <div className="team">                     <div className="position">{el.rank}</div>                       <div className="team-logo">                         <img src={el.team.logo} />                       </div>                       <div className="team-name">{el.team.name}</div>                       <div className="stats">                         <div className="games-played">{played}</div>                         <div className="games-won">{win}</div>                         <div className="games-drawn">{draw}</div>                         <div className="games-lost">{lose}</div>                         <div className="goals-for">{goals.for}</div>                         <div className="goals-against">{goals.against}</div>                         <div className="points">{el.points}</div>                       </div>                       <div className="form-history">                         {el.form.split("").map((result) => {                           return (                             <div className={`result-$ {result}`}>{result}</div>                           );                         })}                       </div>                     </div>                   </>                 );               })}             </div>           </div>         </div>       )}     </>   ); }

If you are modifying the save function after a block is already present in the Block Editor, it would show an error like this:

The football rankings block in the WordPress block Editor with an error message that the block contains an unexpected error.

That is because the markup in the saved content is different from the markup in our new save function. Since we are in development mode, it is easier to remove the bock from the current page and re-insert it as a new block — that way, the updated code is used instead and things are back in sync.

This situation of removing it and adding it again can be avoided if we had used the render_callback method since the output is dynamic and controlled by PHP instead of the save function. So each method has it’s own advantages and disadvantages.

Tom Nowell provides a thorough explanation on what not to do in a save function in this Stack Overflow answer.

Styling the block in the editor and the front end

Regarding the styling, it is going to be almost the same thing we looked at in the last article, but with some minor changes which I have explained in the comments. I’m merely providing the full styles here since this is only a proof of concept rather than something you want to copy-paste (unless you really do need a block for showing football rankings styled just like this). And note that I’m still using SCSS that compiles to CSS on build.

Editor styles
/* Target all the blocks with the data-title="Football Rankings" */ .block-editor-block-list__layout  .block-editor-block-list__block.wp-block[data-title="Football Rankings"] {   /* By default, the blocks are constrained within 650px max-width plus other design specific code */   max-width: unset;   background: linear-gradient(to right, #8f94fb, #4e54c8);   display: grid;   place-items: center;   padding: 60px 0;    /* Button CSS - From: https://getcssscan.com/css-buttons-examples - Some properties really not needed :) */   button.fetch-data {     align-items: center;     background-color: #ffffff;     border: 1px solid rgb(0 0 0 / 0.1);     border-radius: 0.25rem;     box-shadow: rgb(0 0 0 / 0.02) 0 1px 3px 0;     box-sizing: border-box;     color: rgb(0 0 0 / 0.85);     cursor: pointer;     display: inline-flex;     font-family: system-ui, -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;     font-size: 16px;     font-weight: 600;     justify-content: center;     line-height: 1.25;     margin: 0;     min-height: 3rem;     padding: calc(0.875rem - 1px) calc(1.5rem - 1px);     position: relative;     text-decoration: none;     transition: all 250ms;     user-select: none;     -webkit-user-select: none;     touch-action: manipulation;     vertical-align: baseline;     width: auto;     &:hover,     &:focus {       border-color: rgb(0, 0, 0, 0.15);       box-shadow: rgb(0 0 0 / 0.1) 0 4px 12px;       color: rgb(0, 0, 0, 0.65);     }     &:hover {       transform: translateY(-1px);     }     &:active {       background-color: #f0f0f1;       border-color: rgb(0 0 0 / 0.15);       box-shadow: rgb(0 0 0 / 0.06) 0 2px 4px;       color: rgb(0 0 0 / 0.65);       transform: translateY(0);     }   } }
Front-end styles
/* Front-end block styles */ .wp-block-post-content .wp-block-football-rankings-league-table {   background: linear-gradient(to right, #8f94fb, #4e54c8);   max-width: unset;   display: grid;   place-items: center; }  #league-standings {   width: 900px;   margin: 60px 0;   max-width: unset;   font-size: 16px;   .header {     display: grid;     gap: 1em;     padding: 10px;     grid-template-columns: 1fr 1fr 3fr 4fr 3fr;     align-items: center;     color: white;     font-size: 16px;     font-weight: 600;     background-color: transparent;     background-repeat: no-repeat;     background-size: contain;     background-position: right;      .stats {       display: flex;       gap: 15px;       &amp; &gt; div {         width: 30px;       }     }   } } .league-table {   background: white;   box-shadow:     rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,     rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;   padding: 1em;   .position {     width: 20px;   }   .team {     display: grid;     gap: 1em;     padding: 10px 0;     grid-template-columns: 1fr 1fr 3fr 4fr 3fr;     align-items: center;   }   .team:not(:last-child) {     border-bottom: 1px solid lightgray;   }   .team-logo img {     width: 30px;     top: 3px;     position: relative;   }   .stats {     display: flex;     gap: 15px;     &amp; &gt; div {       width: 30px;       text-align: center;     }   }   .last-5-games {     display: flex;     gap: 5px;     &amp; &gt; div {       width: 25px;       height: 25px;       text-align: center;       border-radius: 3px;       font-size: 15px;     &amp; .result-W {       background: #347d39;       color: white;     }     &amp; .result-D {       background: gray;       color: white;     }     &amp; .result-L {       background: lightcoral;       color: white;     }   } } 

We add this to src/style.scss which takes care of the styling in both the editor and the frontend. I will not be able to share the demo URL since it would require editor access but I have a video recorded for you to see the demo:


Pretty neat, right? Now we have a fully functioning block that not only renders on the front end, but also fetches API data and renders right there in the Block Editor — with a refresh button to boot!

But if we want to take full advantage of the WordPress Block Editor, we ought to consider mapping some of the block’s UI elements to block controls for things like setting color, typography, and spacing. That’s a nice next step in the block development learning journey.


Rendering External API Data in WordPress Blocks on the Back End originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

CSS-Tricks

, , , , ,

Scroll Shadows? Pure CSS Parallax? Game Back On.

Chris calls scroll shadows one his favorite CSS-Tricks of all time. Lea Verou popularized the pure CSS approach using four layered background gradients with some clever background-attachment magic. The result is a slick scrolling interaction that gives users a hint that additional content is available in a scrollable container.

Just one problem: it broke in Safari iOS 13. One day it was all good. The next, not so much. And that wasn’t the only thing affected. Keith Clark’s CSS-only parallax effect also stopped working right about then.

Well, reader Ronald wrote in to say that all is working once again! In fact, I’m writing this on my iPad (Safari 15.5) right now and Chris’s demo is looking sharp as ever. So is Keith’s original demo.

So, what broke it? We still don’t know. But the Safari 13 release notes offer clues:

  • Added support for one-finger accelerated scrolling to all frames and overflow:scroll elements eliminating the need to set-webkit-overflow-scrolling: touch.
  • Changed the default behavior on iPad for wide web pages with responsive meta-tags that require horizontal scrolling. Pages are scaled to prevent horizontal scrolling and any text is resized to preserve legibility.

When was it fixed and what fixed it? Well, on the scroll shadow side, the Safari 15.4 included some work on background-attachment: local that may have done the trick. On the parallax side, Safari 14.1 added support for individual transform properties… so maybe that?


Scroll Shadows? Pure CSS Parallax? Game Back On. originally published on CSS-Tricks. You should get the newsletter.

CSS-Tricks

, , , , ,
[Top]

Web Unleashed is Back! (Use Coupon Code “CSS-Tricks” for 50% Off)

(This is a sponsored post.)

Now in its tenth year (!!!), Web Unleashed is one of the top events for web devs. It’s coming up quick: October 20-22, 2021.

The lineup is amazing. You’ll hear from leaders in the industry, including Ethan Marcotte, Rachel Andrew, Harry Roberts, Addy Osmani, Tracy Lee Ire, Aderinokun, Suz Hinton, Shawn Wang, Jen Looper, and many more.

And don’t forget the coupon code CSS-Tricks as 50% off is a massively good deal! (Here’s where you apply it)

So many hot topics will be covered, including responsive design, optimizing images, open source projects, performance metrics, Gatsby, GraphQL, Applied ML, growing your career, and much much more.

But, seriously folks, take it from someone who has gone to this event a bunch of years… it’s really well done, you’re going to learn a lot, and the price right now is unbeatable. The entire conference clocks in at CA $ 179.16, which is CA $ 89.91 with the CSS-Tricks coupon code. That’s… just go.

Direct Link to ArticlePermalink


The post Web Unleashed is Back! (Use Coupon Code “CSS-Tricks” for 50% Off) appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]

Jetpack Backup: Roll Back Your WooCommerce Site Without Losing Orders

Here’s a dilemma: what happens if your WooCommerce site has a problem and the quickest and best way to fix it is to roll back to a previous version? The dilemma is, if you roll back the database, you would lose literal customer orders that are stored in the database. That’s not acceptable for an eCommerce store.

Good news: Jetpack Backup won’t do you wrong like that. You can still restore to a prior point, but you won’t lose any customer order or product data.

Do you lose all the orders that came in since that last backup? Nope.

Will the inventory get all screwed up? Nope.

What about the new products that were added after the restore point? Still there.

All that data is treated as immutable. The way that it plays out is that the database is restored to that point (along with everything else) and that all the new product and order data that has changed since then is replayed on top of the database after the restore.

With Jetpack Backup, there’s absolutely no guesswork. Its real-time snapshots feature has a unique feature that protects WooCommerce customer and product data when rolling back things back so you get the best-case scenario: readily available backups that preserves customer orders and product information.

That’s just one of the magical benefits you get from such a deep integration between Jetpack and WooCommerce. There are others, of course! But you can imagine just what a big deal this specific integration for any WooCommerce-powered store.

And, hey, Jetpack Backup is sold à la carte. So, if backups are all you want from Jetpack, then you can get just that and that alone.


The post Jetpack Backup: Roll Back Your WooCommerce Site Without Losing Orders appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , , , ,
[Top]

Front of the Front / Back of the Front

People really latched onto Brad’s framing. And for good reason. Front-end development has gotten so wide scoping that there are specialists inside of it. Two years ago, I cut it down the middle and now Brad is putting a point on that here, saying he has actual clients who have shifted their hiring strategy away from full-stack and toward these exact distinctions. Nice.

Brad shoots for a simple distinction:

A succinct way I’ve framed the split is that a front-of-the-front-end developer determines the look and feel of a button, while a back-of-the-front-end developer determines what happens when that button is clicked.

Part of me loves the clarity there. And part of me like But! But! Wait! I’m a front-of-the-front kinda guy, but I totally deal with what happens on click. I’m a state updating machine over here. I’ll fire off that GraphQL mutation myself, thankyouverymuch. I friggin own that onClick.

And yet, I still don’t feel back-of-the-front at all. I can’t set up that GraphQL API or troubleshoot it. I don’t know what the security implications of the network request is. I don’t know if the query I wrote will be performant or not, nor where to look at a graph to find out. I think I’d draw the line in a slightly different place than Brad, but he knows that. He’s flexible here:

The line between front-of-the-front-end and back-of-the-front-end can be fuzzy and varies greatly from developer to developer. It’s totally possible that one developer might be able to perform many tasks across the front-end spectrum. But it’s also worth pointing out that’s not super common.

That’s why the term full-stack isn’t my favorite. I bet an awful lot of developers have skill sets on both sides of the “great divide”, which I think makes it feel like you’re full-stack when it’s more likely you’re cross-stack. Full-stack makes me feel like you’re deeply knowledgeable about literally everything across not only the front-end spectrum but back-end too. Brad says that’s not common and I’ll up that and say it’s downright rare.

My main regret about trying to cut front-end development in half is that it’s too clean of a metaphor for a messy thing.

I live in Bend, Oregon where outdoor hobbies are like the main thing. You don’t really go up and ask people if they are a summer sports person or a winter sports person because they are almost always both. But one person might be into snowshoeing, downhill skiing, and day hiking, where the next person might be into paddle boarding, nordic skiing, and mountain biking. So I had this idea of Bend Name Tags where it lists all the outdoor hobbies and you write your name and then circle all the ones that you’re into.

It should almost be like that with front-end development. You write your name and then list out all the things you’re into. Me? I like HTML, CSS, JavaScript, Build Processes, Design, React, WordPress, with a dash of Accessibility, Performance, and Copywriting. What does that make me? Me.

Direct Link to ArticlePermalink


The post Front of the Front / Back of the Front appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

I’m getting back to making videos

It’s probably one part coronavirus, one part new-fancy-video setup, and one part “hey this is good for CodePen too,” but I’ve been doing more videos lately. It’s nice to be back in the swing of that for a minute. There’s something fun about coming back to an old familiar workflow.

Where do the videos get published? I’m a publish-on-your-own site kinda guy, as I’m sure you know, so there is a whole Videos section of this site where every video we’ve ever published lives. There is also a YouTube channel, of course, which is probably the most practical way for most people to subscribe. We’re about halfway to Wes Bos-level, so let’s go people!

I had literally forgotten about it, but ages ago when I set this up, I created a special RSS feed for the videos so I could submit it as a video podcast on iTunes. That’s all still there and working! An interesting side note is that this enables offline viewing, as most podcatchers can cache subscriptions. Why build an app when you get the core ability for free, right?

I keep the original videos, of course. On individual video pages, I show a YouTube player that could be somewhat easily swapped out for another player if something crazy happened, like YouTube closes down or drastically changed their business model in some way that makes it problematic to show videos with their player. The originals are stored in an S3 bucket. If you’re an MVP Supporter, I give you the original high-quality download link right on the video pages.

If your curious about my workflow, I’m still using ScreenFlow. I don’t make nearly enough use of it, but it feels good in that it’s fairly easy to use, very reliable and fast, and I can always learn and do more with it. Shooting my screen is easy and a built-in feature of ScreenFlow of course. I also have a Rode Podcaster on a boom arm at my desk so the audio is passable. And I just went through a whole process to use a DSLR camera at my desk too, and I think the quality from that is great. It’s all a little funny because I have this whole sound recording booth as well, with a $ 1,000 audio setup in there, but I only use that for podcasting. The lighting sucks in there, making it no good for video.

It’s this new desk setup that has inspired me to do more video, and I suspect it will continue! One thing I could really use is a new high quality intro video. Just like a five-second thing with refreshed aesthetics. Anyone do that kind of work?

The post I’m getting back to making videos appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Two Steps Forward, One Step Back

Brent Jackson says CSS utility libraries failed somewhat:

Eventually, you’ll need to add one-off styles that just aren’t covered by the library you’re using, and there isn’t always a clear way to extend what you’re working with. Without a clear way to handle things like this, developers tend to add inconsistent hacks and append-only styles.

I have a feeling Tailwind people would disagree. I have no particular opinion here, I’m just noting that Tailwind seems to have a more fervent fanbase than those early days of Basscss/Tachyons.

Brent goes on to say that CSS-in-JS solves the same problem, but in a better way:

CSS-in-JS libraries help solve a lot of the same issues Utility-based CSS methodologies were focused on (and more) in a much better way. They connect styles directly to elements without needing to name things or create abstractions in class selectors. They avoid append-only stylesheets with encapsulation and hashed classnames. These libraries work with existing build tools, allowing for code splitting, lazy loading, and dead code elimination with virtually zero effort, and they don’t require additional tools like Sass or PostCSS. Many libraries also include CSS performance optimizations, such as critical CSS, enabled by default so that developers don’t need additional tooling or even need to think about them.

No wonder people have been raving about this.

The one-step-back refers to the fact that CSS-in-JS is more open-ended and doesn’t encourage consistency as much. I’m not sure about that. Seems like if you’re building in a component-based way already, consistency kind of comes along for the ride, even before using design tokens and the like — which a CSS-in-JS approach also encourages.

Direct Link to ArticlePermalink

The post Two Steps Forward, One Step Back appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

JavaScript to Native (and Back!)

I admit I’m quite intrigued by frameworks that allow you write apps in web frameworks because they do magic to make them into native apps for you. There are loads of players here. You’ve got NativeScript, Cordova, PhoneGap, Tabris, React Native, and Flutter. For deskop apps, we’ve got Electron.

What’s interesting now is to see what’s important to these frameworks by honing in on their focus. Hummingbird is Flutter for the web. (There is a fun series on Flutter over on the Bendworks blog in addition to a post we published earlier this year.) The idea being you get super high performance ,thanks to the framework, and you’ve theoretically built one app that runs both on the web and natively. I don’t know of any real success stories I can point to, but it does seem like an awesome possibility.

Nicolas Gallagher has been a strong proponent of React Native for the web.

The post JavaScript to Native (and Back!) appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]