loading...
Cover image for Making a Vue component: an Editable Navigation Element

Making a Vue component: an Editable Navigation Element

teekatwo profile image Tori Pugh Updated on ・5 min read

I had an interesting idea to create a navigation that for simplicity I’ll call an editable navigation. I wanted this navigation to be easy to use. When you fill out the form then a link gets added. You can delete links, edit the content of a link, and change the order of the links.

To start this project I broke it down into 3 phases: add links, delete links, and move links. As I went through them though, I felt these phases were a bit too vague. I had overlooked the important details that make a product usable. The nuances behind making something like adding seem so simple, error-handling and warnings. Without these I could create a very poor experience. As such, I added another phase of error handling but could have easily added more phases to encompass a smoother experience.

Phase 1: Add Links

Phase 1 Codepen: The first phase of my navigation creation was developing a function to add links from input data. This was a simple matter of creating an empty array to place the new links into and when the “Add to Navigation” button is pressed it adds the content of the inputs into the array. This in turn would add a new link to the navigation.

It’s probably more simplistic than I intended. There is no safeguard against either input being blank and triggering the function or any error message if an input is blank telling the user it needs to be filled out. I’d fix this later on, the first thought was to make this function work.

The tough part about this code was a problem with the nested arrays storing the data. I initially couldn’t get the information pushed into the array, they just didn't show up. When I wrote it more literally [{ text: text, url: url }] as a nested array it worked.

addLink: function() {
  var text = this.newLink.trim()
  var url = this.newURL.trim()
  if (text) {
   this.links.push({ text: text, url: url })
   this.newLink = ""
   this.newURL = ""
 }
}

Phase 2: Deleting Links

Phase 2 Codepen: The next step was to create a function that would take into account the index of the link you clicked and then delete that object from the array.

 

removeLink: function(index) {
  this.links.splice(index, 1)
}

Phase 3: Error Handling

Phase 3 Codepen: This started as a need to add error handling (warnings and stopping the data from being added to the array). It than morphed into adding functionality for pressing enter to trigger the function. If you pressed enter on the last input it would trigger the add function. I found myself wanting to finish filling out the last input and just press enter expecting it to work and it didn’t.

addLink: function() {

      var text = this.newLink.trim()
      var url = this.newURL.trim()

      if ((text !== '') && (url !== '')) {
        this.isEmpty = false
        this.links.push({ text: text, url: url })
        this.newLink = ""
        this.newURL = ""

      } else {
        this.isEmpty = true
        this.error = 'Complete all boxes!'
      }
    }

This function is pretty straight forward in it's concept. The reason behind the logic is to make the text-input content and url-input content a variable and then check if either variable is empty. If so, then the function will make the variable isEmpty true which shows the error box with the message. The array push will not happen and the links will not be added.

I did make a few mistakes in this function. With the original logic statement it wasn’t properly triggering the function. It was checking only if variable text existed and if variable url was empty. The issue came because I wanted to check if the inputs are empty and not that they exist. The function would trigger if the url was not empty and text was empty. This was not what I wanted to happen. This would push a link with no title and would be a blank space.

