Web Components Multiple Slots

Signals and slots are used for communication between objects. The signals and slots mechanism is a central feature of Qt and probably the part that differs most from the features provided by other frameworks. Signals and slots are made possible by Qt's meta-object system.

Slots

Multiple render fragments. When we write mark-up inside a component, Blazor will assume it should be assigned to a Parameter on the component that is descended from the RenderFragment class and is named ChildContent. If we wish to use a different name, or multiple render fragments, then we must explicitly specify the parameter’s name in our. Accessing assignedNodes or assignedElements on a slot is the canonical way to verify if it has slotted content. In the case of modal with a header, for example, the component needs to handle the case where a slotted content is added or removed.

Introduction

In GUI programming, when we change one widget, we often want another widget to be notified. More generally, we want objects of any kind to be able to communicate with one another. For example, if a user clicks a Close button, we probably want the window's close() function to be called.

Other toolkits achieve this kind of communication using callbacks. A callback is a pointer to a function, so if you want a processing function to notify you about some event you pass a pointer to another function (the callback) to the processing function. The processing function then calls the callback when appropriate. While successful frameworks using this method do exist, callbacks can be unintuitive and may suffer from problems in ensuring the type-correctness of callback arguments.

Signals and Slots

In Qt, we have an alternative to the callback technique: We use signals and slots. A signal is emitted when a particular event occurs. Qt's widgets have many predefined signals, but we can always subclass widgets to add our own signals to them. A slot is a function that is called in response to a particular signal. Qt's widgets have many pre-defined slots, but it is common practice to subclass widgets and add your own slots so that you can handle the signals that you are interested in.

The signals and slots mechanism is type safe: The signature of a signal must match the signature of the receiving slot. (In fact a slot may have a shorter signature than the signal it receives because it can ignore extra arguments.) Since the signatures are compatible, the compiler can help us detect type mismatches when using the function pointer-based syntax. The string-based SIGNAL and SLOT syntax will detect type mismatches at runtime. Signals and slots are loosely coupled: A class which emits a signal neither knows nor cares which slots receive the signal. Qt's signals and slots mechanism ensures that if you connect a signal to a slot, the slot will be called with the signal's parameters at the right time. Signals and slots can take any number of arguments of any type. They are completely type safe.

All classes that inherit from QObject or one of its subclasses (e.g., QWidget) can contain signals and slots. Signals are emitted by objects when they change their state in a way that may be interesting to other objects. This is all the object does to communicate. It does not know or care whether anything is receiving the signals it emits. This is true information encapsulation, and ensures that the object can be used as a software component.

Slots can be used for receiving signals, but they are also normal member functions. Just as an object does not know if anything receives its signals, a slot does not know if it has any signals connected to it. This ensures that truly independent components can be created with Qt.

You can connect as many signals as you want to a single slot, and a signal can be connected to as many slots as you need. It is even possible to connect a signal directly to another signal. (This will emit the second signal immediately whenever the first is emitted.)

Together, signals and slots make up a powerful component programming mechanism.

Signals

Signals are emitted by an object when its internal state has changed in some way that might be interesting to the object's client or owner. Signals are public access functions and can be emitted from anywhere, but we recommend to only emit them from the class that defines the signal and its subclasses.

When a signal is emitted, the slots connected to it are usually executed immediately, just like a normal function call. When this happens, the signals and slots mechanism is totally independent of any GUI event loop. Execution of the code following the emit statement will occur once all slots have returned. The situation is slightly different when using queued connections; in such a case, the code following the emit keyword will continue immediately, and the slots will be executed later.

If several slots are connected to one signal, the slots will be executed one after the other, in the order they have been connected, when the signal is emitted.

Signals are automatically generated by the moc and must not be implemented in the .cpp file. They can never have return types (i.e. use void).

A note about arguments: Our experience shows that signals and slots are more reusable if they do not use special types. If QScrollBar::valueChanged() were to use a special type such as the hypothetical QScrollBar::Range, it could only be connected to slots designed specifically for QScrollBar. Connecting different input widgets together would be impossible.

Slots

A slot is called when a signal connected to it is emitted. Slots are normal C++ functions and can be called normally; their only special feature is that signals can be connected to them.

