How to Manage Dialog Visibility and Communication Between Parent and Child Components in Element UI

Element UI's ElDialog component implements Vue's .sync modifier pattern through a Boolean visible prop and update:visible events, while mixing in the emitter utility to enable broadcast and dispatch methods for cross-component tree communication.

Managing modal state across component hierarchies requires predictable data flow and event propagation mechanisms. The ElemeFE/element repository solves this through a carefully architected Dialog component that combines Vue's reactive prop system with custom event broadcasting capabilities. Understanding these implementation details allows you to control dialog visibility programmatically and establish communication channels between deeply nested child components and their parents.

How ElDialog Implements the Vue Sync Pattern

The Dialog component in packages/dialog/src/component.vue establishes two-way data binding through prop synchronization rather than direct state mutation.

The Visible Prop and Watcher Architecture

At the core of visibility management, the component defines a Boolean visible prop with a default value of false (lines 13-16):

props: {
  visible: {
    type: Boolean,
    default: false
  }
}

A dedicated watcher monitors this prop (lines 21-41) and immediately invokes open() when the value becomes true or close() when it becomes false. This reactive connection ensures the dialog's display state always reflects the parent's data property without requiring imperative method calls.

Automatic State Synchronization

When users interact with the dialog—clicking the overlay, pressing Escape, or selecting the close button—the component executes the hide() method (lines 76-80). This handler emits update:visible with a false payload:

this.$emit('update:visible', false);

Because the parent binds the dialog using :visible.sync or v-model, Vue automatically updates the parent's data property to match, maintaining a single source of truth across the component boundary.

Controlling Dialog Visibility from Parent Components

Parents manage dialog state by binding a local data property to the dialog's visible prop using Vue's synchronization syntax.

Using the Sync Modifier

The .sync modifier provides explicit two-way binding syntax. In your parent component template:

<template>
  <div>
    <el-button @click="dialogVisible = true">Open Dialog</el-button>
    
    <el-dialog :visible.sync="dialogVisible" title="Settings">
      <p>Dialog content here</p>
    </el-dialog>
  </div>
</template>

<script>
export default {
  data() {
    return {
      dialogVisible: false
    };
  }
};
</script>

When dialogVisible changes to true, the watcher in component.vue detects the change and triggers the open() method. Conversely, when the dialog closes itself, the update:visible event updates dialogVisible to false in the parent.

Using v-model Directives

Alternatively, you can use v-model for the same effect, treating the dialog as a form input component:

<el-dialog v-model="dialogVisible" title="User Preferences">
  <span>Configure your settings below</span>
</el-dialog>

Both approaches achieve identical synchronization, though .sync explicitly documents the prop name being synchronized.

Handling Dialog Events in Parent Components

Beyond visibility state, ElDialog emits a series of lifecycle events that enable parents to respond to specific modal states.

Visibility Update Events

The primary communication channel remains the update:visible event, which fires whenever the dialog's visibility changes. This event integrates seamlessly with the .sync modifier but can also be handled manually:

<el-dialog :visible="dialogVisible" @update:visible="handleVisibilityChange">
  Content
</el-dialog>

Lifecycle Hook Events

According to the source implementation, the component emits four distinct lifecycle events:

  • open: Fired immediately when the dialog begins opening (before animations)
  • opened: Fired after the dialog has fully opened and animations complete
  • close: Fired when the dialog begins closing
  • closed: Fired after the dialog has fully closed

Bind listeners to these events to trigger side effects:

<el-dialog 
  :visible.sync="dialogVisible"
  @open="fetchData"
  @opened="focusInput"
  @close="confirmUnsavedChanges"
  @closed="resetForm">
  <complex-form-component />
</el-dialog>

Advanced Communication with the Emitter Mixin

For scenarios requiring communication between the dialog and deeply nested children—or between sibling components—the Dialog component leverages the emitter mixin from src/mixins/emitter.js.

Broadcast and Dispatch Methods

