<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: muncey</title>
    <description>The latest articles on DEV Community by muncey (@muncey).</description>
    <link>https://dev.to/muncey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F134950%2F3eb79b41-4cda-4d72-945a-a9474f32e6ea.jpg</url>
      <title>DEV Community: muncey</title>
      <link>https://dev.to/muncey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/muncey"/>
    <language>en</language>
    <item>
      <title>How I was able to configure syntax highlighting on my WordPress site</title>
      <dc:creator>muncey</dc:creator>
      <pubDate>Mon, 27 Dec 2021 20:27:57 +0000</pubDate>
      <link>https://dev.to/muncey/how-i-was-able-to-configure-syntax-highlighting-on-my-wordpress-site-p6b</link>
      <guid>https://dev.to/muncey/how-i-was-able-to-configure-syntax-highlighting-on-my-wordpress-site-p6b</guid>
      <description>&lt;p&gt;I like to blog about technical topics and part of this involves providing code snippets with an explanation and further exploration about those code snippets.&lt;/p&gt;

&lt;p&gt;When readers are reading a page on my blog I would like any code snippets that are on a page to be formatted with syntax highlighting as per the following image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VE9xJDMS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oscticuy1f2hafl67ffo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VE9xJDMS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oscticuy1f2hafl67ffo.png" alt="Image description" width="744" height="435"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A89z6tgD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ec2k8cos22ws6bkzopnt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A89z6tgD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ec2k8cos22ws6bkzopnt.png" alt="Image description" width="744" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The WordPress Gutenberg editor provides a code block which will be used to output HTML tags which are typically used to hold a code snippet.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--75Ybvemm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwniumj63qlzb6ue3090.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--75Ybvemm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwniumj63qlzb6ue3090.png" alt="Image description" width="879" height="60"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem is that the HTML code block does not have any specific formatting and will require changes to be made to the contents of the code block in order to display with syntax highlighting. The changes are to identify key words in a code snippet and then put &lt;span&gt; tags around those keywords with a specific class applied to each. This can be done fairly easily with a regular expression search applied across a small block of code.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;I made a decision that I would use a code library to implement this functionality rather than write my own library. I decided to use the Code Prettify library from the Google archives in GitHub. I haven’t used this library before but according to the readme on the github page for code-prettify it is used to power &lt;a href="https://code.google.com/"&gt;https://code.google.com/&lt;/a&gt; and &lt;a href="http://stackoverflow.com/"&gt;http://stackoverflow.com/&lt;/a&gt; which is encouraging.&lt;/p&gt;

