Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

SOS UiComponents

785 views

Published on

Working with Magento 2 UiComponents can be challenging.
This talk is about how to create and customize UiComponents without going crazy. The first part covers some general advice for writing self documenting code, the second (and in my opinion more interesting one) is about managing shared state in the view.
The slides where created for MageTitans Italy in April 2018.

Published in: Software
  • Visit this site: tinyurl.com/sexinarea and find sex in your area for one night)) You can find me on this site too)
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Sex in your area for one night is there tinyurl.com/hotsexinarea Copy and paste link in your browser to visit a site)
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Girls for sex are waiting for you https://bit.ly/2TQ8UAY
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Meetings for sex in your area are there: https://bit.ly/2TQ8UAY
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Best site for flirting and sex in your area you can find there: https://bit.ly/2SlcOnO
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

SOS UiComponents

  1. 1. SOSUiComponents SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  2. 2. SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  3. 3. Alternative titles: Lessons learned from UiComponents or My own best practices for UiComponents SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  4. 4. I'm assuming you have at least a (very) basic understanding of — require.js — Knockout.js — jQuery UI Widgets — UiComponents SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  5. 5. SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  6. 6. SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  7. 7. SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  8. 8. UiComponents So what is the problem? SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  9. 9. For me grokking UiComponents was one of the hardest things SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  10. 10. It still takes me a long time to do some things that I expect to be quick SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  11. 11. I try to understand UiComponents mainly by reading code Reading Code > Reading Docs SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  12. 12. So I try to optimize code for reading SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  13. 13. This talk 1. A little bit of whining 2. General advice on readable code 3. View state management SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  14. 14. Three issues I frequently encounter: 1. High cognitive load 2. Duplication of knowledge 3. State is coupled to the DOM SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  15. 15. If it was my job... ..to fix UiComponents SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  16. 16. Cognitive load Super verbose declaration (no sane defaults) SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  17. 17. Cognitive load Mixing of concepts (Knockout.js and jQuery UI Widgets) SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  18. 18. Cognitive load Inconsistent use in core SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  19. 19. Self Documenting CodeSOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  20. 20. Rules I try to apply to code I write. When working with the core code... well, it is what it is. SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  21. 21. 4 Rules of simple design by Kent Beck: 1. Passes all tests 2. Reveals intent 3. No duplication 4. Fewest elements SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  22. 22. Passes all tests The code has to work. SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  23. 23. Reveals intent SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  24. 24. Reveals intent Good naming — Descriptive base class names — Intent of functions, not implementation — Descriptive properties and variables SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  25. 25. Reveals intent bad: interface ButtonProviderInterface { /** * Retrieve button-specified settings * * @return array */ public function getButtonData(); } SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  26. 26. Reveals intent because: public function getButtonData() { // ... return [ 'label' => __('Save'), 'class' => 'save primary', 'data_attribute' => [ 'mage-init' => [ 'buttonAdapter' => [ 'actions' => [ [ 'targetName' => 'product_form.product_form', 'actionName' => 'save', 'params' => [false] ] ] ] ] ], 'class_name' => Container::SPLIT_BUTTON, 'options' => $this->getOptions(), ]; } SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  27. 27. Reveals intent better: interface ButtonProviderInterface { public function getLabel(); public function getCssClassNames(); public function getActions(); ... } SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  28. 28. Reveals intent bad: { getSortOrder: function () { return this.order; } } SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  29. 29. Reveals intent good: { isDescending: function() { return this.order === SORT_DESC; }, isAscending: function() { return this.order === SORT_ASC; } } SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  30. 30. Reveals intent Try to use a style familiar to the most likely reader SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  31. 31. Reveals intent Favor UiComponent JSON over XML SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  32. 32. Reveals intent <item name="progressBar" xsi:type="array"> <item name="sortOrder" xsi:type="string">0</item> <item name="component" xsi:type="string">Magento_Checkout/js/view/progress-bar</item> <item name="displayArea" xsi:type="string">progressBar</item> <item name="config" xsi:type="array"> <item name="deps" xsi:type="array"> <item name="0" xsi:type="string">checkout.steps.shipping-step.shippingAddress</item> <item name="1" xsi:type="string">checkout.steps.billing-step.payment</item> </item> </item> </item> SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  33. 33. Reveals intent { "progressBar": { "component": "Magento_Checkout/js/view/progress-bar", "sortOrder": 0, "displayArea": "progressBar", "config": { "deps": [ "checkout.steps.shipping-step.shippingAddress", "checkout.steps.billing-step.payment" ] } } } SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  34. 34. No duplication SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  35. 35. No duplication Keep knowledge in one place SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  36. 36. No duplication Duplication is a common problem in M2 EAV, DataInterfaces, UiComponents, GraphQL, ... How does it apply in the view layer? SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  37. 37. No duplication Avoid selectors in view models loginFormSelector = 'form[data-role=email-with-possible-login]', SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  38. 38. No duplication :'-( miniCart.sidebar({ 'targetElement': 'div.block.block-minicart', 'button': { 'checkout': '#top-cart-btn-checkout', 'remove': '#mini-cart a.action.delete', 'close': '#btn-minicart-close' }, 'showcart': { 'parent': 'span.counter', 'qty': 'span.counter-number', 'label': 'span.counter-label' }, 'minicart': { 'list': '#mini-cart', 'content': '#minicart-content-wrapper', 'qty': 'div.items-total', 'subtotal': 'div.subtotal span.price' }, 'item': { 'qty': ':input.cart-item-qty', 'button': ':button.update-cart-item' } }); SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  39. 39. No duplication If selectors are required? Keep the definition and the configuration as close as possible, ideally in the same file SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  40. 40. No duplication DOM selector declaration and config <div data-bind="scope: 'foo'"> <div data-role="message-container">...</div> </div> <script type="text/x-magento-init"> { "*": { "Magento_Ui/js/core/app": { "components": { "foo": { "component": "MyCompany_MyModule/js/view/foo", "containerSelector": "div[data-role=message-container]" } } } } } </script> SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  41. 41. No duplication UiComponent selector declaration and config <div data-bind="scope: 'messages'"> ... </div> <script type="text/x-magento-init"> { "*": { "Magento_Ui/js/core/app": { "components": { "messages": { "component": "Magento_Theme/js/view/messages" } } } } } </script> SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  42. 42. No duplication Keep knowledge in one file and close together. <script type="text/x-magento-init"> { "*": { "Magento_Ui/js/core/app": { "components": { "appRoot": { "component": "My_Module/js/root" "children": { "thingA": { "component: "My_Module/js/thing-a" }, "thingB": { "component: "My_Module/js/thing-b", "sibling":" "thingA" } } } } } } } </script> SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  43. 43. No duplication Injecting DOM-Nodes is better E.g. with a custom binding: <div data-bind="scope: 'myViewModel', bindDomNode: true"> </div> SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  44. 44. No duplication Custom binding handler declaration: define(['ko'], function(ko) { 'use strict'; ko.bindingHandlers.bindDomNode = { init: function(element, valueAccessor, _a, _b, bindingContext) { if (valueAccessor()) { bindingContext.$data.domNode = element; } } }; }); SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  45. 45. No duplication Example access of injected DOM node the view model: this.domNode.getBoundingClientRect(); SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  46. 46. Fewest elements SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  47. 47. Fewest elements If something works, expresses intent and contains no duplicate knowledge, don't split it further SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  48. 48. Fewest elements The Single Responsibility Principle is not the Single Public Method Principle. SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  49. 49. Fewest elements If things are related, keep them together. SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  50. 50. Managing State SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  51. 51. Context: Magento <= 2.2.2 Not a SPA/PWA SRP with several frontend "components", each with its own more or less independent state. SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  52. 52. We have three options. 1. Keep state in the DOM or UiComponents 2. Pass state from root down to children 3. Keep state outside of the view SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  53. 53. Example TicTacToe https://github.com/Vinai/example-module-tictactoe SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  54. 54. Example TicTacToe Shamelessly stolen from https://reactjs.org/tutorial/tutorial.html SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  55. 55. Example TicTacToe Simple example for each approach to managing shared state: https://github.com/Vinai/example-module-tictactoe $ git branch * 0-in-component-state 1-pass-to-children 2-external-state SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  56. 56. 1. Keep state in the DOM or UiComponents E.g. jQuery or pure Knockout.js Only for simplest, single component cases SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  57. 57. 1. State in the DOM How is state stored in the DOM? — Form element values — CSS classes (e.g. "active" or "invalid") — Inline styles (e.g. "display: none") — Element attributes (e.g. disabled) SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  58. 58. 1. State in the DOM DOM state storage is common with jQuery self.element.off('submit'); // disable 'Add to Cart' button addToCartButton = $(form).find(this.options.addToCartButtonSelector); addToCartButton.prop('disabled', true); addToCartButton.addClass(this.options.addToCartButtonDisabledClass); form.submit(); SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  59. 59. 1. State in UiComponents Properties on the knockout view model Shared state: imports, exports, links SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  60. 60. 1. State in UiComponents Component.extend({ defaults: { links: { squares: '${ $.provider }:squares' }, imports: { xIsNext: '${ $.provider }:xIsNext', winner: '${ $.provider }:winner' } }, square: function () { return this.squares[this.squareIndex]; } handleClick: function () { const square = this.square(); if ('' === square() && !this.winner) { square(this.xIsNext ? 'X' : 'O'); } } }); SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  61. 61. 1. Shared state in DOM or UiComponents Strong coupling through cross-component dependencies SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  62. 62. 1. Derived state In this approach state derived from one model is o!en stored in another model SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  63. 63. 1. Derived State updateFoo: function() { this.foo = this.other.bar * this.other.bar; } // vs. foo: function() { return this.other.bar * this.other.bar; } SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  64. 64. 2. Pass state from root down to children E.g. pure react components Works within one component hierarchy SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  65. 65. 2. Example from React TicTacToe <Board squares={current.squares} onClick={i => this.handleClick(i)} /> class Board extends React.Component { renderSquare(i) { return ( <Square value={this.props.squares[i]} onClick={() => this.props.onClick(i)} /> ); } SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  66. 66. 2. Pass state from root down to children Problem when state is required outside one hierarchy SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  67. 67. 2. Pass state from root down to children Not common in Magento 2 SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  68. 68. 2. Pass state from root down to children No callback on parent, only on child Component.extend({ initContainer: function (parent) { const state = parent.state; this.cells = [...state.squares.keys()].map(function (i) { return new Square({index: i, state: state}); }); return this._super(parent); } ... } SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  69. 69. 3. Keep state outside the view E.g. Elm architecture, flux, redux/vuex Most versatile but needs more plumbing SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  70. 70. 3. Keep state outside the view There are differences: Naming, (im)mutability, 1 or 2 way data flow, (F)RP, effects handling, amount of architecture... But there always is a single state storage SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  71. 71. 3. Separate state with UiComponents State object with observable properties define(['ko'], function (ko) { 'use strict'; return ko.track({ moves: [], squares: [...Array(9).keys()].map(() => ko.observable('')), xIsNext: true, winner: false }); }); SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  72. 72. 3. Separate state with UiComponents Require and use state container object define( ['uiComponent', 'ticTacToeState'], function (Component, state) { 'use strict'; return Component.extend({ ... handleClick: function () { if ('' === this.value() && !state.winner) { state.squares[this.index](state.xIsNext ? 'X' : 'O'); } }, }); } ); SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  73. 73. Example Breakout https://github.com/Vinai/example-module-breakout SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  74. 74. 3. Multiple state storage objects define( ['gameComponent', 'ballState', 'frameState', 'gameState'], function (GameComponent, ball, frame, game) { 'use strict'; return GameComponent.extend({ initialize: function () { this._super(); this.initDimensions(frame); frame.width = function () { return game.width * .98; }; frame.height = function () { return game.height * .80; }; frame.left = function () { return game.width * .01; }; frame.top = function () { return game.height * .19; }; }, ... SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  75. 75. 3. State outside the view Benefits: — State is simple to share — View models contain mostly only logic — Less code — Easier to understand — More maintainable SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  76. 76. Recommendations for maintainable UiComponents... SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  77. 77. The code needs to... 1. Pass all tests 2. Reveal intent 3. Contain no duplication 4. Have the fewest possible elements 5. Separate shared state from the view SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018
  78. 78. Thanks to Kent Beck for his timeless 4 rules of simple design. Thank you for your attention! SOS UiComponents - (c) @VinaiKopp - for #MageTitansIT, Milano, 06. April 2018

×