diceline-chartmagnifierquestion-marktwitter-whiteTwitter_Logo_Blue

Today I Learned

How to disable WordPress parent menu link

Working with WordPress menus, I wanted a top-level navigation element to act as a trigger for a drop-down menu.

It took me some time to figure out how to make this navigation element not redirect anywhere, but it was so easy.

You need to take the following steps:

  1. Insert a custom link with any link address and label that you want.

  2. Click on the 'Edit Menu Item' and delete the link that you insert previously.

  3. Create the sub-menu with the wanted links.

  4. Save.

That's all, nothing more, nothing less.

How to create pagination for custom taxonomy in WordPress

To create a pagination for the custom taxonomy archive, you must enter the following snippet in the code:

function custom_tax_query_change( $query ) {
     if ( ! is_admin() && $query->is_tax( 'example_taxonomy_name' ) ) {
          $query->set( 'posts_per_page', 10 );
     }
}
add_action( 'pre_get_posts', 'custom_tax_query_change' );

The 'post_per_page' tell us how many posts we want to display per page. If you want more or less, you can change the value as you need.

How to dynamically display different types of cards based on a pattern

Let's say you have an array of cards inside which you want add more items.

Example: [card, card, card, itemOne, card, card, itemTwo, ...]

For this we will create another empty array that we will work with:

let posts = [];

const itemsTemplate = [itemOne, itemTwo];
let items = [itemOne, itemTwo];

for (let i = 0; i < cards.length; i++) {
  posts.push(cards[i]);

  if (i !== 0) {

    //next index is multiple of 3
    if ((i + 1) % 5 === 3) {
      if (items.length === 0) {
        items = itemsTemplate;
      }
      posts.push(items.pop());
    }

    //next index is multiple of 5
    if ((i + 1) % 5 === 0) {
      if (items.length === 0) {
        items = itemsTemplate;
      }
      posts.push(items.pop());
    }
  }
}

How to display all the categories for blog posts from WordPress

To display all the categories of posts in WordPress and the active category, you need to go through the following steps:

  1. Get the current category:
<?php $current_category = get_queried_object(); ?>
  1. Get all the categories:
<?php $categories = get_categories(); ?>
  1. Use a loop to iterate all the categories:
<?php foreach ($categories as $category):
    $category_link = get_category_link($category); ?>
 <a class="blog__category <?php echo $current_category->cat_name ===
 $category->cat_name
     ? "is-active"
     : ""; ?>" 
  href="<?php echo $category_link; ?>">
  <?php echo $category->name; ?>
  </a>
<?php endforeach; ?>

Now you can see all the categories that are used on your blog posts and the current selected category.

Use a hidden iframe to submit a form via Javascript for legacy applications

To prevent the page from being redirected or refreshed, we will create a javascript iframe inside which we will create the form:

methods: {
  hiddenIframe(fields) {
    let customHiddenIframeName = 'iframeId'
    if (!document.getElementById(customHiddenIframeName)) {
      let iFrame = document.createElement('iframe')
      iFrame.id = customHiddenIframeName
      iFrame.name = customHiddenIframeName
      iFrame.src = 'about:blank'
      iFrame.style.display = 'none'
      document.body.appendChild(iFrame)
     }

     let form = document.createElement('form')
     form.method = 'POST'
     form.action =
        'https://webto.salesforce.com/servlet/servlet.WebToCase?encoding=UTF-8'
     form.setAttribute('target', customHiddenIframeName)
      
     for (let fieldName in fields) {
       let input = document.createElement('input')
       input.name = fieldName
       input.value = fields[fieldName]
       input.id = fieldName
       input.setAttribute('type', 'hidden')
       form.appendChild(input)
     }

     document.body.appendChild(form)
     form.submit()
  }
},

created() {
  this.hiddenIframe({
    email: 'example@test.com',
    name: 'John Doe',
    subject: 'Form',
    description: 'Form description'
   })
}

How to create a social media share button

If you want to share your site on social media, you can create different types of buttons that can do this:

For Facebook:

<a href="https://m.facebook.com/sharer.php?u=yourwebsite.com" title="Share on Facebook" class="social__link" target="_blank" rel="external noopener">Share on Facebook</a>

For LinkedIn:

<a href="https://www.linkedin.com/shareArticle?mini=true&url=yourwebsite.com" title="Share on LinkedIn" class="social__link" target="_blank" rel="external noopener">Share on LinkedIn</a>

For Twitter:

<a href="https://twitter.com/share?url=yourwebsite.com" title="Share on Twitter" class="social__link" target="_blank" rel="external noopener">Share on Twitter</a>

Instagram currently doesn’t allow you to share a photo or video from another website – you can only upload photos/videos directly from your mobile device. Since there is no sharing mechanism, there is no way for us to include a button that will share your content to Instagram.