&lt;p&gt;The code-prettify library can be found here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/googlearchive/code-prettify"&gt;https://github.com/googlearchive/code-prettify&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And to use the library once it is installed on a page what I need to do is to put a class=”prettyprint” on the pre or code tags on my page:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Qa7QHQe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwq8fl3ii98wxs12zpwa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Qa7QHQe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwq8fl3ii98wxs12zpwa.png" alt="Image description" width="865" height="313"&gt;&lt;/a&gt;&lt;br&gt;
Now typically you would do this I guess in the functions.php file by providing a content hook and I am thinking in the future I might do this. But for the moment I am applying this functionality through a global search and replace in my client side react code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function formatPost(post) {
  const rendered = post.content.rendered;
  let result = rendered.replace(/\&amp;lt;code\&amp;gt;/g, '&amp;lt;code class="prettyprint"&amp;gt;');
  const parsed = parse(result);
  return parsed;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see from the code snippet (hopefully highlighted) above the logic to apply syntax highlighting has now shrunk down to a single line regular expression. The content for each post is returned inside a post.content.rendered string when retrieved via the REST API. So what the formatPost function will do is take that string and apply any client side formatting before the contents of the post are rendered on the page. I should also mention that I am using a react library to convert the post.content.rendered string into a react object for rendering on the page. This library is the react-html-parser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/react-html-parser"&gt;https://www.npmjs.com/package/react-html-parser&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above code of course will only run if I have included a link to the code-prettify library on my HTML page. For WordPress sites this is typically done using the wp_enqueue_script function call. My first call when linking to a third party library now is to use a CDN. I find that this is a stable and fast way to bring libraries in and also means you should be on a later version.&lt;/p&gt;

&lt;p&gt;According to the instructions on the git hub page for code-prettify I need to include a script tag in my document as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h4XHNIuo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/adce94vktevhlavtnw44.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h4XHNIuo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/adce94vktevhlavtnw44.png" alt="Image description" width="875" height="157"&gt;&lt;/a&gt;&lt;br&gt;
Because I am using a WordPress theme I use the following code inside the script_loader_tag hook to add the run_prettify.js file on each page as a linked Javascript library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    wp_enqueue_script( 'code-prettify', 'https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js' );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally if you are more of a content creator than a developer then you may want to link to a plugin to achieve the above functionality. I have found this Code Prettify plugin on the WordPress plugins directory which uses the code-prettify library and might help you if you would like syntax highlighting on your site.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/code-prettify/"&gt;https://wordpress.org/plugins/code-prettify/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>react</category>
    </item>
    <item>
      <title>I have updated my WordPress REST API to set and return the number of views per post</title>
      <dc:creator>muncey</dc:creator>
      <pubDate>Sun, 26 Dec 2021 21:30:23 +0000</pubDate>
      <link>https://dev.to/muncey/i-have-updated-my-wordpress-rest-api-to-set-and-return-the-number-of-views-per-post-lm3</link>
      <guid>https://dev.to/muncey/i-have-updated-my-wordpress-rest-api-to-set-and-return-the-number-of-views-per-post-lm3</guid>
      <description>&lt;p&gt;The next iteration or feature I have implemented in my react theme is a view counter which shows the number of views each one of my posts is getting on my WordPress site. The number of views is a number value that is held against each post on my WordPress site using what is known as post meta. Post meta is a mechanism that WordPress provides that will let you store some additional data with each post and is normally set behind the scenes for you by custom PHP code running in either a theme or a plugin.&lt;/p&gt;

&lt;p&gt;In my case I have set up a filter inside my functions.php file to increment a post views value by 1 each time a request is made to retrieve data for a specific post via the Rest API. The Rest API is a handy out of the box feature that WordPress provides that will let you access your pages and posts as a JSON string. You can try this out by appending &lt;strong&gt;/wp/v2/posts/&lt;/strong&gt; to the end of your site URL. You can read more information about the Rest API by going to this site: &lt;a href="https://developer.wordpress.org/rest-api/"&gt;https://developer.wordpress.org/rest-api/&lt;/a&gt;. With WordPress programming what you often need to do is search and find an appropriate hook.&lt;/p&gt;

&lt;p&gt;I was not able to find an exact hook for what I wanted to do which is to be able to run some custom code when a user requests a single post via the WordPress API. There are some hooks available for when a post is processed for sending to the user but these hooks also ran when the list of posts were retrieved which isn’t ideal particularly as I couldn’t find an easy way to distinguish between a single post and a list of posts.&lt;/p&gt;

&lt;p&gt;So what I found was there is a hook called rest_request_before_callbacks which as the name suggest runs before further processing of a rest request occurs. The documentation for this hook can be found here: &lt;a href="https://developer.wordpress.org/reference/hooks/rest_request_before_callbacks/"&gt;https://developer.wordpress.org/reference/hooks/rest_request_before_callbacks/&lt;/a&gt;. I feel that there will be a job for future Phil to use another hook or write a custom post handling class but for now this hook works quite well.&lt;/p&gt;

&lt;p&gt;Currently my code for this hook looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function my_react_count_views( $response, $handler, WP_REST_Request $request ) {
    $id = $request-&amp;gt;get_param('id');
    $route = $request-&amp;gt;get_route();
    if ((stristr($route, 'posts') || stristr($route, 'pages')) &amp;amp;&amp;amp; isset($id)) {
        $views = get_post_meta($id, 'post_views', true);
        if (!$views) {
            add_post_meta($id, 'post_views', 1);
        } else {
            update_post_meta($id, 'post_views', intval($views) + 1);
        }
    }
    return $response; 
}



add_filter( 'rest_request_before_callbacks', 'my_react_count_views', 10, 3 );

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will walk through the code step by step for you.&lt;/p&gt;

&lt;p&gt;The first thing I do is attempt to get the id parameter for the post or page by looking for an id in the page parameters using $request-&amp;gt;get_param(‘id’);. This of course will not work in the future when I move to permalinks but in the interests of getting the work done I have taken this approach for now. I haven’t looked too far into how to get this working with permalinks rather than id’s but I have a feeling the concept will be the same, I will just replace looking for an id with looking for a permalink. The reason for doing this is that I needed something to distinguish between someone viewing a post on a page and someone viewing a list of posts. In my original version I was finding that the counter was increasing each time I viewed the list.&lt;/p&gt;

&lt;p&gt;The next step is to check that the route contains either posts or pages by using the PHP stristr function.&lt;/p&gt;

&lt;p&gt;So this means that for pages or posts with an id I want to then check the post meta by using the WordPress get_post_meta function. This function will either return a value or null. If the value is null I use add_post_meta to assign a value of 1 to the post_views and if the value is set (i.e. &amp;gt; 0) then I use update_post_meta to store an incremented by 1 value into my WordPress site.&lt;/p&gt;

&lt;p&gt;You can view this working on my personal site, &lt;a href="https://munceyweb.com"&gt;https://munceyweb.com&lt;/a&gt; by clicking on a post:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cVfzXuZx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/brnyth7g6esq6g3b2e4c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cVfzXuZx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/brnyth7g6esq6g3b2e4c.png" alt="Image description" width="771" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QJtVZHrD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nm3jjxlw7t26mghau04w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QJtVZHrD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nm3jjxlw7t26mghau04w.png" alt="Image description" width="769" height="501"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The post_views counter is available in the Network tools – Preview tab in Chrome, Edge or Brave. You can see that the post_views is returned as part of the meta element and can then be used inside my theme by the React code to render the counter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2ZLW3GXe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zk2e61y40otlwvy8ps2h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2ZLW3GXe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zk2e61y40otlwvy8ps2h.png" alt="Image description" width="880" height="236"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;In a future post I will show how I used this value inside my React WordPress theme to render the view using Javascript.&lt;/p&gt;

</description>
      <category>wordpress</category>
    </item>
    <item>
      <title>OAuth calls for PHP</title>
      <dc:creator>muncey</dc:creator>
      <pubDate>Thu, 22 Jul 2021 09:17:00 +0000</pubDate>
      <link>https://dev.to/muncey/oauth-calls-for-php-2kkf</link>
      <guid>https://dev.to/muncey/oauth-calls-for-php-2kkf</guid>
      <description>&lt;p&gt;If you are in a situation where you need to set up OAuth authentication on a website that you manage you might be wondering where to start and what exactly are the steps you need to follow in order to allow users to authenticate using the authentication scheme of their choice.&lt;/p&gt;

&lt;p&gt;If you are wondering what to do then worry no more, this quick article will give you the basic knowledge that you will need to set up your web site to support OAuth. I will also provide a small bit of code to get you started.&lt;/p&gt;

&lt;p&gt;Now what is OAuth? OAuth is a way for a 3rd Party web site (commonly called a provider) to tell you that a user is valid and can be allowed to access your web site. If you have ever logged on to a website using your Google or Facebook account then you have used OAuth. You can also use OAuth to request an access token to make future requests for resources from the provider server. The rest of this article will tell you the basics for enabling OAuth on a web site.&lt;/p&gt;

&lt;p&gt;To be able to support OAuth on a web site that you manage requires that you create the following four functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A page to redirect the home page to the OAuth provider.&lt;/li&gt;
&lt;li&gt;A page to accept redirects from the OAuth provider with an access code.&lt;/li&gt;
&lt;li&gt;A function to swap the access code for an access token and hold it in memory&lt;/li&gt;
&lt;li&gt;A page to log the user out by clearing out the access token from memory&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the easiest way to understand OAuth is that you are building a function that will swap an access code that the OAuth provider gives you in exchange for an access token to be used for future calls. That is basically it and you can apply this pattern to any application or OAuth provider that you work on in the future.&lt;/p&gt;

&lt;p&gt;Anyway I promised some code so I will provide two quick snippets.&lt;/p&gt;

&lt;p&gt;The first is the code used to set up a redirect to the Google OAuth 2 servers. This code will require that you have set up an OAuth client id in the Google API Console and also that you have enabled the APIs that you would like to call. The code checks for the existence of a session variable called oauth which if set means that the web site has already obtained an access token for this user and can proceed as if they were signed in to the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (!isset($_SESSION['oauth']))
 {

$url = 'https://accounts.google.com/o/oauth2/v2/auth?' .
 'scope=https%3A//www.googleapis.com/auth/drive.file&amp;amp;' .
 'access_type=offline&amp;amp;' .
 'include_granted_scopes=true&amp;amp;' .
 'response_type=code&amp;amp;' .
 'state=state_parameter_passthrough_value&amp;amp;' .
 'redirect_uri=https%3A//localhost:3000/redirect-login.php&amp;amp;' .
 'client_id=' . $client_id;
  header('Location: ' . $url);
  exit;
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next block of code will show how to swap the code (read from the query string using $code = $_GET[‘code’]) for an access token. You will need to have a $client_id and a $client_secret and also make sure the redirect_uri matches the uri of the page containing this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$url = 'https://oauth2.googleapis.com/token';

$postdata = http_build_query(
  array(
    'code' =&amp;gt; $code,
    'client_id' =&amp;gt; $client_id,
    'client_secret' =&amp;gt; $client_secret,
    'redirect_uri' =&amp;gt; 'https://localhost:3000/redirect-login.php',
    'grant_type' =&amp;gt; 'authorization_code'
  )
);

$opts = array('http' =&amp;gt; 
  array(
    'method' =&amp;gt; 'POST',
    'header' =&amp;gt; 'Content-Type: application/x-www-form-urlencoded',
    'content' =&amp;gt; $postdata
  )
);

$context = stream_context_create($opts);
$result = file_get_contents($url, false, $context);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When google is called as a post provided the $code, $client_id, $client_secret, redirect_uri are correct google will return an access token that will then be able to be passed in the authentication header of future requests to retrieve data from google.&lt;/p&gt;

&lt;p&gt;Sorry one more bit of code. This code which can run on any page on your site will get the access_token value from the oauth session variable and then pass it with files request in the Authorization header. Provided the access token remains valid google will respond by returning resources related to files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$oauth = json_decode($_SESSION['oauth']);
$token = $oauth-&amp;gt;access_token;

$url = 'https://www.googleapis.com/drive/v3/files';
$options = array('http' =&amp;gt; array(
    'method'  =&amp;gt; 'GET',
    'header' =&amp;gt; 'Authorization: Bearer '.$token
));
$context  = stream_context_create($options);
$response = file_get_contents($url, false, $context);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what do you think? Did this help you understand OAuth better? If you liked this article please comment, like or share.&lt;/p&gt;

</description>
      <category>php</category>
      <category>googlesheets</category>
      <category>oauth</category>
    </item>
    <item>
      <title>Enabling API Access for Google Sheets</title>
      <dc:creator>muncey</dc:creator>
      <pubDate>Wed, 21 Jul 2021 21:15:18 +0000</pubDate>
      <link>https://dev.to/muncey/enabling-api-access-for-google-sheets-2ini</link>
      <guid>https://dev.to/muncey/enabling-api-access-for-google-sheets-2ini</guid>
      <description>&lt;p&gt;Before you can connect to Google Sheets what you will need to do is enable API Access for Google Sheets. This involves logging on to Googles Cloud API console and enabling the API for google sheets and google drive. Once you have completed this step you can then create a OAuth client id and client secret so that your users can authenticate to your application using OAuth.&lt;/p&gt;

&lt;p&gt;Google didn’t make this step super easy but basically once you have created a new project (let me know in comments if you would like to see how to create a new project), you then need to open the APIs &amp;amp; Services page and select Library.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9ilDbjYI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hehrokfn9pwqsb8gd5aa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9ilDbjYI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hehrokfn9pwqsb8gd5aa.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
This brings up a page with all google API services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ATVRNj1Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2e4gwzpbyou2yf4j8w9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ATVRNj1Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2e4gwzpbyou2yf4j8w9.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
Run a search for sheets&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ByrWiO18--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zh3qyetm5rvat47es8jh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ByrWiO18--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zh3qyetm5rvat47es8jh.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
Once you select Google Sheets API this will bring up the enable API page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lj0xJVRb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t7nseoh8kkfdd1snt2pi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lj0xJVRb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t7nseoh8kkfdd1snt2pi.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
Clicking on Enable will enable the API for use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fiHBP7Iy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d92wbmsw8s3pw4o277fb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fiHBP7Iy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d92wbmsw8s3pw4o277fb.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
I then repeated the process for Google Drive and you can repeat this process for any API you would like to connect to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TDr3wQOD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ayz5x70hqk1io3ajoxfm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TDr3wQOD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ayz5x70hqk1io3ajoxfm.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
In my next post I will show how to create client credentials for google OAuth so that you can connect via a web application.&lt;/p&gt;

</description>
      <category>api</category>
      <category>googlesheets</category>
    </item>
    <item>
      <title>Run a local PHP web server</title>
      <dc:creator>muncey</dc:creator>
      <pubDate>Wed, 21 Jul 2021 13:10:38 +0000</pubDate>
      <link>https://dev.to/muncey/run-a-local-php-web-server-3jb9</link>
      <guid>https://dev.to/muncey/run-a-local-php-web-server-3jb9</guid>
      <description>&lt;p&gt;One of the reasons that I like using PHP is that it is an easy language to pickup and also an easy language to create a new project. I will often times use PHP as a prototyping language and once I am happy with the project move onto a more robust platform such as .NET or Java.&lt;/p&gt;

&lt;p&gt;To create a new web server using PHP is actually very easy. All that you need to do is create a file such as index.php with some php and html code in it and then run this command:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;php -S localhost:3000&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;This command will start a local web server that you can then access easily enough at &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; as per the example below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlk0zn28espejb049gzy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlk0zn28espejb049gzy.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>webserver</category>
    </item>
    <item>
      <title>Using google sheets as a database</title>
      <dc:creator>muncey</dc:creator>
      <pubDate>Fri, 16 Jul 2021 22:11:23 +0000</pubDate>
      <link>https://dev.to/muncey/using-google-sheets-as-a-database-1ekd</link>
      <guid>https://dev.to/muncey/using-google-sheets-as-a-database-1ekd</guid>
      <description>&lt;p&gt;I am planning to write a series of articles which will show how google sheets can be connected to an used as a database for an application. This article will outline the post plan and also provide an overview of how I am going to structure the application.&lt;/p&gt;

&lt;p&gt;Now I am using google sheets as a good way to prototype an application and also a lot of my user base are comfortable using google sheets as a way of managing their data.&lt;/p&gt;

&lt;p&gt;The application itself will be the my budget tool that I showed how to create in a previous set of articles. What I will be doing is enhancing that application so that it will connect to google sheets to save data there.&lt;/p&gt;

&lt;p&gt;The application URL is here if you would like to play around with the current application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://my-easy-budget.herokuapp.com/index.php"&gt;https://my-easy-budget.herokuapp.com/index.php&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am planning to write the following set of articles outlining how I build this application to save data to google sheets.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Testing and planning REST calls using Postman&lt;/li&gt;
&lt;li&gt;New pages to handle OAuth authentication&lt;/li&gt;
&lt;li&gt;Obtaining an OAuth access token from google&lt;/li&gt;
&lt;li&gt;Application design and flow&lt;/li&gt;
&lt;li&gt;Future plans&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please let me know in the comments if there is anything else you would like to see me write about.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Deploying my budget tool to Heroku</title>
      <dc:creator>muncey</dc:creator>
      <pubDate>Thu, 15 Jul 2021 13:20:18 +0000</pubDate>
      <link>https://dev.to/muncey/deploying-my-budget-tool-to-heroku-p1d</link>
      <guid>https://dev.to/muncey/deploying-my-budget-tool-to-heroku-p1d</guid>
      <description>&lt;p&gt;I thought I would just make a really quick post about deploying my budget tool to Heroku as a static web application.&lt;/p&gt;

&lt;p&gt;Heroku is a hosting platform owned by Salesforce that provides a free hosting tier.&lt;/p&gt;

&lt;p&gt;So I have created a free account on Heroku and I have then selected the New app option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W9hgxoqE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aebiyg0mjl85wbfm9rll.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W9hgxoqE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aebiyg0mjl85wbfm9rll.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next I create a pipeline called “deploy-production” and set the Deployment method to GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I-Yi3rEt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kbk38k8fiovi4l91oymj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I-Yi3rEt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kbk38k8fiovi4l91oymj.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I then connect to GitHub and select the muncey/MyBudgetFrontEnd project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6EE_mmoj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o109mhxhaio29qw5je0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6EE_mmoj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o109mhxhaio29qw5je0w.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
I setup Automatic deploys and kick of a manual deploy&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--blayyuYy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kdyfm1pxkcteuie8mx88.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--blayyuYy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kdyfm1pxkcteuie8mx88.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
Because I have not set up a buildpack yet, when I attempt to deploy to Heroku I received a no buildpack error.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m1ApqRrO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yapupyquvfzcmrxeac3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m1ApqRrO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yapupyquvfzcmrxeac3p.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
So to fix this I went to settings and added a php buildpack&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zVzuyps6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vhmb1pe8hdsj9190wv7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zVzuyps6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vhmb1pe8hdsj9190wv7j.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
I also changed index.html to be index.php without making too many other changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tvFpYdx5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ox1jgaormm1rtij38h97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tvFpYdx5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ox1jgaormm1rtij38h97.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
I was then able to deploy a branch to Heroku&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c0dgZOrX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uo584u2ie3jxli771vsk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c0dgZOrX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uo584u2ie3jxli771vsk.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
And I can access my app at url: &lt;a href="https://my-easy-budget.herokuapp.com/index.php"&gt;https://my-easy-budget.herokuapp.com/index.php&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qzp9x9NR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2z7nhk8kaqjjjs1zggnt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qzp9x9NR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2z7nhk8kaqjjjs1zggnt.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
So that is about it, I can now deploy my app up to heroku where anyone can access it. In my next article I am going to show a technique for saving and persisting the data for a user.&lt;/p&gt;

</description>
      <category>heroku</category>
      <category>php</category>
    </item>
    <item>
      <title>Creating an online budget tool 5/5</title>
      <dc:creator>muncey</dc:creator>
      <pubDate>Thu, 08 Jul 2021 20:18:07 +0000</pubDate>
      <link>https://dev.to/muncey/creating-an-online-budget-tool-5-x-hgi</link>
      <guid>https://dev.to/muncey/creating-an-online-budget-tool-5-x-hgi</guid>
      <description>&lt;p&gt;In this article I am going to add styles to my form so that it looks visually appealing with final version looking like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ej3k7ETN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ypgz2rtv7dz7s4k6ey32.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ej3k7ETN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ypgz2rtv7dz7s4k6ey32.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is still a lot of work to be performed to complete the styling phase but I want to demonstrate the techniques that I used first.&lt;/p&gt;

&lt;p&gt;The page is styled using bootstrap and is based of the bootstrap starter form:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://getbootstrap.com/docs/5.0/getting-started/introduction/"&gt;https://getbootstrap.com/docs/5.0/getting-started/introduction/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To style the form I have inserted a link to bootstrap styles into the page header&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;head&amp;gt;
    &amp;lt;!-- Required meta tags --&amp;gt;
    &amp;lt;meta charset="utf-8"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1"&amp;gt;

    &amp;lt;!-- Bootstrap CSS --&amp;gt;
    &amp;lt;link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"&amp;gt;
    &amp;lt;title&amp;gt;My Budget&amp;lt;/title&amp;gt;
    &amp;lt;link rel="preconnect" href="https://fonts.googleapis.com"&amp;gt;
    &amp;lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&amp;gt;
    &amp;lt;link href="https://fonts.googleapis.com/css2?family=Noto+Serif&amp;amp;display=swap" rel="stylesheet"&amp;gt;
    &amp;lt;link href="style.css" rel="stylesheet" type="text/css" /&amp;gt;
  &amp;lt;/head&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that I have also brought in a google font. These are customised fonts that you can use to make your site look a lot better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fonts.google.com/"&gt;https://fonts.google.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have added a navbar to the top of the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;nav class="navbar navbar-expand-lg navbar-light bg-light"&amp;gt;
  &amp;lt;div class="container-fluid"&amp;gt;
    &amp;lt;a class="navbar-brand" href="#"&amp;gt;My Budget&amp;lt;/a&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/nav&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I have enclosed the budget table in a container. A container is used to centre a group of elements on the page. If you want to have full width for a group of elements you use container-fluid.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;div class="container bg-white mt-3 rounded"&amp;gt;
      &amp;lt;div class="my-budget"&amp;gt;
        &amp;lt;table id="budgetTable" class="table"&amp;gt;
          &amp;lt;thead&amp;gt;
            &amp;lt;tr&amp;gt;
              &amp;lt;th&amp;gt;Item&amp;lt;/th&amp;gt;
              &amp;lt;th&amp;gt;Amount&amp;lt;/th&amp;gt;
              &amp;lt;th&amp;gt;Action&amp;lt;/th&amp;gt;
            &amp;lt;/tr&amp;gt;
            &amp;lt;tr&amp;gt;
              &amp;lt;td&amp;gt;&amp;lt;input placeholder="Enter item" type="text" autocomplete="off" id="newItem" class="form-control"&amp;gt;&amp;lt;/td&amp;gt;
              &amp;lt;td&amp;gt;&amp;lt;input placeholder="Enter amount" type="number" autocomplete="off" id="newAmount" class="form-control"&amp;gt;&amp;lt;/td&amp;gt;
              &amp;lt;td&amp;gt;&amp;lt;button id="addButton" type="button" class="btn btn-link"&amp;gt;Add&amp;lt;/button&amp;gt;&amp;lt;/td&amp;gt;
            &amp;lt;/tr&amp;gt;
          &amp;lt;/thead&amp;gt;
          &amp;lt;tbody&amp;gt;
          &amp;lt;/tbody&amp;gt;
          &amp;lt;tfoot&amp;gt;
          &amp;lt;/tfoot&amp;gt;
        &amp;lt;/table&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have also moved the new item form into the table so that it appears as the first row and styled the buttons using the btn btn-link class. Note as well that the container is making use of bg-white mt-3 and rounded classes which let you style an element without needing to write your own custom css.&lt;/p&gt;

&lt;p&gt;In order to use bootstrap properly you need to include some javascript which I have placed at the bottom of the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally I have added a small bit of css into the styles.css file to control the page fonts and backgrounds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
  font-family: 'Noto Serif', serif;
  background: #D3D3D3;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I fixed a bug where the disabled attribute was not being cleared when the user clicked cancel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const cancelEdit = () =&amp;gt; {
  id = 'budgetTable';

  document.getElementById('newItem').disabled = false;
  document.getElementById('newAmount').disabled = false;
  document.getElementById('addButton').disabled = false;

  document.getElementById(id).tBodies[0].innerHTML = renderRows(budgetItems);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I have shown is how easy it is to add bootstrap to a form and then to style that form so that it looks nice.  The code for this iteration can be found here:&lt;br&gt;
&lt;a href="https://github.com/muncey/MyBudgetFrontEnd/tree/style-form"&gt;https://github.com/muncey/MyBudgetFrontEnd/tree/style-form&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>javascript</category>
      <category>bootstrap</category>
    </item>
    <item>
      <title>Creating an online budget tool 4/5</title>
      <dc:creator>muncey</dc:creator>
      <pubDate>Thu, 08 Jul 2021 11:08:12 +0000</pubDate>
      <link>https://dev.to/muncey/creating-an-online-budget-tool-4-x-19gg</link>
      <guid>https://dev.to/muncey/creating-an-online-budget-tool-4-x-19gg</guid>
      <description>&lt;p&gt;The next step in creating an online budget tool is to add the ability to save data between sessions. In this case I am using local storage in the browser. It is not the most secure solution but it will demonstrates the techniques that you need to use to create a form that will save your budget.&lt;/p&gt;

&lt;p&gt;The key to being able to make is to create a global click handler for the budgetTable table which will map buttons to actions based on the className of each buttons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.getElementById('budgetTable').addEventListener('click', function($ev) {
  const idx = $ev.target.dataset.idx;
  if ($ev.target.className.indexOf('edit-button') &amp;gt; -1) {
    editBudgetItem(idx);
  } else if ($ev.target.className.indexOf('delete-button') &amp;gt; -1) {
    deleteItem(idx);
  } else if ($ev.target.className.indexOf('save-button') &amp;gt; -1) {
    save(idx);
  } else if ($ev.target.className.indexOf('cancel-button') &amp;gt; -1) {
    cancelEdit();
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The end goal of this is a form that looks like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2bOp632I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/527usisi86dd6r2cpz94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2bOp632I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/527usisi86dd6r2cpz94.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
It is obviously not styled as yet but it demonstrates the ability to add items, edit and delete items. The data is held for now in localStorage and in future I will look at setting up a back end so that the data can be held securely in a database but for now localStorage will do.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AeQwwy0U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c87mg9kqn9u5qblpdqlf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AeQwwy0U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c87mg9kqn9u5qblpdqlf.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
The lower level code to save and load the budget makes use of the window.localStorage object to get and set items. Items in local storage are held using name/value pairs and you will typically use JSON.stringify to prepare items for saving and JSON.parse to read items back. The logic is that if there is no my-budget in local storage I will create a default budget with sample data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let budgetItems = [{
  item: 'Car',
  amount: 1.00
}]

const loadBudget = (storageKey) =&amp;gt; {
  const budget = window.localStorage.getItem(storageKey);
  if (budget) {
    budgetItems = JSON.parse(budget);  
  }
}

const saveBudget = (storageKey) =&amp;gt; {
  const budget = JSON.stringify(budgetItems);
  window.localStorage.setItem(storageKey, budget);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have added two new functions renderActions and renderEditRow. The renderActions will render the edit and delete button and the renderEditRow will render a budget item row as a form with a save and cancel button. Note the use of a specific class on both which will be used in the table click handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const renderActions = (idx) =&amp;gt; {
  return `
  &amp;lt;button type="button" class="edit-button" data-idx="${idx}"&amp;gt;Edit&amp;lt;/button&amp;gt;
  &amp;lt;button type="button" class="delete-button" data-idx="${idx}"&amp;gt;Delete&amp;lt;/button&amp;gt;`
}


const renderEditRow = (data, idx) =&amp;gt; {

  return `&amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;input type="text" id="editItem" value="${data.item}"&amp;gt;&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;input type="number" id="editAmount" value="${parseFloat(data.amount)}"&amp;gt;&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
              &amp;lt;button type="button" class="save-button" data-idx="${idx}"&amp;gt;Save&amp;lt;/button&amp;gt;
              &amp;lt;button type="button" class="cancel-button" data-idx="${idx}"&amp;gt;Cancel&amp;lt;/button&amp;gt;
            &amp;lt;/td&amp;gt;
          &amp;lt;/tr&amp;gt;`
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have made a small change to renderRow to add an additional column for actions (edit/delete). Because the renderRow is also used for the totals row I also configure the function to only renderActions when idx is not null.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const renderRow = (data, idx) =&amp;gt; {
  return `&amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;${data.item}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;$${data.amount}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;${idx != null ? renderActions(idx) : '' }&amp;lt;/td&amp;gt;
          &amp;lt;/tr&amp;gt;`
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The renderRows function becomes a bit more complicated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const renderRows = (data, idx) =&amp;gt; {
  const html = [];
  for (let i=0; i&amp;lt;data.length; i++) {
    if (idx != null &amp;amp;&amp;amp; idx == i) {
      html.push(renderEditRow(data[i], i));
    } else if (idx != null &amp;amp;&amp;amp; idx != i) {
      html.push(renderRow(data[i]));
    } else {
      html.push(renderRow(data[i], i));
    }
  }
  return html.join('');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This change is to render an edit row if the user wants to edit a certain row.&lt;/p&gt;

&lt;p&gt;Next I add some utility functions to edit, save, delete and cancel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const addBudgetItem = () =&amp;gt; {
  const budgetItem = {
    item: document.getElementById('newItem').value,
    amount: document.getElementById('newAmount').value
  }
  budgetItems.push(budgetItem);
  document.getElementById('newItem').value = null;
  document.getElementById('newAmount').value = null;
}

const editBudgetItem = (idx) =&amp;gt; {
  id = 'budgetTable';

  document.getElementById('newItem').setAttribute('disabled', true);
  document.getElementById('newAmount').setAttribute('disabled', true);
  document.getElementById('addButton').setAttribute('disabled', true);

  document.getElementById(id).tBodies[0].innerHTML = renderRows(budgetItems, idx);
}

const cancelEdit = () =&amp;gt; {
  id = 'budgetTable';

  document.getElementById('newItem').setAttribute('disabled', false);
  document.getElementById('newAmount').setAttribute('disabled', false);
  document.getElementById('addButton').setAttribute('disabled', false);

  document.getElementById(id).tBodies[0].innerHTML = renderRows(budgetItems);
}

const save = (idx) =&amp;gt; {

  budgetItems[idx].item = document.getElementById('editItem').value;
  budgetItems[idx].amount = parseFloat(document.getElementById('editAmount').value);

  saveBudget('my-budget');
  renderPage('budgetTable');

  document.getElementById('newItem').setAttribute('disabled', false);
  document.getElementById('newAmount').setAttribute('disabled', false);
  document.getElementById('addButton').setAttribute('disabled', false);
}

const deleteItem = (idx) =&amp;gt; {
  const temp = [];
  for (let i=0; i &amp;lt; budgetItems.length; i++) {
    if (i != idx) {
      temp.push(budgetItems[i]);
    }
  }
  budgetItems = temp;

  saveBudget('my-budget');
  renderPage('budgetTable');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the end of each function if I change data in budgetItems I call saveBudget followed by renderPage.&lt;/p&gt;

&lt;p&gt;So this gives me a functional form which can be used for personal use. In my next article I am planning to discuss how to style the form so that it looks great and is ready for dropping into a CMS (WordPress, Wix, Joomla) of your choice.&lt;/p&gt;

&lt;p&gt;I have saved changes into a local-storage branch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/muncey/MyBudgetFrontEnd/tree/local-storage"&gt;https://github.com/muncey/MyBudgetFrontEnd/tree/local-storage&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>javascript</category>
      <category>localstorage</category>
    </item>
    <item>
      <title>Creating an online budget tool 3/5</title>
      <dc:creator>muncey</dc:creator>
      <pubDate>Thu, 08 Jul 2021 09:48:37 +0000</pubDate>
      <link>https://dev.to/muncey/creating-an-online-budget-tool-3-x-4fek</link>
      <guid>https://dev.to/muncey/creating-an-online-budget-tool-3-x-4fek</guid>
      <description>&lt;p&gt;In this short instalment I am going to demonstrate how to total all the budget items and render the totals at the bottom of the budget table.&lt;/p&gt;

&lt;p&gt;Code for this iteration has been saved to the add-totals branch available here: &lt;a href="https://github.com/muncey/MyBudgetFrontEnd/tree/add-totals"&gt;https://github.com/muncey/MyBudgetFrontEnd/tree/add-totals&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First I have made updates to the budgetTable to remove the contents of the tfoot element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        &amp;lt;table id="budgetTable"&amp;gt;
          &amp;lt;thead&amp;gt;
            &amp;lt;tr&amp;gt;
              &amp;lt;th&amp;gt;Item&amp;lt;/th&amp;gt;
              &amp;lt;th&amp;gt;Amount&amp;lt;/th&amp;gt;
            &amp;lt;/tr&amp;gt;
          &amp;lt;/thead&amp;gt;
          &amp;lt;tbody&amp;gt;
          &amp;lt;/tbody&amp;gt;
          &amp;lt;tfoot&amp;gt;
          &amp;lt;/tfoot&amp;gt;
        &amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next I have added in a calculateTotals function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const calculateTotals = () =&amp;gt; {
  let total = 0.00;
  for (let i=0; i&amp;lt;budgetItems.length; i++) {
    total+=parseFloat(budgetItems[i].amount);
  }
  return { item: 'Total', amount: total }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the use of the parseFloat function which ensures each amount is treated as a number.&lt;/p&gt;

&lt;p&gt;Then I assign the innerHTML of the tFoot element to the result of the renderRow and calculateTotals function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const renderPage = (id) =&amp;gt; {
  document.getElementById(id).tBodies[0].innerHTML = renderRows(budgetItems);
  document.getElementById(id).tFoot.innerHTML = 
              renderRow(calculateTotals());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This leaves us with a functional, but not very good looking budget tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cPSpoqCB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4bq3gb4ntlhdy43qrliq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cPSpoqCB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4bq3gb4ntlhdy43qrliq.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
In the next article I will be looking into saving the budget using local storage.&lt;/p&gt;

</description>
      <category>html</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Creating an online budget tool 2/5</title>
      <dc:creator>muncey</dc:creator>
      <pubDate>Wed, 07 Jul 2021 21:04:52 +0000</pubDate>
      <link>https://dev.to/muncey/creating-an-online-budget-tool-2-x-1nk3</link>
      <guid>https://dev.to/muncey/creating-an-online-budget-tool-2-x-1nk3</guid>
      <description>&lt;p&gt;In my last post I showed how to layout the HTML for creating an online budget tool. In this post I am going to show how to add JavaScript to make the form dynamic.&lt;/p&gt;

&lt;p&gt;First, I modify the add form at the top of the page by adding id’s to the input fields and the button. I also change the button to have a type of button to stop the form from submitting when the button is clicked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      &amp;lt;form&amp;gt;
          &amp;lt;label&amp;gt;Item&amp;lt;/label&amp;gt;
          &amp;lt;input type="text" id="newItem"&amp;gt;
          &amp;lt;label&amp;gt;Amount&amp;lt;/label&amp;gt;
          &amp;lt;input type="number" id="newAmount"&amp;gt;
          &amp;lt;button id="addButton" type="button"&amp;gt;Add&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I add an id to the table and remove the contents of the tbody.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        &amp;lt;table id="budgetTable"&amp;gt;
          &amp;lt;thead&amp;gt;
            &amp;lt;tr&amp;gt;
              &amp;lt;th&amp;gt;Item&amp;lt;/th&amp;gt;
              &amp;lt;th&amp;gt;Amount&amp;lt;/th&amp;gt;
            &amp;lt;/tr&amp;gt;
          &amp;lt;/thead&amp;gt;
          &amp;lt;tbody&amp;gt;
          &amp;lt;/tbody&amp;gt;
          &amp;lt;tfooter&amp;gt;
            &amp;lt;tr&amp;gt;
              &amp;lt;td&amp;gt;Total&amp;lt;/td&amp;gt;
              &amp;lt;td&amp;gt;$1.00&amp;lt;/td&amp;gt;
            &amp;lt;/tr&amp;gt;
          &amp;lt;/tfooter&amp;gt;
        &amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then add a renderRow function to my Javascript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const renderRow = (data) =&amp;gt; {
  return `&amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;${data.item}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;$${data.amount}&amp;lt;/td&amp;gt;
          &amp;lt;/tr&amp;gt;`
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a renderRows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const renderRows = (data) =&amp;gt; {
  const html = [];
  for (let i=0; i&amp;lt;data.length; i++) {
    html.push(renderRow(data[i]));
  }
  return html.join('');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I add a budgetItems array to the Javascript and also add an addBudgetItem function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const budgetItems = [{
  item: 'Car',
  amount: 1.00
}]

const addBudgetItem = () =&amp;gt; {
  const budgetItem = {
    item: document.getElementById('newItem').value,
    amount: document.getElementById('newAmount').value
  }
  budgetItems.push(budgetItem);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I add a renderPage function that will link everything together to produce HTML for the table body and then assign the contents of the table body using the innerHTML property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const renderPage = (id) =&amp;gt; {
  document.getElementById(id).tBodies[0].innerHTML = renderRows(budgetItems);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I add two event listeners, one called DOMContentLoaded that will call renderPage when the page loads and one being a click event on the add button to add a new item.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.addEventListener('DOMContentLoaded', function($ev) {
  renderPage('budgetTable');
});

document.getElementById('addButton').addEventListener('click', function($ev) {
  addBudgetItem();
  renderPage('budgetTable');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can now start to keep track of all my subscriptions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--asEDNPCH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oww99skvn704zqvv6r77.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--asEDNPCH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oww99skvn704zqvv6r77.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
Clicking add will add a new item to the budgetItems array and then generate html to insert into the table body.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LplrsCYA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xxj7uhcoyldtohwhuccn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LplrsCYA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xxj7uhcoyldtohwhuccn.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The biggest thing I had to remember when doing this was to use tBodies as spelt as it is case sensitive.&lt;/p&gt;

&lt;p&gt;In my next post I will show how to create the total row and will start looking at how to style the form.&lt;/p&gt;

&lt;p&gt;The code for the budget tool can be found here: &lt;a href="https://github.com/muncey/MyBudgetFrontEnd"&gt;https://github.com/muncey/MyBudgetFrontEnd&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Creating an online budget form 1/5</title>
      <dc:creator>muncey</dc:creator>
      <pubDate>Mon, 05 Jul 2021 19:58:47 +0000</pubDate>
      <link>https://dev.to/muncey/creating-an-online-budget-form-1-x-1nf0</link>
      <guid>https://dev.to/muncey/creating-an-online-budget-form-1-x-1nf0</guid>
      <description>&lt;p&gt;Keeping track of spending is an ongoing chore. It is also a good first step in getting on top of your finances and can be fun to see where all the money is going. To help in keeping track of spending I am building an online budget form that will let me enter in monthly spending and total the amounts so I have a good idea of what my normal expenses are.&lt;/p&gt;

&lt;p&gt;The first step in creating this online budget form is to set up the basic html.&lt;/p&gt;

&lt;p&gt;First part is a form containing the option to add a new item.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        &amp;lt;form&amp;gt;
          &amp;lt;label&amp;gt;Item&amp;lt;/label&amp;gt;
          &amp;lt;input type="text"&amp;gt;
          &amp;lt;label&amp;gt;Amount&amp;lt;/label&amp;gt;
          &amp;lt;input type="number"&amp;gt;
          &amp;lt;button&amp;gt;Add&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
        &amp;lt;h3&amp;gt;Items&amp;lt;/h3&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second part is to add a table that will contain the budget items.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        &amp;lt;table&amp;gt;
          &amp;lt;thead&amp;gt;
            &amp;lt;tr&amp;gt;
              &amp;lt;th&amp;gt;Item&amp;lt;/th&amp;gt;
              &amp;lt;th&amp;gt;Amount&amp;lt;/th&amp;gt;
            &amp;lt;/tr&amp;gt;
          &amp;lt;/thead&amp;gt;
          &amp;lt;tbody&amp;gt;
            &amp;lt;tr&amp;gt;
              &amp;lt;td&amp;gt;Car&amp;lt;/td&amp;gt;
              &amp;lt;td&amp;gt;$1.00&amp;lt;/td&amp;gt;
            &amp;lt;/tr&amp;gt;
          &amp;lt;/tbody&amp;gt;
          &amp;lt;tfooter&amp;gt;
            &amp;lt;tr&amp;gt;
              &amp;lt;td&amp;gt;Total&amp;lt;/td&amp;gt;
              &amp;lt;td&amp;gt;$1.00&amp;lt;/td&amp;gt;
            &amp;lt;/tr&amp;gt;
          &amp;lt;/tfooter&amp;gt;
        &amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The table is split into 3 parts thead, tbody and tfooter. And the form currently looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MX9p16qj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uau70b9at45v7r71ybst.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MX9p16qj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uau70b9at45v7r71ybst.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
Not much to look at obviously but in the coming posts I will show how to add dynamic functionality to the form and how to style the form so that it looks professional. Finally I will show how to use the form to feed into a submission to a CRM so that this could be used on the website of a financial counsellor.&lt;/p&gt;

</description>
      <category>budget</category>
      <category>html</category>
    </item>
  </channel>
</rss>