Since slots are normal member functions, they follow the normal C++ rules when called directly. However, as slots, they can be invoked by any component, regardless of its access level, via a signal-slot connection. This means that a signal emitted from an instance of an arbitrary class can cause a private slot to be invoked in an instance of an unrelated class.

You can also define slots to be virtual, which we have found quite useful in practice.

Compared to callbacks, signals and slots are slightly slower because of the increased flexibility they provide, although the difference for real applications is insignificant. In general, emitting a signal that is connected to some slots, is approximately ten times slower than calling the receivers directly, with non-virtual function calls. This is the overhead required to locate the connection object, to safely iterate over all connections (i.e. checking that subsequent receivers have not been destroyed during the emission), and to marshall any parameters in a generic fashion. While ten non-virtual function calls may sound like a lot, it's much less overhead than any new or delete operation, for example. As soon as you perform a string, vector or list operation that behind the scene requires new or delete, the signals and slots overhead is only responsible for a very small proportion of the complete function call costs. The same is true whenever you do a system call in a slot; or indirectly call more than ten functions. The simplicity and flexibility of the signals and slots mechanism is well worth the overhead, which your users won't even notice.

Note that other libraries that define variables called signals or slots may cause compiler warnings and errors when compiled alongside a Qt-based application. To solve this problem, #undef the offending preprocessor symbol.

A Small Example

A minimal C++ class declaration might read:

A small QObject-based class might read:

The QObject-based version has the same internal state, and provides public methods to access the state, but in addition it has support for component programming using signals and slots. This class can tell the outside world that its state has changed by emitting a signal, valueChanged(), and it has a slot which other objects can send signals to.

All classes that contain signals or slots must mention Q_OBJECT at the top of their declaration. They must also derive (directly or indirectly) from QObject.

Slots are implemented by the application programmer. Here is a possible implementation of the Counter::setValue() slot:

The emit line emits the signal valueChanged() from the object, with the new value as argument.

In the following code snippet, we create two Counter objects and connect the first object's valueChanged() signal to the second object's setValue() slot using QObject::connect():

Calling a.setValue(12) makes a emit a valueChanged(12) signal, which b will receive in its setValue() slot, i.e. b.setValue(12) is called. Then b emits the same valueChanged() signal, but since no slot has been connected to b's valueChanged() signal, the signal is ignored.

Note that the setValue() function sets the value and emits the signal only if value != m_value. This prevents infinite looping in the case of cyclic connections (e.g., if b.valueChanged() were connected to a.setValue()).

By default, for every connection you make, a signal is emitted; two signals are emitted for duplicate connections. You can break all of these connections with a single disconnect() call. If you pass the Qt::UniqueConnectiontype, the connection will only be made if it is not a duplicate. If there is already a duplicate (exact same signal to the exact same slot on the same objects), the connection will fail and connect will return false.

This example illustrates that objects can work together without needing to know any information about each other. To enable this, the objects only need to be connected together, and this can be achieved with some simple QObject::connect() function calls, or with uic's automatic connections feature.

A Real Example

The following is an example of the header of a simple widget class without member functions. The purpose is to show how you can utilize signals and slots in your own applications.

LcdNumber inherits QObject, which has most of the signal-slot knowledge, via QFrame and QWidget. It is somewhat similar to the built-in QLCDNumber widget.

The Q_OBJECT macro is expanded by the preprocessor to declare several member functions that are implemented by the moc; if you get compiler errors along the lines of 'undefined reference to vtable for LcdNumber', you have probably forgotten to run the moc or to include the moc output in the link command.

After the class constructor and public members, we declare the class signals. The LcdNumber class emits a signal, overflow(), when it is asked to show an impossible value.

If you don't care about overflow, or you know that overflow cannot occur, you can ignore the overflow() signal, i.e. don't connect it to any slot.

If on the other hand you want to call two different error functions when the number overflows, simply connect the signal to two different slots. Qt will call both (in the order they were connected).

A slot is a receiving function used to get information about state changes in other widgets. LcdNumber uses it, as the code above indicates, to set the displayed number. Since display() is part of the class's interface with the rest of the program, the slot is public.

Several of the example programs connect the valueChanged() signal of a QScrollBar to the display() slot, so the LCD number continuously shows the value of the scroll bar.

