DEV Community

Cover image for Making a Vue component: an Editable Navigation Element
Tori Pugh
Tori Pugh

Posted on • Edited on

Making a Vue component: an Editable Navigation Element

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 = ""
 }
}
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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!'
      }
    }
Enter fullscreen mode Exit fullscreen mode

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 !== '')
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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 = ""
    }
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (11)

Collapse
 
tsanak profile image
tsanak

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.

Collapse
 
teekatwo profile image
Tori Pugh

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.

Collapse
 
maccabee profile image
Maccabee

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

Collapse
 
teekatwo profile image
Tori Pugh

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?

Collapse
 
maccabee profile image
Maccabee

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.

Collapse
 
pedro profile image
Pedro M. M. • Edited

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.

Collapse
 
teekatwo profile image
Tori Pugh

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.

Collapse
 
pedro profile image
Pedro M. M. • Edited

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.

Collapse
 
coolgoose profile image
Alexandru Bucur

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

Collapse
 
rhymes profile image
rhymes

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

Collapse
 
teekatwo profile image
Tori Pugh

It really is!