Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to post form with multiple components using Vue js

as I'm on my Vue spree (started recently but so far I'm really enjoying learning this framework) couple of questions rised up. One of which is how to post form from multiple components. So before I continue forward I wanted to ask you what are you thinking about this way of structuring and point me in right direction if I'm wrong.

Here it goes. I'm working on a SPA project using ASP.NET CORE 2.1 and Vue JS Template (with webpack)(https://github.com/MarkPieszak/aspnetcore-Vue-starter) and my project is structured in several containers, something like this: In my app-root i registered several containers

<template>

  <div id="app" class="container">

    <app-first-container></app-first-container>
    <app-second-container></app-second-container>
    <!--<app-third-container></app-third-container>-->
    <app-calculate-container></app-calculate-container>
    <app-result-container></app-result-container>

  </div>   
</template>

<script>
  // imported templates
  import firstContainer from './first-container'
  import secondContainer from './second-container'
  import calculateContainer from './calculateButton-container'
  //import thirdContainer from './third-container'
  import resultContainer from './result-container'

  export default {
    components: {
      'app-first-container': firstContainer,
      'app-second-container': secondContainer,
     // 'app-third-container': thirdContainer,
      'app-calculate-container': calculateContainer,
      'app-result-container': resultContainer
    }
  } 
</script>

In my first container I'm having several dropdowns and two input fields with my script file where I'm fetching data from API and filling dropdowns and input fields with fetched data.

Something like this ( entered some dummy code for demonstration)

<template>
  <div>
    <h1>Crops table</h1>

    <p>This component demonstrates fetching data from the server. {{dataMessage}}</p>

    <div class="form-row">

      <div class="form-group col-md-6">
        <label  for="exampleFormControlSelect1" class="col-form-label-sm font-weight-bold">1. Some text</label>
        <select class="form-control" id="exampleFormControlSelect1" v-model="pickedCropType" @change="getCropsByType()">
          <option v-for="(cropType, index) in cropTypes" :key="index" :value="cropType.id" :data-imagesrc="cropType.imgPath">{{ cropType.name }}</option>
        </select>
      </div>

      <div class="form-group col-md-6">
        <label for="exampleFormControlSelect2" class="col-form-label-sm font-weight-bold">2. Some text</label>
        <select class="form-control" id="exampleFormControlSelect2">
          <option v-for="(crop, index) in cropSelectList" :key="index" :value="crop.id">{{ crop.name }}</option>
        </select>
      </div>
    </div>

  </div>
</template>

<script>

  import { mapActions, mapState } from 'vuex'

  export default {
    data() {
      return {
        cropTypes: null,
        cropSelectList: null,
        crops: null,
        pickedCropType: null,

      }
    },

    methods: {
      loadPage: async function () {
        try {
          //Get crop types and create a new array with crop types with an added imgPath property
          var cropTypesFinal = [];
          let responseCropTypes = await this.$http.get(`http://localhost:8006/api/someData`);
          responseCropTypes.data.data.forEach(function (element) {

            cropTypesFinal.push(tmpType);
          });


        } catch (err) {
          window.alert(err)
          console.log(err)
        }
      },
      getCropsByType: async function () {
        //Get crops by crop type
        let responseCrops = await this.$http.get(`http://localhost:8006/api/crop/Type/${this.pickedCropType}`);
        var responseCropsData = responseCrops.data.data;
        this.cropSelectList = responseCropsData;
      }
    },

    async created() {
      this.loadPage()
    }
  }
</script>

And in my second container I have different dropdowns and different input fields with different scripts etc.

So, my questions are:

1.) I'm having required data form field in first container and in second container I'm having additional data and my submit button is separated in third container (app-result-container). So, is this proper and logical way of structuring containers if not can you point me in right direction?

2.) Is it smart to input script tag in every container where I'm processing/fetching/submitting some data for that particular container? Should I put scripts tag in separated file and keep structure clean, separating html from js file.

Example: import { something } from 'something'

export default {
  data () {
    return {
      someData: 'Hello'
    }
  },
  methods: {
    consoleLogData: function (event) {
      Console.log(this.someData)
    }
  }
}

3.) Can I send input values from one container to another (In my particular case from first and second container to app-calculate-container(third container))?

How to on submit return results container with calculated imported values

like image 327
Svinjica Avatar asked Aug 03 '18 13:08

Svinjica


People also ask

What is a mixin Vue?

What are Mixins? Mixins in Vue JS are basically a chunk of defined logic, stored in a particular prescribed way by Vue, which can be re-used over and over to add functionality to your Vue instances and components. So Vue mixins can be shared between multiple Vue components without the need to repeat code blocks.


1 Answers

If you want components to communicate or share data with one another, you will need to either emit an event from one component up to the parent and pass it down via props, or use some kind of state management model, like Vuex, where each of your components can listen to the store.

Take a look at this code sandbox: https://codesandbox.io/s/8144oy7xy2

App.vue

<template>
  <div id="app">
    <child-input @input="updateName" />
    <child-output :value="name" />
  </div>
</template>

<script>
import ChildInput from "@/components/ChildInput.vue";
import ChildOutput from "@/components/ChildOutput.vue";

export default {
  name: "App",
  components: {
    ChildInput,
    ChildOutput
  },
  data() {
    return {
      name: ""
    };
  },
  methods: {
    updateName(e) {
      this.name = e.target.value;
    }
  }
};
</script>

ChildInput.vue

<template>
  <input type="text" @input="changeHandler">
</template>

<script>
export default {
  name: "ChildInput",
  methods: {
    changeHandler(e) {
      this.$emit("input", e);
    }
  }
};
</script>

ChildOutput.vue

<template>
  <p>{{ value }}</p>
</template>

<script>
export default {
  name: "ChildOutput",
  props: {
    value: {
      type: String,
      default: ""
    }
  }
};
</script>

What's going on?

The ChildInput component is a text field and on every change inside it, fires an event (emits using this.$emit() and passes the whole event up).

When this fires, App is listening to the change, which fires a method that updates the name data property.

Because name is a reactive data property and is being passed down as a prop to the ChildOutput component, the screen re-renders and is updated with the text written.

Neither ChildInput nor ChildOutput knows about one another. It's the parent that listens to the event passed to it, then passes the new prop down.

This way of working is fine and simple to understand, but I would strongly recommend looking at Vuex, as this method can get messy and complicated when you go beyond trivial tasks.

like image 154
Michael Giovanni Pumo Avatar answered Sep 30 '22 06:09

Michael Giovanni Pumo