Note that display() is overloaded; Qt will select the appropriate version when you connect a signal to the slot. With callbacks, you'd have to find five different names and keep track of the types yourself.

Signals And Slots With Default Arguments

The signatures of signals and slots may contain arguments, and the arguments can have default values. Consider QObject::destroyed():

When a QObject is deleted, it emits this QObject::destroyed() signal. We want to catch this signal, wherever we might have a dangling reference to the deleted QObject, so we can clean it up. A suitable slot signature might be:

To connect the signal to the slot, we use QObject::connect(). There are several ways to connect signal and slots. The first is to use function pointers:

There are several advantages to using QObject::connect() with function pointers. First, it allows the compiler to check that the signal's arguments are compatible with the slot's arguments. Arguments can also be implicitly converted by the compiler, if needed.

You can also connect to functors or C++11 lambdas:

In both these cases, we provide this as context in the call to connect(). The context object provides information about in which thread the receiver should be executed. This is important, as providing the context ensures that the receiver is executed in the context thread.

The lambda will be disconnected when the sender or context is destroyed. You should take care that any objects used inside the functor are still alive when the signal is emitted.

The other way to connect a signal to a slot is to use QObject::connect() and the SIGNAL and SLOT macros. The rule about whether to include arguments or not in the SIGNAL() and SLOT() macros, if the arguments have default values, is that the signature passed to the SIGNAL() macro must not have fewer arguments than the signature passed to the SLOT() macro.

All of these would work:

But this one won't work:

...because the slot will be expecting a QObject that the signal will not send. This connection will report a runtime error.

Components

Note that signal and slot arguments are not checked by the compiler when using this QObject::connect() overload.

Advanced Signals and Slots Usage

For cases where you may require information on the sender of the signal, Qt provides the QObject::sender() function, which returns a pointer to the object that sent the signal.

Lambda expressions are a convenient way to pass custom arguments to a slot:

Using Qt with 3rd Party Signals and Slots

It is possible to use Qt with a 3rd party signal/slot mechanism. You can even use both mechanisms in the same project. Just add the following line to your qmake project (.pro) file.

It tells Qt not to define the moc keywords signals, slots, and emit, because these names will be used by a 3rd party library, e.g. Boost. Then to continue using Qt signals and slots with the no_keywords flag, simply replace all uses of the Qt moc keywords in your sources with the corresponding Qt macros Q_SIGNALS (or Q_SIGNAL), Q_SLOTS (or Q_SLOT), and Q_EMIT.

See also QLCDNumber, QObject::connect(), Digital Clock Example, Tetrix Example, Meta-Object System, and Qt's Property System.

© 2020 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.

What are Components?

Components are one of the most powerful features of Vue.js. They help you extend basic HTML elements to encapsulate reusable code. At a high level, Components are custom elements that Vue.js’ compiler would attach specified behavior to. In some cases, they may also appear as a native HTML element extended with the special is attribute.

Using Components

Registration

We’ve learned in the previous sections that we can create a component constructor using Vue.extend():

To use this constructor as a component, you need to register it with Vue.component(tag, constructor):

Note that Vue.js does not enforce the W3C rules for custom tag-names (all-lowercase, must contain a hyphen) though following this convention is considered good practice.

Once registered, the component can now be used in a parent instance’s template as a custom element, <my-component>. Make sure the component is registered before you instantiate your root Vue instance. Here’s the full example:

Which will render:

Note the component’s template replaces the custom element, which only serves as a mounting point. This behavior can be configured using the replace instance option.

Also note that components are provided a template instead of mounting with the el option! Only the root Vue instance (defined using new Vue) will include an el to mount to).

Local Registration

You don’t have to register every component globally. You can make a component available only in the scope of another component by registering it with the components instance option:

The same encapsulation applies for other assets types such as directives, filters and transitions.

Registration Sugar

To make things easier, you can directly pass in the options object instead of an actual constructor to Vue.component() and the component option. Vue.js will automatically call Vue.extend() for you under the hood:

Component Option Caveats

Most of the options that can be passed into the Vue constructor can be used in Vue.extend(), with two special cases: data and el. Imagine we simply pass an object as data to Vue.extend():