The emitter mixin provides two critical methods for component tree traversal:

  • dispatch(componentName, eventName, params): Travels upward through parent components until it finds a component matching componentName, then emits the event on that component.
  • broadcast(componentName, eventName, params): Travels downward through child components, finding all instances matching componentName and emitting the event on each.

These methods are mixed into ElDialog alongside Popup and Migrating mixins (as seen in component.vue), giving the dialog event propagation capabilities beyond standard Vue $emit.

Practical Cross-Component Implementation

When a grandchild component nested inside the dialog needs to signal the dialog directly:

<!-- GrandchildComponent.vue -->
<template>
  <el-button @click="notifyDialog">Submit</el-button>
</template>

<script>
export default {
  methods: {
    notifyDialog() {
      // Dispatch upward to find the ElDialog parent
      this.dispatch('ElDialog', 'custom-submit', { data: 'payload' });
    }
  }
};
</script>

The parent dialog listens for this custom event:

<el-dialog :visible.sync="visible" @custom-submit="handleSubmission">
  <grandchild-component />
</el-dialog>

Similarly, a parent component can broadcast downward to all child inputs within a dialog form:

// In parent method
this.broadcast('ElInput', 'clear-validation', []);

Key Source Files and Architecture

Understanding the complete implementation requires examining these specific files in the ElemeFE/element repository:

  • packages/dialog/src/component.vue: Core component logic including the visible prop definition, watcher implementation (lines 21-41), and hide() method (lines 76-80) that emits closure events.
  • src/utils/popup/index.js: The Popup mixin providing modal overlay management, z-index coordination, and body scroll locking functionality mixed into the Dialog component.
  • src/mixins/emitter.js: Defines broadcast and dispatch methods used for cross-component communication when standard prop drilling becomes impractical.
  • packages/dialog/index.js: Public API entry point that registers the component globally.

Summary

  • ElDialog uses a Boolean visible prop (default false) defined in packages/dialog/src/component.vue to control display state reactively.
  • A prop watcher (lines 21-41) monitors visible changes and triggers open() or close() methods automatically.
  • The .sync modifier or v-model enables parents to bind data properties while the dialog emits update:visible events to maintain synchronization.
  • Lifecycle events (open, opened, close, closed) provide hooks for executing logic at specific modal transition points.
  • The emitter mixin from src/mixins/emitter.js adds broadcast and dispatch methods for propagating events up or down the component tree, facilitating communication between nested children and the dialog or parent components.

Frequently Asked Questions

How does the visible prop work in Element UI Dialog?

The visible prop is defined as a Boolean with a default value of false in packages/dialog/src/component.vue (lines 13-16). A Vue watcher monitors this prop (lines 21-41); when the parent changes the bound value to true, the watcher invokes open() to display the dialog. When the dialog closes via user interaction, it emits update:visible with false, automatically updating the parent's data property when using .sync or v-model.

What is the difference between using v-model and the sync modifier for dialog visibility?

Both approaches achieve identical two-way data binding. The .sync modifier explicitly binds the visible prop using :visible.sync="property" syntax, while v-model provides a more concise directive that internally manages the prop and event binding. The .sync modifier makes the synchronized prop name explicit in your template, whereas v-model assumes the primary value prop. Both rely on the update:visible event emitted from the hide() method (lines 76-80).

How can child components inside a dialog communicate with the parent component?

Child components have three communication pathways: standard $emit events bubble up through the template hierarchy and can be captured by the parent listening on the dialog slot content; the dispatch method (from the emitter mixin) walks up the component tree to find the dialog or parent by componentName and emit events directly; and broadcast allows ancestors to send events downward to specific child component types. The emitter mixin in src/mixins/emitter.js provides these tree-traversal utilities.

What events does ElDialog emit during its lifecycle?

ElDialog emits update:visible whenever the visibility state changes, enabling two-way binding. Additionally, it emits four lifecycle events: open when opening begins, opened after opening animations complete, close when closing begins, and closed after closing animations finish. These events allow parent components to synchronize data fetching, focus management, or form reset logic with the dialog's visual state transitions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →