Skip to main content

5 posts tagged with "web"

View All Tags

ยท 2 min read

Motivationโ€‹

Building a simple table of reference for the forgetful๐Ÿ˜‚

Based on:



| Hook | Usage |
|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| useState | const [count, setCount] = useState(0); |
| useEffect | useEffect(() => {<br/> console.log("run when mounted & when state changes")<br/>})<br/><br/>useEffect(() => {<br/> console.log("run once when mounted")<br/>},[])<br/><br/>useEffect(() => {<br/> console.log("run when state changes")<br/>},[state])<br/><br/>useEffect(() => {<br/> console.log("set tear down function");<br/> return () => console.log("run when tear down");<br/>}) |
| useContext | // share data without passing props<br/>// create<br/>const data = {state:'happy'}<br/>const DataContext = createContext(data);<br/><br/>// wrap<br/>const App = () => {<br/> \<DataContext.Provider value={data.state}><br/> \<ChildComponent /><br/> \</DataContext.Provider><br/>}<br/><br/>// use<br/>const ChildComponent = () => {<br/> const data = useContext(DataContext);<br/> return \<p>{state}\</p>;<br/>} |
| useRef | // for mutable state that does not re-render UI<br/>const count = useRef(0);<br/>count.current++; <br/><br/>// for element from the DOM<br/>const myBtn = useRef(null);<br/>const click = () => myBtn.current.click();<br/>return (\<button ref={myBtn}>\</button>); |
| useReducer | // dispatch actions to reducer function<br/>const reducer = (state, action) => {<br/> if (action.type === 'increment') { // or switch<br/> return state + 1;}<br/>}<br/><br/>const [state, dispatch] = useReducer(reducer, 0);<br/><br/>return (<br/> \<button onClick={() => dispatch({type: 'increment'})}>+\</button><br/>); |
| useMemo | // for expensive computation to get return values<br/>useMemo(() => {<br/> return count \*\* 2; // expensive<br/>}, [count]) // recompute when count changes |
| useCallback | // for functions<br/>const showCount = useCallback(() => {<br/> console.log(\`change only when \${count} changes\`);<br/>}, [count])<br/><br/>return (<br/> \<div handler = {showCount}>\</div>;<br/>) |

ยท 2 min read

Refactoring JavaScript is a book by Evan Burchard, about turning bad code into good code. Since I am going to delve a little deeper into JavaScript in the upcoming months, I thought I could learn from books that actively use JavaScript examples so that I get more familiar with the language. I am about halfway through the book and would like to give some summary and thoughts before I forget them.

The first few chapters lay out the foundation that refactoring is about improving code quality while preserving existing behavior. The author empathized on the importance of testing and having tools to help verify that the code works as intended. He went on to explain some intricacies of JavaScript and the fundamentals of testing. Though the content could be common knowledge to people who aren't beginner coders, I thought that the intense focus was on point. Quite significant efforts were given to drill the following concept into a reader's mind:

Avoid refactoring without tests!

Here is a flow chart that the author used to illustrate decisions about test, refactor or implement. You can tell from this diagram that refactoring is a small part of the decision process and it relies heavily on having testing in place. coding.png

Another major discussion in the first part of the book is on understanding functions as six basic components and the author provided some general advice for each of them:

  • Bulk
    • keep lines of code and complexity low in each function
  • Inputs
    • could be explicit, implicit or non-local, but prefer explicit over non-local
  • Outputs
    • avoid returning a null or undefined, or returning different types of values
  • Side effects
    • keep to a minimal or nonexistent
  • this: the implicit input to functions
    • have it well-defined when possible
  • Privacy
    • it necessarily impacts access in JavaScript

Author: Evan Burchard Author's interesting personal site

Link: Book pdf

ยท 6 min read

Motivationโ€‹

Dealing with WordPress again. What I want to achieve is for the documents(PDFs, Images etc) that I have on a WordPress site, the visitors will have to key in their information in order to download them. This way, the number of downloads per document(publication, in my case) and also the details of people who are downloading the documents can be recorded and analysed later on.

Processโ€‹

My initial idea was to look for a free backend service which would serve to process API calls from my front-end. On the pages where I display the documents, I will have a form that collects visitor information before allowing download to happen. When information is collected, download will initiate and an API call will be made to communicate with the backend.

Front-end can be make up of a simple HTML form + Javascript for any data preprocessing. The issue I had was that even if I am able to find a free backend service, the POST API call that I make to that backend will contain authentication keys. Leaving API keys in plain javascript is definitely not a good idea. Although, I must say that there are many interesting Database-as-a-service that caught my attention. One of them is Quickmetrics. It provides free 10000 events per day and 5 metrics to track. No time limit and no credit card required. I will definitely check this out in the future. The documentation also seems to be straight to the point.

Ok, so sending request from Javascript may not be a good idea. The way to go will then be via PHP. There is plenty of opportunities to write PHP in WordPress and my first instinct was to check if there is a plugin available. I did not manage to find suitable ones and those form service plugins are mostly limited in their free versions. So the next step for me was to download a plugin that I used before to easily insert PHP code into a page: Post Snippets. It makes creating shortcodes fairly easy. Also, even if PHP is not used, it is still a good way to write some HTML/Javascript code that can be used in different pages.