The problem with this is that the same data object will be shared across all instances of MyComponent! This is most likely not what we want, so we should use a function that returns a fresh object as the data option:

The el option also requires a function value when used in Vue.extend(), for exactly the same reason.

Template Parsing

Vue.js template engine is DOM-based and uses native parser that comes with the browser instead of providing a custom one. There are benefits to this approach when compared to string-based template engines, but there are also caveats. Templates have to be individually valid pieces of HTML. Some HTML elements have restrictions on what elements can appear inside them. Most common of these restrictions are:

  • a can not contain other interactive elements (e.g. buttons and other links)
  • li should be a direct child of ul or ol, and both ul and ol can only contain li
  • option should be a direct child of select, and select can only contain option (and optgroup)
  • table can only contain thead, tbody, tfoot and tr, and these elements should be direct children of table
  • tr can only contain th and td, and these elements should be direct children of tr

In practice these restriction can cause unexpected behavior. Although in simple cases it might appear to work, you can not rely on custom elements being expanded before browser validation. E.g. <my-select><option>...</option></my-select> is not a valid template even if my-select component eventually expands to <select>...</select>.

Another consequence is that you can not use custom tags (including custom elements and special tags like <component>, <template> and <partial>) inside of select, table and other elements with similar restrictions. Custom tags will be hoisted out and thus not render properly.

In case of a custom element you should use the is special attribute:

In case of a <template> inside of a <table> you should use <tbody>, as tables are allowed to have multiple tbody:

Props

Passing Data with Props

Every component instance has its own isolated scope. This means you cannot (and should not) directly reference parent data in a child component’s template. Data can be passed down to child components using props.

A “prop” is a field on a component’s data that is expected to be passed down from its parent component. A child component needs to explicitly declare the props it expects to receive using the props option:

Then, we can pass a plain string to it like so:

Result:

camelCase vs. kebab-case

HTML attributes are case-insensitive. When using camelCased prop names as attributes, you need to use their kebab-case (hyphen-delimited) equivalents:

Dynamic Props

Similar to binding a normal attribute to an expression, we can also use v-bind for dynamically binding props to data on the parent. Whenever the data is updated in the parent, it will also flow down to the child:

It is often simpler to use the shorthand syntax for v-bind:

Result:


Literal vs. Dynamic

A common mistake beginners tend to make is attempting to pass down a number using the literal syntax:

However, since this is a literal prop, its value is passed down as a plain string '1', instead of an actual number. If we want to pass down an actual JavaScript number, we need to use the dynamic syntax to make its value be evaluated as a JavaScript expression:

Prop Binding Types

By default, all props form a one-way-down binding between the child property and the parent one: when the parent property updates, it will flow down to the child, but not the other way around. This default is meant to prevent child components from accidentally mutating the parent’s state, which can make your app’s data flow harder to reason about. However, it is also possible to explicitly enforce a two-way or a one-time binding with the .sync and .oncebinding type modifiers:

Web components multiple slots numbers

Compare the syntax:

The two-way binding will sync the change of child’s msg property back to the parent’s parentMsg property. The one-time binding, once set up, will not sync future changes between the parent and the child.

Note that if the prop being passed down is an Object or an Array, it is passed by reference. Mutating the Object or Array itself inside the child will affect parent state, regardless of the binding type you are using.

Prop Validation

It is possible for a component to specify the requirements for the props it is receiving. This is useful when you are authoring a component that is intended to be used by others, as these prop validation requirements essentially constitute your component’s API, and ensure your users are using your component correctly. Instead of defining the props as an array of strings, you can use the object hash format that contain validation requirements:

The type can be one of the following native constructors:

  • String
  • Number
  • Boolean
  • Function
  • Object
  • Array

In addition, type can also be a custom constructor function and the assertion will be made with an instanceof check.

When a prop validation fails, Vue will refuse to set the value on the child component, and throw a warning if using the development build.

Parent-Child Communication

Parent Chain

A child component holds access to its parent component as this.$parent. A root Vue instance will be available to all of its descendants as this.$root. Each parent component has an array, this.$children, which contains all its child components.

Although it’s possible to access any instance in the parent chain, you should avoid directly relying on parent data in a child component and prefer passing data down explicitly using props. In addition, it is a very bad idea to mutate parent state from a child component, because:

  1. It makes the parent and child tightly coupled;

  2. It makes the parent state much harder to reason about when looking at it alone, because its state may be modified by any child! Ideally, only a component itself should be allowed to modify its own state.

