Let you learn knowledge clearly, there are codes, there are explanations, copy of the walk, learn will!Copy the code

Let’s say you’re using iView, and if you have a page that’s a little bit more complex, you’re going to pick a pit for the IView Modal component

Let’s cut the crap and get to the scene

scenario

Page A, there’s A lot of content in the popover format, so I drew A slightly disgusting scene, 1-9 is all the content in the Modal box that you want to display Modal, ok button, cancel button, and close in the upper right corner of the Modal box, and all kinds of things are different, so anyway, just to tell you, You’re going to write nine modal boxes that represent your business scenario

When we write vUE, we know that we need to separate some logic into components, so that the deconstruction is clear and the code is maintainable, so the Modal pit in iView is the close button in the upper right corner

The following code does not change the parent component. Note the child component

code

The parent component parent. Vue

<template>
  <div>
    <h3>The parent component</h3>
    <Button @click='showModal = ! showModal'>Open the</Button>
    <son @closeModal='closeModal' :isShow='showModal' :id='id'></son>
  </div>
</template>

<script>
import son from './son'

export default {
  components: {
    son
  },
  data () {
    return {
      showModal: false.id: 0}},methods: {
    closeModal () {
  	  // Simulate different business ids as input arguments to the popup page call interface
  	  this.id = Date.now()
      this.showModal = false}}}</script>
Copy the code

Usually, someone will write down some of the mistakes listed above

Child component – misspelled -1

<template>
  <Modal title='pop-up window' v-model='isShow' @on-cancel='close' @on-ok='handleOk'>hello</Modal>
</template>

<script>
export default {
  name: 'son'.props: {
    isShow: {
      type: Boolean}},methods: {
    close () {
  		// Some business-related code....
      	this.$emit('closeModal')
    },
    handleOk () {
  		// Some business-related code....
      	this.$emit('closeModal')}}}</script>
Copy the code

Click, ok button, cancel button, close button in the upper right corner

It feels like it’s working fine. Data is passed from the parent component to the child component, which uses $emit to modify the parent component’s data

The console reported the following errorObviously, I’m telling you, you are violating the idea of ‘one-way data flow’ in VUE. Someone said, “I am @on-cancel, @on-OK, how can I do this?

Here’s why:

After you click ok/cancel button, the Son component changes isShow first and then executes the handlers corresponding to the two events you passed

The child component changes the props data, and the props can only be changed by the parent component.

This is the pit in iView, and someone said, well, if I don’t write it like this, how do I close the modal box

And that creates the second hole down here

Child component – misspelled – 2

<template>
  <Modal title='pop-up window' v-model='isShow'>hello<div slot='footer'>
      <Button @click='close'>cancel</Button>
      <Button @click='handleOk'>determine</Button>
    </div>
  </Modal>
</template>

<script>
export default {
  name: 'son'.props: {
    isShow: {
      type: Boolean}},methods: {
    close () {
      // Some business-related code....
      this.$emit('closeModal')
    },
    handleOk () {
      // Some business-related code....
      this.$emit('closeModal')}}}</script>
Copy the code

Let iView manage the ok button and cancel button logic

Click ok button, click cancel button, no problem, according to the ‘one-way data flow’ rule, click the top right ×, oh ho! Or a warning? What the hell

That’s right. The x in the upper right corner is still iView managing Modal state values, so the Son component is changing isShow values, it doesn’t work

Some people say, “I’m going to listen to @on-cancel to fix it.” In fact, I’ve already explained that iView changed the value of isShow before executing your @on-Cancel callback, so it’s still wrong

Child component – misspelled -3

Since the above Modal V-model value is so troublesome, there are holes, so I directly write Modal in the parent component, not on the line, and then Modal content, by introducing the form of the component to complete, so it is ok!

I’m telling you, no

Parent.vue

<template>
  <div>
    <h3>The parent component</h3>
    <Button @click='showModal = ! showModal'>Open the</Button>

    <div v-if='showModal'>
      
      <Modal v-model='showModal' title='demo'>
        <! This is the business code you stripped out -->
        <son></son>
      </Modal>
    </div>

  </div>
</template>

<script>
import son from './son'

export default {
  components: {
    son
  },
  data () {
    return {
      showModal: false}},methods: {
    closeModal () {
      this.showModal = false}}}</script>
Copy the code

So, in parent. Vue, you modify the Modal state yourself, so there is no problem. But in practice, there is a disconnect between the content in Modal and the content outside of Modal. It’s div and Modal, div goes away, Modal goes away. There is a sense of scattered, especially obvious, should be Modal has the transition effect caused by

So, this is also problematic

Somebody said I don’t want the divs in the top layer, I want 9 Modal in pareng.vue, see? I want the ones that I want to remove all together, and the @on-cancel and @on-OK parts of Modal, now I just remove the UI layer into the component, The logical layer is not pulled out, is such a status quo, in this case, the problem is not very elegant

The child component is written correctly

<template>
  <Modal title='pop-up window' v-model='showModal' @on-cancel='rightClose'>hello<div slot='footer'>
      <Button @click='close'>cancel</Button>
      <Button @click='handleOk'>determine</Button>
    </div>
  </Modal>
</template>

<script>
import {getDetail} from '@/api/news'
  
