如何动态创建 javascript 表单元素

How to create javascript form elements dynamically

我有一个 vuejs 页面,我正在动态创建 DOM 输入,我想将它们与一组 3 个表单变量绑定以捕获数据。 我不知道他们会从一开始就创建多少组输入来初始化表单条目。 表单是在 Vue 的 setup() 部分使用 useForm 创建的。 以下是代码片段。

<template>
    <app-layout>
        <template #header>
            <h2 class="font-semibold text-xl text-gray-800 leading-tight">
                {{ title }}
            </h2>
        </template>
        <div class="py-12">
            <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
                <div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
                    <div class="audio_edit">
                        <form @submit.prevent="form.post('/pppaudio/newProcessedAudio')">
                            <div class="pp_title">
                                <label style="font-weight: bold">
                                    Title:
                                </label>
                                <label for="title">{{ title }}</label>
                            </div>
                            <div class="pp_audio">
                                <div id="waveform"></div>
                                <div id="wave-timeline"></div>
                                <div class="playButton">
                                    <Button data-action="play" type="button">
                                        <span id="play">
                                            <i class="glyphicon glyphicon-play"></i>
                                            Play
                                        </span>
                                        <span id="pause" style="display: none">
                                            <i class="glyphicon glyphicon-pause"></i>
                                            Pause
                                        </span>
                                    </Button>
                                </div>
                            </div>
                            <div class="pp_transcript flex flex-col">
                                <label style="font-weight: bold">
                                    Transcript:
                                </label>
                                {{ transcript }}
                            </div>
                            <div id="region_block" ref="reg_block" >
                                <div class="region">
                                    <div id="copyTextButton" class="copyButton">
                                        <Button v-on:click="getText" type="button">Copy Marked Text</Button>
                                    </div>
                                    <div class="pp_new_transcript flex flex-col">
                                        <label style="font-weight: bold">
                                            New Transcript:
                                        </label>
                                        <textarea id="selectedText" rows="5" class="selectedTextInput h-full" v-model="form.selectedText" />
                                    </div>
                                    <div class="pp_start-stop">
                                        <label for="start" style="font-weight: bold">
                                            Start:
                                        </label>
                                        <input id="start" v-model="form.start" disabled/>
                                        <label for="stop" style="font-weight: bold">
                                            Stop:
                                        </label>
                                        <input id="stop" v-model="form.stop" disabled/>
                                    </div>
                                    <div id="submit_button">
                                        <Button>Submit</Button>
                                    </div>
                                </div>
                            </div>

                            <div id="return_back">
                                <Button v-on:click="goBack" type="button">Back</Button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </app-layout>
</template>

<script>
import AppLayout from "../../Layouts/AppLayout";
import Label from "../../Jetstream/Label";
import Button from "../../Jetstream/Button";
import {InputFacade} from 'vue-input-facade';
import WaveSurfer from 'wavesurfer.js';
import RegionPlugin from 'wavesurfer.js/dist/plugin/wavesurfer.regions.js';
import TimeLine from 'wavesurfer.js/dist/plugin/wavesurfer.timeline.js';
import {useForm} from '@inertiajs/inertia-vue3';
import Vue from 'vue'