if ((text && url !== '')

The other issue I ran into was trying to be too fancy and have the isEmpty toggle instead of explicitly state true or false. Whenever the logic failed it would keep toggling the error message on and off regardless if there was still an error. In this updated version, the variable is always true if there is an content in the inputs and will be false if the inputs are not empty.

These 3 phases went by quickly, they took me the better portion of a day. I was stuck on phase 3 the most, it was a classic case of needing to simplify and not be so fancy.

Phase 4 — Editing Links

Phase 4 Codepen: This has been my toughest challenge yet! I was initially concerned about the mechanism to make the changes and settled on using a modal.

  <transition name="fade">
    <div class="editMenu" v-show="isEditing">
      <button class="close" @click="hideForm">X</button>
      <h3>Edit Link</h3>
      <label for="" >Link Title</label>
      <input v-model="editLink" @keyup.enter="editLinks" type="text">
      <label for="">Link URL</label>
      <input v-model="editURL" @keyup.enter="editLinks" type="text">
    </div>
  </transition>

I created a simple one by using a transition and v-show. The transition creates a smooth fade in animation and the v-show activates the modal coming into view when a button is pressed.

showForm: function(index) {
  this.isEditing = true
  this.editingIndex = index
  this.editURL = this.links[index].url
  this.editLink = this.links[index].text
}

The editing part had been a trouble spot. After assistance from Roel Nieskens, he solved this crucial problem for me. The answer to making the edits specific to the link that was pressed is to take the index of the link and save that to a data variable. This would be done when the showForm function is being run so that the index value, which is now a variable of editingIndex, is available for the next function.

editLinks: function(){
      var text = this.editLink.trim()
      var url = this.editURL.trim()
      Vue.set(this.links, this.editingIndex, { text: text, url: url })
      this.isEditing = false
      this.editLink = ""
      this.editURL = ""
    }

This function takes the information that is in the inputs and pushes them back to their array. This is where Vue.set() comes in handy.

Vue.set(object, key, value) works like this. The object is where I want the changes to be, the links array. The key is the index of the particular link that has been clicked. The value is the content of the inputs.

Phase 5 - Rearranging Links

This final phase will be centered around making the links change position in the navigation. I'm not sure where to begin with making this work and am unsure whether to use an outside extension or import something to assist with this. I'll try and build something first and then get more complex from there.

Posted on by:

teekatwo profile

Tori Pugh

@teekatwo

UI/UX Developer working within a range from emails, design and development. Dabbles with Wordpress. Working on React and related technologies at the moment.

Discussion

markdown guide
 

Good job!
You can also check v-model modifiers: vuejs.org/v2/guide/forms.html#trim

Trim for example can be used like this:
v-model.trim="editLink"
and you dont have to worry about untrimmed data in the editLinks function.

For rearranging the links you can check out:
github.com/SortableJS/Vue.Draggable
I have tried it and it was easy to use.

 

Nice! I can't wait to try it out and cut down on some of my javascript.

I saw that github and was thinking of giving it a try. Wasn't sure to try and code on my own or use someone's package. Either way I'll look into it.

 

This is great!

Just one comment about adding links on Enter. I've seen many people forget, especially in JS land, that there is a form element. You can add the function call as the submit handler and you have Enter by default.

codepen.io/maccabee/pen/yvWoLd

 

Great point! I was working so hard on other examples that I'd seen and forgot I could use a form.

Is my example practical for a form or does it not matter? If this was a live project would I have to worry about sanitation and other things with submitting?

 

Forms are a logical grouping of inputs. Yours is pretty practical use as it reduces manually event listening. For the modern JS frameworks, creating SPAs, they make less sense as you're mostly making AJAX requests anyway.

For non-SPA Applications, they're very useful as you can add action and method attributes and the default submitting behavior then sends it to the server.

For live projects, HTML5 has native validations for inputs, the-art-of-web.com/html/html5-form..., though I can't recall at the moment if they require the form tag.

Sanitation would need to be done manually though.

 

I really love Vue. It's ridiculously faster than Angular and quite simpler than React but gosh, Vue —even on its latest version— is so tailored to ES5 that I always end up using React. I wish Evan would release a Vue 3 version based on ES6.

 

Interesting, I didn't know its more tailored to ES5. I just use it because its what I'm used to so my mind goes to it automatically. I need to work on transitioning into ES6.

 

Yeah. Due to how it works (writing pure javacript objects), it forces you to write code like the following:


var app = {
    data: {},
    methods: {
       addLink: function() {},
       removeLink: function() {}
    }
}

instead of sth like this:


class App {

    addLink() {
    }

    removeLink() {
    }

}

But well. Turns out it's really hard for Vue to fit with ES6 since it uses plain js objects by design, so I understand the Evan's decision and that's why I say that Vue is tailored to ES5.

I guess I just love ES6 because it's closer to other languages and it seems to me that ES6 is kind of cleaner than ES5.

 

I'd also recommend vuelidate to handle the fields validation.

 

It's amazing how powerful Vue is! :-)