redshift
redshift

Reputation: 5217

VueJS Form does not revert to original after cancel

I have an app where a user can manually delete text and file attachments in a post. It's working fine and the user can delete text and attachments in the post, however, the problem is when the user selects the CANCEL button -- the attachments are still showing the edited attachment items. The desired outcome is that the attachments are reverted to the original list.

Anyone have any advice on how I can fix this?

I have a demo set up here: Codesandbox (click POSTS --> POST ID --> EDIT POST --> delete an attachment --> CANCEL EDIT )

I am using props to pass data to a child component:

Parent component named PostDetail.vue:

Template:

<section v-if="editPostFormIsVis">
      <EditPost
        :post="post"
        @update="editPostFormIsVis=false"
        @cancel="editPostFormIsVis = false"
        :attachmentsArray="attachmentsArray"
        @deleteMediaAttachment="removeItem"
      />
    </section>

Script:

import attachments from "../../public/attachments.json";
import EditPost from "@/components/EditPost.vue";
export default {
  components: {
    EditPost
  },
  data() {
    return {
      post: {},
      editPostFormIsVis: false,
      attachmentsArray: attachments
    };
  },
  created() {
    this.getPost();
  },
  methods: {
    getPost() {
      axios
        .get(
          "https://jsonplaceholder.typicode.com/posts/" + this.$route.params.id
        )
        .then(resp => {
          this.post = resp.data;
        })
        .catch(err => {
          console.log(err);
        });
    },
    editPost() {
      this.editPostFormIsVis = true;
    },
    removeItem: function(item) {
      this.attachmentsArray.attachments.splice(item, 1);
    }
  },
  computed: {
    emailAttachmentsFileNames() {
      if (this.attachmentsArray.emailAttachments) {
        const emailAttachmentsFileNameArray = this.attachmentsArray.emailAttachments.map(
          item => {
            const tokens = item.split("/");
            return tokens[tokens.length - 1];
          }
        );
        return emailAttachmentsFileNameArray;
      } else {
        return null;
      }
    },
    attachmentsFileNames() {
      if (this.attachmentsArray.attachments) {
        const attachmentsFileNameArray = this.attachmentsArray.attachments.map(
          item => {
            const tokens = item.split("/");
            return tokens[tokens.length - 1];
          }
        );
        return attachmentsFileNameArray;
      } else {
        return null;
      }
    }
  }
};

The child component named EditPost.vue:

Template:

<ul>
          <li>Media Attachments
            <ul v-if="attachmentsFileNames && attachmentsFileNames.length">
              <li v-for="(attachmentFileName, index) in attachmentsFileNames" :key="index">
                <a href="
                    #
                  ">{{ attachmentFileName }}</a>&nbsp;
                <button
                  @click.prevent="$emit('deleteMediaAttachment', attachmentFileName)"
                >Delete me!</button>
              </li>
            </ul>
          </li>
        </ul>

Script:

export default {
  name: "EditPost",
  props: {
    post: {
      type: Object
    },
    attachmentsArray: {
      type: Object
    }
    // filenames: {
    //   type: Array
    // }
  },
  created() {
    // clone issue without including observers and reactivity
    this.postBeforeEdit = Object.assign({}, this.post);
    this.attachmentsArrayBeforeEdit = Object.assign({}, this.attachmentsArray);
    //this.attachmentsFileNamesBeforeEdit = Object.assign({}, this.attachmentsFileNames)
  },
  methods: {
    editPost() {
      axios
        .put("https://jsonplaceholder.typicode.com/posts/" + this.post.id)
        .then(response => {
          //dosomething
          console.log("server response: ", response.status);
        })
        .then(() => {
          this.$emit("update");
        })
        .catch(err => {
          console.log(err);
        });
    },
    cancelEdit() {
      // revert UI back to original issue values if user cancels edits
      Object.assign(this.post, this.postBeforeEdit);
      Object.assign(this.attachmentsArray, this.attachmentsArrayBeforeEdit);
      // Object.assign(this.attachmentsFileNames, this.attachmentsFileNamesBeforeEdit);
      this.$emit("cancel");
    }
  },
  computed: {
    emailAttachmentsFileNames() {
      if (this.attachmentsArray.emailAttachments) {
        const emailAttachmentsFileNameArray = this.attachmentsArray.emailAttachments.map(
          item => {
            const tokens = item.split("/");
            return tokens[tokens.length - 1];
          }
        );
        return emailAttachmentsFileNameArray;
      } else {
        return null;
      }
    },
    attachmentsFileNames() {
      if (this.attachmentsArray.attachments) {
        const attachmentsFileNameArray = this.attachmentsArray.attachments.map(
          item => {
            const tokens = item.split("/");
            return tokens[tokens.length - 1];
          }
        );
        return attachmentsFileNameArray;
      } else {
        return null;
      }
    }
  }
};

Any idea why the cancel button does not revert the attachments array back to original?

Upvotes: 1

Views: 1394

Answers (1)

person13
person13

Reputation: 171

in EditPost.vue you have

cancelEdit() {
      // revert UI back to original issue values if user cancels edits
      Object.assign(this.post, this.postBeforeEdit);
      Object.assign(this.attachmentsArray, this.attachmentsArrayBeforeEdit);
      // Object.assign(this.attachmentsFileNames, this.attachmentsFileNamesBeforeEdit);
      this.$emit("cancel");
    }

Object.assign(this.post, this.postBeforeEdit); where this.post is a prop. You shouldn't mutate a prop. Instead in the parent when the cancel event is fired reset your post and attachmentsArray props there (which will automatically update in the child component).

UPDATE: in EditPost you do this when they delete an item @click.prevent="$emit('deleteMediaAttachment', attachmentFileName)" which in the parent then edits the attachments state. If you want to prevent the actual deletion one approach might be to add a field of "deleted: boolean" to each attachment object. In your edit method you could then toggle the field from false to true. In the template you would use a v-if in conjunction with your v-for to conditionally display the attachments based on deleted. In the event the user cancels you would then need to toggle those fields back to true.

Side note: in EditPost you reference this.attachmentsBeforeEdit = Object.assign({}, this.attachmentsArray); but it's not initialized in the component anywhere. Probably include that as a data property so you're not adding it to data in a lifecycle hook.

Upvotes: 1

Related Questions