export default {
    setup() {
        const form = useForm({
            title: null,
            selectedText: null,
            start: null,
            stop: null,
        })

        return {form}
    },
    components: {Button, Label, AppLayout, InputFacade},
    methods: {
        getText() {
            this.newTranscript = window.getSelection().toString();
            this.form.selectedText = this.newTranscript;
            this.form.title = this.title;
        },
        copyValues(start, stop) {
            this.form.start = start;
            this.form.stop = stop;
        },
        goBack() {
            let formData = new FormData();
            formData.append('id', localStorage.id);

            axios.post('/pppaudio/goBack',
                formData, {
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    }
                }
            ).then( (response) => {
                if (response.data.status === 'success') {
                    window.location.href = response.data.url;
                }
            }).catch(error => {
                console.log("ERRRR:: ", error.response.data);
            });
        }
    },
    mounted() {
        if (localStorage.title) {
            this.title = localStorage.title;
        }
        if (localStorage.path) {
            this.path = localStorage.path;
        }
        if (localStorage.transcript) {
            this.transcript = localStorage.transcript;
        }

        const wavesurfer = WaveSurfer.create({
            container: '#waveform',
            waveColor: 'violet',
            progressColor: 'purple',
            scrollParent: true,
            plugins: [
                RegionPlugin.create({
                    dragSelection: true
                }),
                TimeLine.create({
                    container: '#wave-timeline'
                })
            ]
        });

        wavesurfer.load('../storage/preprocessed-audio/' + this.path);

        let play = false;

        wavesurfer.on('region-click', (region, e) => {
            e.stopPropagation();
            // Play on click, loop on shift click
            if (play) {
                wavesurfer.pause();
                play = false;
            } else {
                region.play();
                play = true;
            }
            // console.log(this.form.title);
           this.copyValues(region.start, region.end);
        });

        wavesurfer.on('region-created', (region, e) => {
            console.log(region.id.substring(10))
            let regionNode = document.createElement("div")
            regionNode.className = "region"
            let regionTitle = document.createElement("div")
            regionTitle.className = "regionTitle"
            regionTitle.innerHTML = region.id
            regionTitle.style.fontWeight = "bold"
            regionNode.appendChild(regionTitle)
            let color = () => Math.random() * 256 >> 0;
            let col = `${color()}, ${color()}, ${color()}`
            regionTitle.style.color = 'rgb('+col+')';
            region.color = 'rgba('+ col + ', 0.1)';
            let copyButtonDiv = document.createElement("div")
            copyButtonDiv.className = "copyTextButton"
            let copyButton = document.createElement("Button")
            copyButton.className = "inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring focus:ring-gray-300 disabled:opacity-25 transition"
            let copyButtonText = document.createTextNode("Copy Marked Text")
            copyButton.appendChild(copyButtonText)
            copyButton.addEventListener('click', this.getText)
            let cpBtnType = document.createAttribute("type")
            cpBtnType.value = "button"
            copyButton.setAttributeNode(cpBtnType)
            copyButtonDiv.appendChild(copyButton)
            regionNode.appendChild(copyButtonDiv)
            let newTranscriptDiv = document.createElement("div")
            newTranscriptDiv.className = "pp_new_transcript flex flex-col"
            let newTransLabel = document.createElement("label")
            newTransLabel.innerText = "New Transcript:"
            newTransLabel.style.fontWeight = "bold"
            let selectTextArea = document.createElement("textarea")
            selectTextArea.id = "selectedText"
            selectTextArea.className = "selectedTextInput h-full"
            let selectTextAreType = document.createAttribute("rows")
            selectTextAreType.value = "5"
            selectTextArea.setAttributeNode(selectTextAreType)
            newTranscriptDiv.appendChild(newTransLabel)
            newTranscriptDiv.appendChild(selectTextArea)
            regionNode.appendChild(newTranscriptDiv)
            let startStopDiv = document.createElement("div")
            startStopDiv.className = "pp_start-stop"
            let startLabel = document.createElement("label")
            startLabel.innerText = "Start:"
            startLabel.style.fontWeight = "bold"
            let startLabelType = document.createAttribute("for")
            startLabelType.value = "start"
            startLabel.setAttributeNode(startLabelType)
            let startInput = document.createElement("input")
            startInput.id = "start"
            let startInputType = document.createAttribute("disabled")
            startInput.setAttributeNode(startInputType)
            let stopLabel = document.createElement("label")
            stopLabel.innerText = "Stop:"
            stopLabel.style.fontWeight = "bold"
            let stopLabelType = document.createAttribute("for")
            stopLabelType.value = "stop"
            stopLabel.setAttributeNode(stopLabelType)
            let stopInput = document.createElement("input")
            stopInput.id = "stop"
            let stopInputType = document.createAttribute("disabled")
            stopInput.setAttributeNode(stopInputType)
            startStopDiv.appendChild(startLabel)
            startStopDiv.appendChild(startInput)
            startStopDiv.appendChild(stopLabel)
            startStopDiv.appendChild(stopInput)
            regionNode.appendChild(startStopDiv)
            let submitBtnDiv = document.createElement("div")
            submitBtnDiv.id = "submit_button"
            let submitButton = document.createElement("Button")
            let submitButtonText = document.createTextNode("Submit")
            submitButton.className = "inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring focus:ring-gray-300 disabled:opacity-25 transition"
            submitButton.appendChild(submitButtonText)
            submitBtnDiv.appendChild(submitButton)
            regionNode.appendChild(submitBtnDiv)
            // this.$set()


            document.querySelector('#region_block').appendChild(regionNode)
        })

        let playButton = document.querySelector('#play');
        let pauseButton = document.querySelector('#pause');
        wavesurfer.on('play', function() {
            playButton.style.display = 'none';
            pauseButton.style.display = '';
        });
        wavesurfer.on('pause', function() {
            playButton.style.display = '';
            pauseButton.style.display = 'none';
        });

        playButton.addEventListener('click', function () {
            wavesurfer.play()
        })

        pauseButton.addEventListener('click', function () {
            wavesurfer.pause()
        })

    },
    data() {
        return {
            title: '',
            path: '',
            transcript: '',
            selectedText: '',
            newTranscript: '',
            start: '',
            stop: ''
        }
    }
}
</script>