export default {
  name: 'son'.props: {
    isShow: {
      type: Boolean
    },
  	id: {
  		type: Number
  	}
  },
  data () {
    return {
      showModal: false}},watch: {
  	id(newVal) {
  		// When the ID is changed, mounted is not executed again. Mounted is executed only once, unless the parent destroys the Son component and recreates it
  		this.init()
  	},
    isShow (newVal, oldVal) {
      this.showModal = newVal
    }
  },
  mounted() {
  	this.init()
  },
  methods: {
  	// Suppose we want to call data to get details
  	async init() {
  		let res = await getDetail({id: this.id})
  		// ...
  	},
    close () {
      // Some business-related code....
      this.$emit('closeModal')},async handleOk () {
      // Some business-related code.... , such as to request data, get the result of the return value, and then close
  	  let res = await fetch('/test-api')
      if(res) {
		// Data modified successfully
   		this.$emit('closeModal')}else {
  		// Failed to modify data}},rightClose() {
      console.log('Off in the upper right corner, Son component changes the ShowModal state by itself')
      // Some business-related code....
      // Update the state of the parent component
      this.$emit('closeModal')}}}</script>
Copy the code

Application of V-Model parent-child component bidirectional data binding in Modal

Can I write this a little bit more succinctly? The answer is yes

The parent component

<template>
  <div>
    <Button @click='openModal'>Open the</Button>
    <Son v-model='isShowModal' :info='info'></Son>
  </div>
</template>

<script>
import Son from './son'
export default {
  name: 'iview-modal'.components: {
    Son
  },
  data() {
    return {
      isShowModal: false.info: {
        name: 'Joe'.age: 12.time: 0}}},methods: {
    openModal() {
      this.isShowModal = true
      this.info.time = Date.now()
    }
  }
}
</script>

Copy the code

Child components

<template>
  <Modal v-model='open' @on-cancel='handle(false)' @on-ok='handle(true)'>
    <div>
      <Form>
        <FormItem label='name'>{{info.name}}</FormItem>
        <FormItem label='age'>{{info.age}}</FormItem>
        <FormItem label='View time'>{{info.time}}</FormItem>
      </Form>
    </div>
  </Modal>
</template>

<script>
export default {
  name: 'son'.props: {
    value: {
      type: Boolean.default: false
    },
    info: {
      type: Object.default: () = > {
      	return{}}}},data() {
    return {
      open: false}},watch: {
    value(val) {
      this.open = val
    }
  },
  methods: {
    handle(status) {
      this.$emit('input'.false)}}}</script>
Copy the code

Two-way data binding with the parent and child components is nice to look at and has a cleaner syntax

Scenario: The child component simply does the data presentation, which is fine

Limitations: If you do something else while the button is clicked, you find that you still have to write an event to the parent component. For example, for a simple field edit, you need to write the data collected in the popover back to the parent component. You need to display the write input event in the parent component, and then handle the extra work

The parent component

<template>
  <div>
    <Button @click='openModal'>Open the</Button>
    <Son v-model='isShowModal' :info='info' @input='input'></Son>
  </div>
</template>

<script>
import Son from './son'
export default {
  name: 'iview-modal'.components: {
    Son
  },
  data() {
    return {
      isShowModal: false.info: {
        name: 'Joe'.age: 12.time: 0}}},methods: {
    openModal() {
      this.isShowModal = true
      this.info.time = Date.now()
    },
    input(status, obj) {
      // After closing the popover, I want to update the parent component's data, or I want to call the interface to pull the data
      // There is some extra work for the parent component to do, which is to display the defined input event
      / / scenario 1: pull data fetch (' http://www.xxx/api/getData ')

      // Scenario 2: The data in the child component popup updates the parent component to the parent component
      // this.info.name = obj.name}}}</script>
Copy the code

The child component calls, passing additional arguments to the parent component

Child components

handle(status) {
  this.$emit('input'.false, {
    name: 'bill'})}Copy the code

What kind of method to use ultimately depends on your application scenario, the most appropriate is the best, flexible use of VUE syntax sugar, solve the practical problems of the application scenario is the absolute truth

conclusion

Don’t shiver nonsense, straight up summary:

  • Slot, instead of the default footer change ok/cancel button, to avoid the child component directly modify the parent component state
  • Change an internal variable of Son component (or state) showModal, to control the display and hiding of Son component, Son component to modify its own data, there is no problem, there is no problem; The * in the upper right corner changes its state after clicking on it. Yes, that’s the way it works, and then communicates with the parent component via $emit
  • Watch listens to props, and then assigns the props value to the Son component’s showModal to synchronize the state to the Son component

Where you don’t like to use Modal

1, API writing method directly call

this.$Modal.confirm({
	title: ' '
})
Copy the code

The footer action button cannot be centered. There is no API, which means no configurable items are provided

Style =’text-align: center’

2, Modal: API call, click mask layer, can not close. So it’s configured by default: Closable =”false” inconvenient

3, Template, do not have the body content, there is a blank, because the body of modal is the upper and lower padding 15px;

<Modal>
    <! -- No content here -->
    <div slot='footer'>
    	<! -- Custom Modal footer -->
    </div>
</Modal>
Copy the code

Some scenes just don’t want the body content, but if you don’t give it, there will be a blank space and it will look ugly

Some pits, you do not step on, how do you know it will not be flattened 😝😝😝Copy the code

A link to the

  • You don’t know the pit of iView checksum
  • Why the validation in the Form component in ivew is invalid
  • When vue uses iVEW, it always submits data of type Date.