My PHP code to render the front-end form is as follows:

Part 1: Copy and edit a html form template from W3 Schoolโ€‹
echo "<h3>Please tell us about you before downloading</h3>";

$html= <<<HTML
<form id="toolkit" method="post" action="">
<label for="name">Name:</label>
<input
type="text"
id="name"
name="name"
placeholder="e.g John"
required
maxlength="100"
minlength="1"
/>
<label for="email">Email:</label>
<input
type="email"
id="email"
name="email"
placeholder="e.g John@example.com"
required
/>
<label for="organisation">Organisation:</label>
<input
type="text"
id="organisation"
name="organisation"
placeholder="e.g Google"
maxlength="100"
minlength="1"
required
/>
<input type="hidden" id="publication" name="publication" value="" />
<input type="hidden" name="action" value="addDownload" />
<input type="submit" />
</form>
HTML;

echo $html;
Part 2: Copy and add in the style for the html form as wellโ€‹
$style = <<<STYLE
<style>
/* Style inputs, select elements and textareas */
input[type="text"],
select,
input[type="email"] {
width: 100%;
padding: 12px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
resize: vertical;
}
/* Style the label to display next to the inputs */
label {
padding: 12px 12px 12px 0;
display: inline-block;
}
/* Style the submit button */
input[type="submit"] {
background-color: #4caf50;
color: white;
padding: 12px 20px;
margin-top: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
float: right;
}
/* Style the container */
.container {
border-radius: 5px;
background-color: #f2f2f2;
padding: 20px;
}
/* Floating column for labels: 25% width */
.col-25 {
float: left;
width: 25%;
margin-top: 6px;
}
/* Floating column for inputs: 75% width */
.col-75 {
float: left;
width: 75%;
margin-top: 6px;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
/* Responsive layout - when the screen is less than 600px wide, make the two columns stack on top of each other instead of next to each other */
@media screen and (max-width: 600px) {
.col-25,
.col-75,
input[type="submit"] {
width: 100%;
margin-top: 0;
}
}
</style>
STYLE;

echo $style ;
Part 3: Add in JS logic to handle form submitโ€‹

Two things I did here:

  • Submit the completed form via Ajax to "/wp-admin/admin-ajax.php" (my backend)
  • Trigger auto download of document by simulating clicking of downloable link to the document
$js = <<<JS
<script>
jQuery("#toolkit").submit(ajaxSubmit);
const submitBtn = document.querySelector("input[type='submit']");

// add title of publication as part of form
const title = document.querySelector(".blog-post-detail .inner h2").innerText;
const publication = document.querySelector("#publication");
publication.value = title;

function ajaxSubmit() {
// handle storing of data
var newDownloaderForm = jQuery(this).serialize();
jQuery.ajax({
type: "POST",
url: "/wp-admin/admin-ajax.php",
data: newDownloaderForm,
success: function (data) {
console.log(data);
submitBtn.value = "Thank You";
submitBtn.style.backgroundColor = "grey";
submitBtn.disable = true;
},
error: function (error) {
console.log(error);
submitBtn.value = "Something is wrong...please try again";
submitBtn.style.backgroundColor = "grey";
submitBtn.disable = true;
},
});

// handle auto downloading of files
const link = document.createElement("a");
link.href ="{link}";
link.download = "";
link.dispatchEvent(new MouseEvent("click"));

return false;
}
</script>
JS;

echo $js;

So that's all for the front-end. Not the best way to do things but it does the job. The last part of the puzzle is to setup the backend to receive the form and store data accordingly.

I honestly do not know WordPress well enough to find an alternative. So here, I will add my backend processing code in the current theme's functions.php file (accessible from Appearance -> Theme Editor -> functions.php). I am aware that if the theme changes, this code will no longer work but oh well...

Part 4: Add in PHP function to be triggered to save form dataโ€‹
// Add to the bottom of functions.php
// Added to deal with tracking of document downloads
// Every download will add a post of post type: downloader
// and it includes the form data
// https://stackoverflow.com/questions/43557755/how-to-call-ajax-in-wordpress
wp_enqueue_script('jquery');

function update() {
$new_post = array(
'post_title' => sanitize_text_field($_POST['publication']),
'post_status' => 'publish',
'post_author' => 1,
'post_type' => 'downloader'
);

// Insert the post into the database
$new_post_id = wp_insert_post( $new_post );
update_field('name', sanitize_text_field($_POST['name']) , $new_post_id);
update_field('email', sanitize_email($_POST['email']), $new_post_id);
update_field('organisation', sanitize_text_field($_POST['organisation']), $new_post_id);
update_field('publication', sanitize_text_field($_POST['publication']), $new_post_id);
}

// this is invoked when ajax call from front-end JS is invoked
// because of <input type="hidden" name="action" value="addDownload" />
// the ajax action(function) to call is specified in the form to be named "addDownload"
function addDownload() {
update();
echo "done";
}
add_action('wp_ajax_addDownload', 'addDownload');
add_action('wp_ajax_nopriv_addDownload', 'addDownload');

This is by no means a difficult task but I am really happy to actually know how to make an ajax call within WordPress.

Achievement unlocked!