Vue.js same form for add and edit
Asked Answered
E

2

6

I'm a rookie on vue.js and I'm trying to extend some tutorials a completed. Been fighting with this three hours now and I'm frustrated. FYI, I'm using firebase but I'm not sure it really matters here.

So, I have a CRUD app for listing movies (I told you it was basic!). There is a form at the top of the page where you can add movies, and a table below it, where the new registries are listed. This works well.

I added Edit and Delete buttons to each row on the table. The delete function works. But the Edit function is the problem.

I'd like to use v-if on the initial form, to trigger different methods (save, edit) and show different buttons (Add, Save, Cancel).

I'm not sure how to access the objects to do this, I tried a couple of things and the v-if says the object is not defined.

thank you for reading, please ask anything you need.

import './firebase' // this has my credententials and initializeApp
import Vue from 'vue'
import App from './App'
import VueFire from 'vuefire'

Vue.use(VueFire)
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  template: '<App/>',
  components: { App }
})
<template>
  <div id="app" class="container">
    <div class="page-header">
      <h1>Vue Movies</h1>
    </div>
 
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3>Add Movie</h3>
      </div>
 
      <div class="panel-body">

        <div v-if="!isEditing">
          <form id="form" class="form-inline" v-on:submit.prevent="addMovie">
            <div class="form-group">
              <label for="movieTitle">Title:</label>
              <input type="text" id="movieTitle" class="form-control" v-model="newMovie.title">
            </div>
            <div class="form-group">
              <label for="movieDirector">Director:</label>
              <input type="text" id="movieDirector" class="form-control" v-model="newMovie.director">
            </div>
            <div class="form-group">
              <label for="movieUrl">URL:</label>
              <input type="text" id="movieUrl" class="form-control" v-model="newMovie.url">
            </div>
            <input type="submit" class="btn btn-primary" value="Add Movie">
          </form>
        </div>

        <div v-else>
          <form id="form" class="form-inline" v-on:submit.prevent="saveEdit(movie)">
            <div class="form-group">
              <label for="movieTitle">Title:</label>
              <input type="text" id="movieTitle" class="form-control" v-model="movie.title">
            </div>
            <div class="form-group">
              <label for="movieDirector">Director:</label>
              <input type="text" id="movieDirector" class="form-control" v-model="movie.director">
            </div>
            <div class="form-group">
              <label for="movieUrl">URL:</label>
              <input type="text" id="movieUrl" class="form-control" v-model="movie.url">
            </div>
            <input type="submit" class="btn btn-primary" value="Save">
            <button v-on:click="cancelEdit(movie['.key'])">Cancel</button>
          </form>
        </div>

      </div>

    </div>
 
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3>Movies List</h3>
      </div>
      <div class="panel-body">
        <table class="table table-stripped">
          <thead>
            <tr>
              <th>Title</th>
              <th>director</th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="movie in movies">

              <td>
                <a v-bind:href="movie.url" v-bind:key="movie['.key']" target="_blank">{{movie.title}}</a>
              </td>
              <td>
                {{movie.director}}
              </td>
              <td>
                <button v-on:click="editMovie(movie)">Edit</button>
              </td>
              <td>
                <button v-on:click="removeMovie(movie)">Remove</button>
              </td>

            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</template>

<script>

import { moviesRef } from './firebase'

export default {
  name: 'app',
  firebase: {
    movies: moviesRef
  },
  data () {
    return {
      isEditing: false, // maybe this helps?
      newMovie: {
        title: '',
        director: '',
        url: 'http://',
        edit: false // or maybe this??
      }
    }
  },
  methods: {
    addMovie: function() {
      moviesRef.push( this.newMovie )
      this.newMovie.title = '',
      this.newMovie.director = '',
      this.newMovie.url = 'http://'
      this.newMovie.edit = false
    },
    editMovie: function (movie){
      moviesRef.child(movie['.key']).update({ edit:true }); // can't access this one with v-if, not sure why
      //this.newMovie = movie;
    },
    removeMovie: function (movie) {
      moviesRef.child(movie['.key']).remove()
    },
    cancelEdit(key){
      moviesRef.child(key).update({ edit:false })
    },
    saveEdit(movie){
      const key = movie['key'];
      moviesRef.child(key).set({
        title    : movie.title,
        director : movie.director,
        url      : movie.url,
        edit     : movie.edit
      })
    }
  }
}

</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
Ehlers answered 15/12, 2017 at 4:46 Comment(0)
C
5

You should change the isEditing to true when the Edit button clicked, and you should define the data movie.

editMovie: function (movie){
  ...
  this.movie = Vue.util.extend({}, movie); // deep clone to prevent modify the original object
  this.isEditing = true;
},
Cnossus answered 15/12, 2017 at 5:21 Comment(3)
thanks for your answer my method looks like this now editMovie: function (movie){ moviesRef.child(movie['.key']).update({ edit:true }); //this.newMovie = movie; this.movie = Vue.util.extend({}, movie); // deep clone to prevent modify the original object this.isEditing = true; }, but on the console I see "Uncaught ReferenceError: Vue is not defined"Ehlers
you haven't import Vue in .vue file. import Vue from 'vue'Cnossus
I have " import Vue from 'vue' " in my main.js file. Shouldn't that bee enough? Anyway, what really helped was adding movie to the data object. Now my v-if works :) I didn't use isEditing, but movie.edit.Ehlers
E
1

As suggested in the comments (by Ben), I added the movie declaration to the initial data object. So now it looks like this:

data () {
    return {
      isEditing: false,
      newMovie: {
        title: '',
        director: '',
        url: 'http://',
        edit: false
      },
      movie: {
        edit: false
      }
    }
  },

Now v-if works just fine, like this:

<div v-if="!movie.edit">

"is Editing" was no longer necessary so I removed it.

Ehlers answered 15/12, 2017 at 19:16 Comment(1)
I have done something like this a couple of times but I'm really not sure it's the best way to go. If the structure of your movie object changes then you have to come back and update the structure here. You can't quickly drop this component into a new project with a different model as you'll need to change the structure again. You could pass in the 'prototype' of the object in order to abstract it out but that seems like it could cause problems. Maybe the solution is to simply use different forms for create and edit, and hide them conditionally? I have 39 characters left - a perfect comment!Schwaben

© 2022 - 2024 — McMap. All rights reserved.