Skip to content
intermediate Phase 55 · Frontend JavaScript

UI Components JS Deep Dive

Advanced Magento 2 UI Components: component lifecycle, mixins, extending components, and custom component creation

1h
0 problems
Topic Progress 0%

Component Lifecycle

UI Component Lifecycle

1. initialize() → 2. initConfig() → 3. setDependencies()
4. initElement() → 5. onReady() → 6. destroy()

Lifecycle Methods

define([
    'uiComponent'
], function(Component) {
    'use strict';
    
    return Component.extend({
        defaults: {
            template: 'Vendor_Module/component'
        },
        
        // Called during initialization
        initialize: function() {
            this._super();
            console.log('Component initialized');
            return this;
        },
        
        // Called when component is ready
        onReady: function() {
            console.log('Component ready');
        },
        
        // Called when component is destroyed
        destroy: function() {
            this._super();
            console.log('Component destroyed');
        }
    });
});

Initialization Order

initialize: function() {
    // 1. Parent initialization
    this._super();
    
    // 2. Set default values
    this.setData(this.defaultData);
    
    // 3. Initialize observables
    this.initObservable();
    
    // 4. Initialize modules
    this.initModules();
    
    return this;
}

Mixins

What are Mixins?

Mixins allow you to extend UI components without modifying the original code.

Creating a Mixin

// view/frontend/web/js/component-mixin.js
define([
    'jquery'
], function($) {
    'use strict';
    
    return function(target) {
        return target.extend({
            // Override method
            initialize: function() {
                this._super();
                console.log('Mixin initialized');
                return this;
            },
            
            // Add new method
            customMethod: function() {
                return 'Custom behavior';
            }
        });
    };
});

Register Mixin

// view/frontend/requirejs-config.js
var config = {
    config: {
        'mixins': {
            'Vendor_Module/js/component': {
                'Vendor_Module/js/component-mixin': true
            }
        }
    }
};

Mixin with Configuration

define([], function() {
    'use strict';
    
    return function(target, config) {
        return target.extend({
            defaults: {
                mixinConfig: config
            },
            
            initialize: function() {
                this._super();
                console.log('Mixin config:', this.mixinConfig);
                return this;
            }
        });
    };
});

Multiple Mixins

// Mixin 1
define([], function() {
    return function(target) {
        return target.extend({
            method1: function() { return 'mixin1'; }
        });
    };
});

// Mixin 2
define([], function() {
    return function(target) {
        return target.extend({
            method2: function() { return 'mixin2'; }
        });
    };
});

Both mixins are applied in order.

Extending Components

Extending via Inheritance

define([
    'Vendor_Original/js/component'
], function(Component) {
    'use strict';
    
    return Component.extend({
        defaults: {
            template: 'Vendor_Extended/component'
        },
        
        // Override method
        originalMethod: function() {
            var result = this._super();
            return result + ' extended';
        },
        
        // Add new method
        newMethod: function() {
            return 'New functionality';
        }
    });
});

Extending via Preferences (di.xml)

<config>
    <type name="Vendor\Original\Component">
        <plugin name="extend_component" type="Vendor\Extended\Plugin\ComponentPlugin"/>
    </type>
</config>

Extending via Layout

<referenceBlock name="component.block">
    <arguments>
        <argument name="js_component" xsi:type="string">
            Vendor_Extended/js/component
        </argument>
    </arguments>
</referenceBlock>

Composition Pattern

define([
    'uiComponent',
    'Vendor_Module/js/helper'
], function(Component, helper) {
    'use strict';
    
    return Component.extend({
        defaults: {
            modules: {
                childComponent: '${ $.name }.child'
            }
        },
        
        initialize: function() {
            this._super();
            helper.setupComponent(this);
            return this;
        }
    });
});

Custom Component Creation

Custom UI Component

define([
    'uiComponent',
    'knockout'
], function(Component, ko) {
    'use strict';
    
    return Component.extend({
        defaults: {
            template: 'Vendor_Module/custom-component',
            imports: {
                data: '${ $.provider }:data'
            },
            exports: {
                value: '${ $.provider }:value'
            }
        },
        
        initialize: function() {
            this._super();
            this.value = ko.observable('');
            return this;
        },
        
        getValue: function() {
            return this.value();
        },
        
        setValue: function(val) {
            this.value(val);
        }
    });
});

Component Configuration

<listing>
    <settings>
        <dataProvider class="Vendor\Module\Ui\DataProvider" name="dataProvider"/>
    </settings>
    <columns>
        <column name="custom" class="Vendor\Module\Ui\Component\Column">
            <settings>
                <dataType>text</dataType>
                <label translate="true">Custom Column</label>
            </settings>
        </column>
    </columns>
</listing>

Data Providers

<?php
namespace Vendor\Module\Ui\DataProvider;

use Magento\Ui\DataProvider\AbstractDataProvider;

class DataProvider extends AbstractDataProvider
{
    public function getData()
    {
        return [
            'data' => [
                'items' => $this->collection->getItems()
            ]
        ];
    }
}

Component Events

initialize: function() {
    this._super();
    
    // Listen to provider changes
    this.source.on('data.submitted', function(data) {
        console.log('Data submitted:', data);
    });
    
    return this;
}

Quiz

1. What is a UI Component mixin?

Question 1 options

2. Which method is called when a UI Component is ready?

Question 2 options

3. How do you extend a UI Component via inheritance?

Question 3 options

Flashcards

Question

What is the UI Component lifecycle?

Answer

initialize → initConfig → initElement → onReady → destroy

Question

How do you create a mixin?

Answer

Export function that takes target and returns target.extend()

Question

How do you extend a component?

Answer

Component.extend({ ... })

Question

What is a data provider?

Answer

PHP class that provides data to UI Components

Question

How do you register a mixin?

Answer

In requirejs-config.js under config.mixins

Revision Notes

Key Takeaways

  • 1. UI Components have a defined lifecycle with key methods
  • 2. Mixins extend components without modifying originals
  • 3. Components can be extended via inheritance or mixins
  • 4. Data providers supply data to UI Components
  • 5. Components use imports/exports for data flow

Interview Tips

  • Explain the UI Component lifecycle
  • Know when to use mixins vs inheritance
  • Discuss how to create custom UI Components
  • Be ready to extend an existing component

Cheat Sheet

Lifecycle: initialize → onReady → destroy

Mixin:
define([], function() {
    return function(target) {
        return target.extend({ ... });
    };
});

Extend:
Component.extend({
    defaults: { template: '...' },
    initialize: function() { this._super(); }
});