Netlify CMS relation widget with a list

As I was using Netlify CMS, I wanted to use a relation widget that would dynamically add content depending on the other collection title field.

It's really easy to do this, but I struggled a little bit with it because the documentation wasn't so explicit, and I could not find something useful on the internet.

So here's how you do it:

This is the collection that will have dynamic content, depending on how many call-to-actions you'll create.

  - name: 'call-to-action'
    label: 'Call to action'
    path: "{{slug}}"
    create: true
    folder: 'content/call-to-action'
    fields:
      - {label: 'CTA Category', name: 'ctaCategory', widget: 'string'}
      - label: 'CTA Content'
        name: 'cta'
        widget: 'list'
        fields:
        - {label: 'Title', name: 'title', widget: 'string' }
        - {label: 'Content', name: 'content', widget: 'string' }
        - {label: 'cover', name: 'cover', widget: 'image', media_folder: '/assets/images/covers/'}
        - label: 'Button'
          name: 'button'
          widget: 'object'
          fields:
          - {label: 'url', name: 'url', widget: 'string'}
          - {label: 'title', name: 'title', widget: 'string'}

And now we have to link this collection to a field in the blog collection. The field in the blog collection looks like this :

- { label: 'Call to action category',
 name: 'ctaCategory',
 widget: 'relation', 
 collection: 'call-to-action', 
 search_fields: ["ctaCategory"], 
 value_field: "cta.*", 
 display_fields: ["ctaCategory"]}

The search will be done depending on what "ctaCategory" you select, and to get all the items in the list widget from the "call-to-action" collection, we need to specify in the value field that we want to get every item, and you do this with ".*".

That's all .

React custom hook for form state management

Goal: defining a custom useForm hook for managing the state of our forms. This can have some initial state, mainly used for precompleting update forms, but can also provide empty form fields. 




Problem: when we want to operate on a precompleted form, altough we pass all the data for our fields, the form is not being precompleted and its fields are empty.

export default function useForm(initial = {}) {
  const [inputs, setInputs] = useState(initial);



  const handleChange = (e) => {
    let { value, name, type } = e.target;

    if (type === 'number') {
      value = parseInt(value);
    }
    if (type === 'file') {
      [value] = e.target.files;
    }

    setInputs({
      ...inputs,
      [name]: value,
    });
  };

  const resetForm = () => {
    setInputs(initial);
  };

  const clearForm = () => {
    const blankState = Object.fromEntries(
      Object.entries(inputs).map(([key, value]) => [key, ''])
    );

    setInputs(blankState);
  };

  return {
    inputs,
    clearForm,
    resetForm,
    handleChange,
  };
}


This is what happens:

  1. We pass an initial state object which is undefined (server-side rendered) until the GQL query loads.
  2. This initial object (which is undefined) populates the form fields making them empty.
  3. After the query loads, the initial state object is repassed to the useForm hook, but the DOM is not rerendered => a possible solution is to make use of the useEffect() hook for forcing rerendering.
  4. We cannot watch for changes directly on the initial object and reassign it using setInputs, because it triggers the useEffect callback once again and again and again when altering its value, causing an infinite loop.
  5. The solution is to watch for changes on a string joined by the values of the initial object. When that changes from undefined to the GraphQL query results, the useEffect callback is called and it initializes and rerenders the fields correspondingly.


An example implementation could be:

const initialValues = Object.values(initial).join('');
useEffect(() => {
  setInputs(initial);
}, [initialValues]);



Now the form precompletion works fine using our custom useForm() hook.

Vuex composition helpers utility package

One big advantage of the Composition API over the Options API is that it lets us group our variables and methods how we want. Most of the time we group them together by feature/functionality, as they use/call one another and it's convenient to have them grouped together this way.

For all apps where we use Vuex for state management, we probably take advantage of the mapState, mapGetters, mapActions and mapMutations binding helpers. Unfortunately, this does not work as expected with the Composition API. But there is also a way of how we can replicate this behavior and also maintain the structure of our components as described at the beginning as an advantage.

We have to install the vuex-composition-helpers utility package:

npm i vuex-composition-helpers@next

and use it as follows in our component:

<script setup lang="ts">

import { useState, useActions, useGetters, useMutations } from 'vuex-composition-helpers'

const { globalMessage, onlineUsers } = useState(['globalMessage', 'onlineUsers'])
const { getGlobalMessage } = useGetters(['getGlobalMessage'])
const { UPDATE_MESSAGE, ADD_USER } = useMutations(['UPDATE_MESSAGE', 'ADD_USER'])
const { updateMessage, addOnlineUser } = useActions(['updateMessage', 'addOnlineUser'])


</script>