Custom Events

All Vue instances implement a custom event interface that facilitates communication within a component tree. This event system is independent from the native DOM events and works differently.

Each Vue instance is an event emitter that can:

  • Listen to events using $on();

  • Trigger events on self using $emit();

  • Dispatch an event that propagates upward along the parent chain using $dispatch();

  • Broadcast an event that propagates downward to all descendants using $broadcast().

Unlike DOM events, Vue events will automatically stop propagation after triggering callbacks for the first time along a propagation path, unless the callback explicitly returns true.

A simple example:

v-on for Custom Events

The example above is pretty nice, but when we are looking at the parent’s code, it’s not so obvious where the 'child-msg' event comes from. It would be better if we can declare the event handler in the template, right where the child component is used. To make this possible, v-on can be used to listen for custom events when used on a child component:

This makes things very clear: when the child triggers the 'child-msg' event, the parent’s handleIt method will be called. Any code that affects the parent’s state will be inside the handleIt parent method; the child is only concerned with triggering the event.

Child Component Refs

Despite the existence of props and events, sometimes you might still need to directly access a child component in JavaScript. To achieve this you have to assign a reference ID to the child component using v-ref. For example:

When v-ref is used together with v-for, the ref you get will be an Array or an Object containing the child components mirroring the data source.

Content Distribution with Slots

When using components, it is often desired to compose them like this:

There are two things to note here:

  1. The <app> component does not know what content may be present inside its mount target. It is decided by whatever parent component that is using <app>.

  2. The <app> component very likely has its own template.

To make the composition work, we need a way to interweave the parent “content” and the component’s own template. This is a process called content distribution (or “transclusion” if you are familiar with Angular). Vue.js implements a content distribution API that is modeled after the current Web Components spec draft, using the special <slot> element to serve as distribution outlets for the original content.

Compilation Scope

Before we dig into the API, let’s first clarify which scope the contents are compiled in. Imagine a template like this:

Should the msg be bound to the parent’s data or the child data? The answer is parent. A simple rule of thumb for component scope is:

Everything in the parent template is compiled in parent scope; everything in the child template is compiled in child scope.

A common mistake is trying to bind a directive to a child property/method in the parent template:

Assuming someChildProperty is a property on the child component, the example above would not work as intended. The parent’s template should not be aware of the state of a child component.

If you need to bind child-scope directives on a component root node, you should do so in the child component’s own template:

Similarly, distributed content will be compiled in the parent scope.

Single Slot

Parent content will be discarded unless the child component template contains at least one <slot> outlet. When there is only one slot with no attributes, the entire content fragment will be inserted at its position in the DOM, replacing the slot itself.

Anything originally inside the <slot> tags is considered fallback content. Fallback content is compiled in the child scope and will only be displayed if the hosting element is empty and has no content to be inserted.

Suppose we have a component with the following template:

Parent markup that uses the component:

The rendered result will be:

Named Slots

<slot> elements have a special attribute, name, which can be used to further customize how content should be distributed. You can have multiple slots with different names. A named slot will match any element that has a corresponding slot attribute in the content fragment.

There can still be one unnamed slot, which is the default slot that serves as a catch-all outlet for any unmatched content. If there is no default slot, unmatched content will be discarded.

For example, suppose we have a multi-insertion component with the following template:

Parent markup:

The rendered result will be:

The content distribution API is a very useful mechanism when designing components that are meant to be composed together.

Dynamic Components

You can use the same mount point and dynamically switch between multiple components by using the reserved <component> element and dynamically bind to its is attribute:

keep-alive

If you want to keep the switched-out components alive so that you can preserve its state or avoid re-rendering, you can add a keep-alive directive param:

activate Hook

When switching components, the incoming component might need to perform some asynchronous operation before it should be swapped in. To control the timing of component swapping, implement the activate hook on the incoming component:

Note the activate hook is only respected during dynamic component swapping or the initial render for static components - it does not affect manual insertions with instance methods.

transition-mode

The transition-mode param attribute allows you to specify how the transition between two dynamic components should be executed.