<style>
.audio_edit {
    padding: 10px;
}

.pp_title, .pp_audio, .copyButton, .pp_transcript, .pp_new_transcript, .pp_start-stop, #wave-timeline, #submit_button {
    padding-bottom: 10px;
}

.region {
    border-top: 3px solid black;
    border-bottom: 3px solid black;
    padding-top: 3px;
    margin-bottom: 3px;
}

.pp_new_transcript {
    display: flex;
    width: 100%;
}

.selectedTextInput {
    -webkit-box-flex:1;
    -webkit-flex:1;
    -ms-flex:1;
    flex:1;
    border: none;
}

</style>
  1. 每当你需要一些 HTML 在 Vue 中重复时,使用 v-for。在下面的示例中,我将它与 component

    一起使用
  2. 在 Vue 中很少需要使用 createElement,应该避免使用 。如果您考虑我下面的示例,您应该看到整个 wavesurfer.on('region-created') 部分可以通过将新的 Region 对象推入 v-for

    中使用的数组来替换

const app = Vue.createApp({
  data() {
    return {
      forms: [{
          id: 1,
          selectedText: 'Hello',
          start: 0,
          stop: 4
        },
        {
          id: 2,
          selectedText: 'world',
          start: 6,
          stop: 10
        },
      ]
    }
  }
})

app.component('my-form', {
  props: {
   form: {
     type: Object,
     required: true
   }
  },
  template: `
  <div class="region">
    <div id="copyTextButton" class="copyButton">
       <button type="button">Copy Marked Text</button>
    </div>
    <div class="pp_new_transcript flex flex-col">
        <label style="font-weight: bold">
            New Transcript:
        </label>
        <textarea id="selectedText" rows="5" class="selectedTextInput h-full" v-model="form.selectedText" />
    </div>
    <div class="pp_start-stop">
        <label for="start" style="font-weight: bold">
            Start:
        </label>
        <input id="start" v-model="form.start" disabled/>
        <label for="stop" style="font-weight: bold">
            Stop:
        </label>
        <input id="stop" v-model="form.stop" disabled/>
    </div>
    <div id="submit_button">
        <button>Submit</button>
    </div>
 </div>
  `
})

app.mount('#app')
<script src="https://unpkg.com/vue@3.1.4/dist/vue.global.js"></script>
<div id='app'>
  <my-form v-for="form in forms" :key="form.id" :form="form"></my-form>
</div>