By default, the transitions for incoming and outgoing components happen simultaneously. This attribute allows you to configure two other modes:

  • in-out: New component transitions in first, current component transitions out after incoming transition has finished.

  • out-in: Current component transitions out first, new component transitions in after outgoing transition has finished.

Example

Misc

Components and v-for

You can directly use v-for on the custom component, like any normal element:

However, this won’t pass any data to the component, because components have isolated scopes of their own. In order to pass the iterated data into the component, we should also use props:

The reason for not automatically injecting item into the component is because that makes the component tightly coupled to how v-for works. Being explicit about where its data comes from makes the component reusable in other situations.

Authoring Reusable Components

When authoring components, it is good to keep in mind whether you intend to reuse this component somewhere else later. It is OK for one-off components to have some tight coupling with each other, but reusable components should define a clean public interface.

Web Components Multiple Slots

The API for a Vue.js component essentially comes in three parts - props, events and slots:

Web Components Multiple Slots Machines

  • Props allow the external environment to feed data to the component;

  • Events allow the component to trigger actions in the external environment;

  • Slots allow the external environment to insert content into the component’s view structure.

With the dedicated shorthand syntax for v-bind and v-on, the intents can be clearly and succinctly conveyed in the template:

Web Components Multiple Slots Vegas World

Async Components

In large applications, we may need to divide the app into smaller chunks, and only load a component from the server when it is actually needed. To make that easier, Vue.js allows you to define your component as a factory function that asynchronously resolves your component definition. Vue.js will only trigger the factory function when the component actually needs to be rendered, and will cache the result for future re-renders. For example:

The factory function receives a resolve callback, which should be called when you have retrieved your component definition from the server. You can also call reject(reason) to indicate the load has failed. The setTimeout here is simply for demonstration; How to retrieve the component is entirely up to you. One recommended approach is to use async components together with Webpack’s code-splitting feature:

Assets Naming Convention

Some assets, such as components and directives, appear in templates in the form of HTML attributes or HTML custom tags. Since HTML attribute names and tag names are case-insensitive, we often need to name our assets using kebab-case instead of camelCase, which can be a bit inconvenient.

Vue.js actually supports naming your assets using camelCase or PascalCase, and automatically resolves them as kebab-case in templates (similar to the name conversion for props):

This works nicely with ES6 object literal shorthand:

Recursive Component

Components can recursively invoke itself in its own template, however, it can only do so when it has the name option:

A component like the above will result in a “max stack size exceeded” error, so make sure recursive invocation is conditional. When you register a component globally using Vue.component(), the global ID is automatically set as the component’s name option.

Fragment Instance

When you use the template option, the content of the template will replace the element the Vue instance is mounted on. It is therefore recommended to always have a single root-level, plain element in templates.

Instead of templates like this:

Prefer this:

There are multiple conditions that will turn a Vue instance into a fragment instance:

  1. Template contains multiple top-level elements.
  2. Template contains only plain text.
  3. Template contains only another component (which can potentially be a fragment instance itself).
  4. Template contains only an element directive, e.g. <partial> or vue-router’s <router-view>.
  5. Template root node has a flow-control directive, e.g. v-if or v-for.

The reason is that all of the above cause the instance to have an unknown number of top-level elements, so it has to manage its DOM content as a fragment. A fragment instance will still render the content correctly. However, it will not have a root node, and its $el will point to an “anchor node”, which is an empty Text node (or a Comment node in debug mode).

What’s more important though, is that non-flow-control directives, non-prop attributes and transitions on the component element will be ignored, because there is no root element to bind them to:

There are, of course, valid use cases for fragment instances, but it is in general a good idea to give your component template a single, plain root element. It ensures directives and attributes on the component element to be properly transferred, and also results in slightly better performance.

Web Components Multiple Slots Numbers

Inline Template

When the inline-template special attribute is present on a child component, the component will use its inner content as its template, rather than treating it as distributed content. This allows more flexible template-authoring.

Web Components Multiple Slots Games

However, inline-template makes the scope of your templates harder to reason about, and makes the component’s template compilation un-cachable. As a best practice, prefer defining templates inside the component using the template option.

Web Components Multiple Slots Folders

Caught a mistake or want to contribute to the documentation? Edit this page on Github!