Congratulations!

[Valid RSS] This is a valid RSS feed.

Recommendations

This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: http://blog.riand.com/feeds/posts/default?alt=rss

  1. <?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-7557605395826374162</atom:id><lastBuildDate>Tue, 05 Mar 2024 10:02:41 +0000</lastBuildDate><category>lwc</category><category>web components</category><title>Elementary, My Dear Watson!</title><description></description><link>http://blog.riand.com/</link><managingEditor>noreply@blogger.com (Philippe Riand)</managingEditor><generator>Blogger</generator><openSearch:totalResults>40</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-7707254757976016113</guid><pubDate>Sun, 15 Mar 2020 20:13:00 +0000</pubDate><atom:updated>2020-03-15T14:09:13.775-07:00</atom:updated><title>LWC: A sample fetch data accessor</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  2. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  3. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNnX62gnbtAGMlOjm7u_C2oJqhX7N-P8s6o6ZahrsQ1FsKJWBYRkyibsS0PBZwXdP97Bg2xi4r2EwgAYzytdviDOZ0kSXv3-mhXHop556C13yQ3xjry__aa6F-1rdSn8JeD30zISRyIHI/s1600/fetch-wire.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;287&quot; data-original-width=&quot;628&quot; height=&quot;146&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNnX62gnbtAGMlOjm7u_C2oJqhX7N-P8s6o6ZahrsQ1FsKJWBYRkyibsS0PBZwXdP97Bg2xi4r2EwgAYzytdviDOZ0kSXv3-mhXHop556C13yQ3xjry__aa6F-1rdSn8JeD30zISRyIHI/s320/fetch-wire.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  4. &lt;br /&gt;
  5. What would an LWC application be if it does not fetch data from a server? The browser now made this easy, particularly with the fetch function, or its polyfill for older browsers. The general pattern with LWC/Web Components is to fetch data in &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;connectedCallback()&lt;/span&gt;, once the component is added to the DOM. If this can be achieved with simple code, let see how we can make it even easier with wire adapters. In particular, we like to do the following:&lt;br /&gt;
  6. &lt;ul style=&quot;text-align: left;&quot;&gt;
  7. &lt;li&gt;Have a simple syntax for calling the services.&lt;/li&gt;
  8. &lt;li&gt;Do not hard code the full URL in the components, so we can easily, and globally, switch the endpoint (dev, staging, prod...), or even mock the server for test purposes.&amp;nbsp;&lt;/li&gt;
  9. &lt;li&gt;Use URL variable parts, or query parameters.&lt;/li&gt;
  10. &lt;li&gt;Be able to re-fetch the data when needed, in particular when the variable values change.&lt;/li&gt;
  11. &lt;/ul&gt;
  12. The code that illustrates this post is available as part of the LWC essentials: &lt;a href=&quot;https://github.com/LWC-Essentials/wire-adapters/tree/master/packages/fetch&quot;&gt;https://github.com/LWC-Essentials/wire-adapters/tree/master/packages/fetch&lt;/a&gt;. Feel free to check it out and submit enhancements. or suggestions! You can also consume the NPM package &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;@lwce/fetch&lt;/span&gt;.&lt;br /&gt;
  13. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  14. Using the fetch wire adapter&amp;nbsp;&lt;/h2&gt;
  15. &lt;div style=&quot;text-align: left;&quot;&gt;
  16. The syntax is straightforward:&lt;/div&gt;
  17. &lt;div style=&quot;text-align: left;&quot;&gt;
  18. &lt;br /&gt;&lt;/div&gt;
  19. &lt;div style=&quot;text-align: left;&quot;&gt;
  20. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;@wire(useFetch, options) myproperty;&lt;/span&gt;&lt;/div&gt;
  21. &lt;br /&gt;
  22. Let see an an example showing the wire adapter in action. This one calls a user service with query parameters:&lt;br /&gt;
  23. &lt;br /&gt;
  24. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;export default class UserList extends LightningElement {&lt;br /&gt;&amp;nbsp;&amp;nbsp; @track queryParams = {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset: 0,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; limit: 10&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @wire(useFetch, {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; url: &#39;/users&#39;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; queryParams: &#39;$queryParams&#39;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }) users;&lt;/span&gt;&lt;br /&gt;
  25. &lt;br /&gt;
  26. When the component is initialized, the fetch request is executed and the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;users&lt;/span&gt; variables get the result.&lt;br /&gt;
  27. Requests can also be executed explicitly. Obviously, this is useful for update requests, but also for on demand GET requests:&lt;br /&gt;
  28. &lt;br /&gt;
  29. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;export default class Users extends LightningElement {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @track variables = {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; userId: &#39;xyz&#39;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @wire(useFetch, {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; url: &#39;/users/{userId}&#39;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; lazy: true,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; variables: &#39;$variables&#39;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }) userById;&lt;/span&gt;&lt;br /&gt;
  30. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; handleUserClick(event) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const idx = this._findUserIdx(event);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(idx!==undefined) {&lt;/span&gt;&lt;br /&gt;
  31. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const &lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;userId&lt;/span&gt; = &lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;this.users.data.users[idx].email;&lt;/span&gt;&lt;br /&gt;
  32. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;this.userById.fetch( {variables:{userId} ).then( ()=&amp;gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; alert(JSON.stringify(this.userById.data));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; });&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
  33. &lt;br /&gt;
  34. The result property exposes a &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;fetch()&lt;/span&gt; method that can be called programmatically, while the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;lazy&lt;/span&gt; parameter of the wire adapter prevent the automatic execution when the component is initialized.&lt;br /&gt;
  35. &lt;br /&gt;
  36. There are several other options, but I don&#39;t want to go too much into the use of the adapter, as the &lt;a href=&quot;https://github.com/salesforce/lwc-essentials/blob/master/packages/fetch/README.md&quot;&gt;README.md&lt;/a&gt; file already does that pretty extensively.&lt;br /&gt;
  37. &lt;br /&gt;
  38. Let&#39;s rather dive into the implementation details. &lt;br /&gt;
  39. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  40. Implementation details &lt;/h2&gt;
  41. The fetch wire adapter implementation is fairly straightforward, but there are a few traps that we should be aware of.&lt;br /&gt;
  42. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  43. Duplicating the result object&lt;/h3&gt;
  44. The current result object is kept in a variable within the wire adapter closure, and new values are simply assigned to this object before it is sent to the component. This makes it easier from an implementation standpoint, for example setting the invariant values, like the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;fetchClient&lt;/span&gt; instance, or the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;fetch()&lt;/span&gt; method.&lt;br /&gt;
  45. But has a drawback: the component will only detect a change, and thus redraw, when a new object is sent by the wire adapter. So the object kept by the adapter can&#39;t be sent as is to the component. Rather, the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;update()&lt;/span&gt; function duplicates it and send the copy to the component. &lt;br /&gt;
  46. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  47. Concurrent requests&lt;/h3&gt;
  48. &lt;div style=&quot;text-align: left;&quot;&gt;
  49. The result from a &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;fetch()&lt;/span&gt; call is by definition retrieved asynchronously, which means that we do not control when that result will be available. Moreover, if multiple requests are sent sequentially by the same wire adapter, there is no guarantee that the respective results will come in the same order than the requests were emitted. Imagine a UI featuring a button that triggers a change in the wire adapter. If a user frenziedly clicks that button, it is likely that new requests will be emitted by the wire adapter before the results of the previous ones come back, with no guarantee that the results will be retrieved in order. This is well known issue in JavaScript based UI, generally hard to detect and debug.&lt;/div&gt;
  50. &lt;div style=&quot;text-align: left;&quot;&gt;
  51. &lt;br /&gt;&lt;/div&gt;
  52. &lt;div style=&quot;text-align: left;&quot;&gt;
  53. Wire adapters, if not designed appropriately, are no exception. I created a code snippet to showcase the issue: &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/tools/playground/8peP8wto/12/edit&quot;&gt;https://developer.salesforce.com/docs/component-library/tools/playground/8peP8wto/12/edit.&lt;/a&gt;&lt;/div&gt;
  54. &lt;div style=&quot;text-align: left;&quot;&gt;
  55. &lt;br /&gt;&lt;/div&gt;
  56. &lt;div style=&quot;text-align: left;&quot;&gt;
  57. Alright, now that we are aware of this issue, here is a set of potential solutions:&lt;/div&gt;
  58. &lt;ol style=&quot;text-align: left;&quot;&gt;
  59. &lt;li&gt;Prevent a new request from being emitted while one is still pending&amp;nbsp;&lt;/li&gt;
  60. &lt;li&gt;Ignore the oldest ones when their result comes back and only process the latest one&lt;/li&gt;
  61. &lt;/ol&gt;
  62. &lt;div style=&quot;text-align: left;&quot;&gt;
  63. If 1. is easy to implement, it does not provide the best user experience because the UI is frozen until the current request&#39;s result comes back. On the other hand, it can also be implement at the application level, for example by disabling the button until the request comes back.&lt;/div&gt;
  64. &lt;div style=&quot;text-align: left;&quot;&gt;
  65. The fetch wire adapter implements the second one by default. It actually associates a unique index to each request, keeps it in the closure of response handler (&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;fetchIndex&lt;/span&gt;), and also globally keeps the index of the latest request (&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;fetchCurrent&lt;/span&gt;). When a result comes back, it only processes it if it corresponds to the latest request. All other results are simply ignored.&lt;/div&gt;
  66. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  67. As a conclusion...&lt;/h2&gt;
  68. &lt;div style=&quot;text-align: left;&quot;&gt;
  69. Basic wire adapters are easy to implement, while providing a unified way to expose data to component developers. Jump over the fence, and start to create yours! Or extend the one we are providing as part of the&amp;nbsp;&lt;a href=&quot;https://github.com/LWC-Essentials/wire-adapters&quot;&gt;LWC-Essentials/wire-adapters&lt;/a&gt; project. I&#39;d love to grow this repo with your contributions.&lt;/div&gt;
  70. &lt;div style=&quot;text-align: left;&quot;&gt;
  71. &lt;br /&gt;&lt;/div&gt;
  72. &lt;div style=&quot;text-align: left;&quot;&gt;
  73. &lt;br /&gt;&lt;/div&gt;
  74. &lt;/div&gt;
  75. </description><link>http://blog.riand.com/2020/03/lwc-sample-fetch-data-accessor.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNnX62gnbtAGMlOjm7u_C2oJqhX7N-P8s6o6ZahrsQ1FsKJWBYRkyibsS0PBZwXdP97Bg2xi4r2EwgAYzytdviDOZ0kSXv3-mhXHop556C13yQ3xjry__aa6F-1rdSn8JeD30zISRyIHI/s72-c/fetch-wire.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-525144314981796537</guid><pubDate>Mon, 10 Feb 2020 17:29:00 +0000</pubDate><atom:updated>2020-03-27T18:34:21.320-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">lwc</category><category domain="http://www.blogger.com/atom/ns#">web components</category><title>LWC: Accessing GraphQL data with the Apollo client</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  76. &lt;div&gt;
  77. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  78. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdhk2fL-W25TyMZxr-oeuEL500bzdh1RB2FfdSgsG5UQIRRP-5YSKG8KJbVBohF2A89LhOEdTc8Yba7N2H5U-prRTpYINEi57K96YVC3ylxWZ5hsDCGNwb2g1dwJ6Y3lwd52gUocvSpak/s1600/apollo+client.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;390&quot; data-original-width=&quot;967&quot; height=&quot;129&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdhk2fL-W25TyMZxr-oeuEL500bzdh1RB2FfdSgsG5UQIRRP-5YSKG8KJbVBohF2A89LhOEdTc8Yba7N2H5U-prRTpYINEi57K96YVC3ylxWZ5hsDCGNwb2g1dwJ6Y3lwd52gUocvSpak/s320/apollo+client.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  79. &lt;br /&gt;
  80. &lt;br /&gt;
  81. If you&#39;re a front-end developer calling &lt;a href=&quot;https://graphql.org/&quot;&gt;GraphQL&lt;/a&gt; endpoints from your UI, you must have heard about the JavaScript &lt;a href=&quot;https://www.apollographql.com/docs/react/&quot;&gt;Apollo Client&lt;/a&gt; library. This library is a state management library that let developers execute GraphQL queries against GraphQL servers as well as a local store. It handles caching, change notifications and features interesting capabilities like &lt;a href=&quot;https://www.apollographql.com/docs/react/performance/optimistic-ui/&quot;&gt;optimistic UI&lt;/a&gt;.&lt;br /&gt;
  82. &lt;br /&gt;
  83. The flagship implementation from the Apollo team is dedicated to React but, fortunately, the implementation is split in two layers:&lt;br /&gt;
  84. &lt;ul style=&quot;text-align: left;&quot;&gt;
  85. &lt;li&gt;A pure JavaScript &lt;a href=&quot;https://www.apollographql.com/docs/react/api/apollo-client/&quot;&gt;Apollo Client library&lt;/a&gt;&lt;br /&gt;This library is not related to any client technology. It implements all the necessary plumbing to support GraphQL queries in JavaScript.&lt;/li&gt;
  86. &lt;li&gt;&lt;a href=&quot;https://www.apollographql.com/docs/react/integrations/integrations/&quot;&gt;View integrations&lt;/a&gt;&lt;br /&gt;Provided on top of the aforementioned library, these integrations make it easier to consume the Apollo client with your technology of choice (React, Angular, Vue, Svelte, Ember, ...). If the React and Angular integrations are done by the Apollo team, the others are provided by third party contributors.&lt;/li&gt;
  87. &lt;/ul&gt;
  88. &lt;/div&gt;
  89. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  90. Integrating LWC&lt;/h2&gt;
  91. There is a &lt;a href=&quot;https://github.com/apollo-elements/apollo-elements&quot;&gt;Web Component integration&lt;/a&gt; available, which can work with LWC as well. Actually, the LWC adapter code on top of this library would be minimal. On the other hand, it won&#39;t take advantage of the LWC specific features, in particular the wire adapters. So the idea is to create a new integration specifically for LWC, leveraging wire adapters similarly to hooks in the React integration.&lt;br /&gt;
  92. &lt;br /&gt;
  93. For the impatient, the integration is available here: &lt;a href=&quot;https://www.npmjs.com/package/@lwce/apollo-client&quot;&gt;apollo-client.&lt;/a&gt; It provides a sample application that shows CRUD operations on a server, in memory list of books.&lt;br /&gt;
  94. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  95. GraphQL Queries&lt;/h2&gt;
  96. &lt;div style=&quot;text-align: left;&quot;&gt;
  97. A GraphQL query in the React integration uses a hook named &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;useQuery&lt;/span&gt;.&amp;nbsp; Here is an example:&lt;/div&gt;
  98. &lt;div style=&quot;text-align: left;&quot;&gt;
  99. &lt;br /&gt;&lt;/div&gt;
  100. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; const { loading, error, data } = useQuery(MY_QUERY);&lt;/span&gt;&lt;br /&gt;
  101. &lt;br /&gt;
  102. A very similar syntax can be achieved with LWC wire adapters. We even keep the same name, &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;useQuery&lt;/span&gt;, for the wire adapter:&lt;br /&gt;
  103. &lt;br /&gt;
  104. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; @wire(useQuery, { query: MY_QUERY }) results;&lt;/span&gt;&lt;br /&gt;
  105. &lt;br /&gt;
  106. The &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;results&lt;/span&gt; property features the following sub properties, similarly to the React hook:&lt;br /&gt;
  107. &lt;br /&gt;
  108. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; { loading, error, data }&lt;/span&gt;&lt;br /&gt;
  109. &lt;br /&gt;
  110. Any of these sub properties is available at any time, so the component template can display appropriate content, with pseudo code like:&lt;br /&gt;
  111. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;template if:true={results.loading}&amp;gt;&lt;/span&gt;&lt;br /&gt;
  112. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;lt;img src=&quot;loading.gif&quot;/&amp;gt;Loading...&lt;/span&gt; &lt;br /&gt;
  113. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;/template&amp;gt;&lt;/span&gt;&lt;br /&gt;
  114. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;template if:true={results.error}&amp;gt;&lt;/span&gt;&lt;br /&gt;
  115. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; Man, there is an error {results.error} &lt;/span&gt;&lt;br /&gt;
  116. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;/template&amp;gt;&lt;/span&gt;&lt;br /&gt;
  117. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;template if:true={results.data}&amp;gt;&lt;/span&gt;&lt;br /&gt;
  118. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; Here is the data: {results.data} &lt;/span&gt;&lt;br /&gt;
  119. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;/template&amp;gt;&lt;/span&gt;&lt;br /&gt;
  120. &lt;br /&gt;
  121. The &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;results&lt;/span&gt; property also features even more sub properties. In particular, it offers a &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;fetch()&lt;/span&gt; method that re-fetches the query.&lt;br /&gt;
  122. &lt;br /&gt;
  123. Finally, a wire adapter has more options than just the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;query&lt;/span&gt;: it can use a specific Apollo &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;client&lt;/span&gt;,&amp;nbsp; defer the query execution with &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;lazy&lt;/span&gt;,&amp;nbsp; use &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;variables&lt;/span&gt; and any other standard options used by the Apollo client &lt;a href=&quot;https://www.apollographql.com/docs/react/api/apollo-client/#ApolloClient.watchQuery&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;watchQuery()&lt;/span&gt;&lt;/a&gt;.&amp;nbsp; See &lt;a href=&quot;https://github.com/LWC-Essentials/apollo-client/blob/master/packages/apollo-client/README.md&quot;&gt;README.md&lt;/a&gt; for a more exhaustive list of options and how to use them. &lt;br /&gt;
  124. &lt;br /&gt;
  125. Here is a example of a query using variables for pagination, assuming that the query supports &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;offset&lt;/span&gt; and &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;limit&lt;/span&gt; parameters:&lt;br /&gt;
  126. &lt;br /&gt;
  127. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; const BOOK_LIST = gql`&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; query($offset: Int!, $limit: Int!) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; books(offset: $offset, limit: $limit) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; id,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; title&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; author&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }`;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
  128. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; class Booklist extends LightningElement {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; variables = {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset: 0,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; limit: 10&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @wire(useQuery, {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; query: BOOK_LIST,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; variables: &#39;$variables&#39;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }) books;&lt;/span&gt;&lt;br /&gt;
  129. &lt;br /&gt;
  130. Here is an example using the lazy mode, which requires a specific call to &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;&quot;&gt;fetch()&lt;/span&gt;:&lt;br /&gt;
  131. &lt;br /&gt;
  132. &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @wire(useQuery, {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; query: USERS,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; lazy: true&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }) users;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; readUsers() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.users.fetch().then( () =&amp;gt; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Notification that the data has been updated&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Do something with the @wire data member...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; })&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
  133. &lt;br /&gt;
  134. A more complete example is available in the project &lt;a href=&quot;https://github.com/salesforce/lwc-apollo-client/tree/master/packages/sample-app&quot;&gt;sample application&lt;/a&gt;.&lt;br /&gt;
  135. &lt;br /&gt;
  136. Because wire adapters options can only observe changes to root properties, we created the variables property to hold the query variables. Then, when we want to update one of its value (ex: &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;offset&lt;/span&gt;), then we have to assign a brand new object to that property:&lt;br /&gt;
  137. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; handleFirst() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.variables = {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...this.variables,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset: 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
  138. &lt;br /&gt;
  139. Finally, if it is possible to use the query inline, it is better to declare it as a constant (&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;BOOK_LIST&lt;/span&gt; in the example). This is because the current wire adapter implementation will repeat that value multiple times in the generated code.&lt;br /&gt;
  140. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  141. GraphQL Mutations&lt;/h2&gt;
  142. Mutations are also provided using wire adapters, although they should be executed on demand as opposed to when the component is connected. The wire adapter provides a &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;mutate&lt;/span&gt; method that can be called anytime to execute the mutation.&lt;br /&gt;
  143. &lt;br /&gt;
  144. Bellow is a simple sample that creates a book:&lt;br /&gt;
  145. &lt;br /&gt;
  146. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; // Define the mutation query &lt;/span&gt;&lt;br /&gt;
  147. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; const BOOK_CREATE = gql`&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; mutation createBook($book: BookInput!) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; createBook(book: $book) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; id&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; title&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; author&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; genre&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; publisher&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; `;&lt;/span&gt;&lt;br /&gt;
  148. &lt;br /&gt;
  149. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; // Mutation adapter use in the component class &lt;/span&gt;&lt;br /&gt;
  150. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; @wire(useMutation, {mutation: BOOK_CREATE}) bookCreate;&lt;br /&gt;&amp;nbsp; onCreateBook() { &lt;/span&gt;&lt;br /&gt;
  151. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; const variables = {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; book: {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; title: &quot;A book&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; author: &quot;John Doe&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; genre: &quot;Novel&quot;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; publisher: &quot;MyBook&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; };&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.bookCreate.mutate({&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;variables&lt;/span&gt;&lt;br /&gt;
  152. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp; });&lt;/span&gt;&lt;br /&gt;
  153. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; } &lt;/span&gt;&lt;br /&gt;
  154. &lt;br /&gt;
  155. The sample app coming with the project shows all the CRUD operations on books: Create, Read, Update and Delete.&lt;br /&gt;
  156. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  157. And What&#39;s Next?&lt;/h2&gt;
  158. Well this project is a first shot. A lot more has to be done, but this is a good start, isn&#39;t it? It needs your contributions to be on par with the React implementation. Please feel free to submit your ideas, code or examples.&lt;/div&gt;
  159. </description><link>http://blog.riand.com/2020/02/lwc-accessing-graphql-data-with-apollo.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdhk2fL-W25TyMZxr-oeuEL500bzdh1RB2FfdSgsG5UQIRRP-5YSKG8KJbVBohF2A89LhOEdTc8Yba7N2H5U-prRTpYINEi57K96YVC3ylxWZ5hsDCGNwb2g1dwJ6Y3lwd52gUocvSpak/s72-c/apollo+client.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-5722800711148429507</guid><pubDate>Wed, 05 Feb 2020 18:07:00 +0000</pubDate><atom:updated>2020-02-05T10:07:24.007-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">lwc</category><category domain="http://www.blogger.com/atom/ns#">web components</category><title>LWC Reactive State Manager</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  160. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  161. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6iuIhlBwqO8inkDv33AA1YQ9NAGP9H3o-17dhqF3VtiQZBBcgprIRF4Wou_h6MYBSkzA-yqIrhm3NrplxED9xPx8fVeSp63madAVkcD_1gnvf3SjAv-0cSvtKGBqcFr-DTiR5x0s2rGQ/s1600/reactive-state.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;524&quot; data-original-width=&quot;1048&quot; height=&quot;160&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6iuIhlBwqO8inkDv33AA1YQ9NAGP9H3o-17dhqF3VtiQZBBcgprIRF4Wou_h6MYBSkzA-yqIrhm3NrplxED9xPx8fVeSp63madAVkcD_1gnvf3SjAv-0cSvtKGBqcFr-DTiR5x0s2rGQ/s320/reactive-state.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  162. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  163. &lt;br /&gt;&lt;/div&gt;
  164. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  165. &lt;/div&gt;
  166. &lt;br /&gt;
  167. If you started to create LWC applications, you might have found that managing the state of a component is trivial, thanks to the reactive properties and the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;@track&lt;/span&gt; decorator. You do not have to deal with a specific state property, or explicitly ask the component to render when its state changed.&lt;br /&gt;
  168. &lt;br /&gt;
  169. Now, when your application gets more complex, you often want this state to be shared with other components. This is when state managers, like &lt;a href=&quot;https://redux.js.org/&quot;&gt;Redux&lt;/a&gt; or &lt;a href=&quot;https://mobx.js.org/README.html&quot;&gt;MobX&lt;/a&gt;, come into play. As these libraries are technology agnostic, you can obviously use them with LWC components. To make it easier, you can even create a few helpers, typically wire adapters.&lt;br /&gt;
  170. &lt;br /&gt;
  171. But can we create a store manager that is closer to LWC, leveraging the core LWC capabilities to provide global state management?&lt;br /&gt;
  172. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  173. Towards a Reactive LWC Store&lt;/h2&gt;
  174. First, the code described bellow is available as a library published here: &lt;a href=&quot;https://www.npmjs.com/package/@lwce/store&quot;&gt;https://www.npmjs.com/package/@lwce/store&lt;/a&gt;. The source code is on Github: &lt;a href=&quot;https://github.com/LWC-Essentials/wire-adapters/tree/master/packages/store&quot;&gt;https://github.com/LWC-Essentials/wire-adapters/tree/master/packages/store&lt;/a&gt;.&lt;br /&gt;
  175. &lt;br /&gt;
  176. The idea is simple: we can have a global object, the &quot;store&quot;, that holds [key:value] entries. Then, we&#39;ll use a wire adapter that lets a component access any entry in the store using a key:&lt;br /&gt;
  177. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; @wire(useStore, {key: &#39;user&#39;}) user;&lt;/span&gt;&lt;br /&gt;
  178. &lt;br /&gt;
  179. That&#39;s it. The user property is &quot;reactive&quot; so any change to its content will be tracked by the component. Multiple components can share the same entry coming from the store, by using the same key.&amp;nbsp; Even pure JavaScript code can access an entry programmatically:&lt;br /&gt;
  180. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; const u = getEntry(&#39;user&#39;)&lt;/span&gt;&lt;br /&gt;
  181. &lt;br /&gt;
  182. To make all the entries reactive, we need the global store object, holding all these entries, to be itself reactive. The LWC component reactivity is provided by a specific membrane, called the reactive membrane, which is private to the runtime. The solution is then to create an invisible component that holds the whole store object as a reactive property. This component is silently created when we initialize the store using:&lt;br /&gt;
  183. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; initStore(content?: object)&lt;/span&gt;&lt;br /&gt;
  184. &lt;div style=&quot;text-align: left;&quot;&gt;
  185. &lt;br /&gt;&lt;/div&gt;
  186. &lt;div style=&quot;text-align: left;&quot;&gt;
  187. The&amp;nbsp; content parameter is optional, just in case we want to initialize the store with initial values. It can be useful, for example, when the store is pre-populated from server side rendering results.&lt;/div&gt;
  188. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  189. Adding Entries to the Store&lt;/h2&gt;
  190. Accessing a store value using the &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;&quot;&gt;@wire&lt;/span&gt; or the API is simple, but how do we add an entry to that store? Well, the answer is straightforward: lazily, when we need it. In other words, an entry is created in the store the first time it is requested by key. And, right after being created, it gets initialized. For this, the developer can register initializers that will be invoked during this step:&lt;br /&gt;
  191. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; registerInitializer(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (key) =&amp;gt; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(key===&#39;user&#39;) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return {name: &#39;Doe&#39;, first: &#39;John&#39;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return undefined; // not for this entry&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; );&lt;/span&gt;&lt;br /&gt;
  192. &lt;br /&gt;
  193. If the initialization data is not immediately available, for example because the data comes from a network request, an initializer can return a &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;Promise&lt;/span&gt;. The store will consume the value when the promise will be resolved. By the meantime, the consumer can check some boolean values to understand the status of the entry, see bellow. &lt;br /&gt;
  194. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  195. Rich Entry Content&lt;/h2&gt;
  196. Entries in the store are actually not just exposing the raw data, but an object with the following properties:&lt;br /&gt;
  197. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; {&lt;/span&gt;&lt;br /&gt;
  198. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; data: any&lt;/span&gt;&lt;br /&gt;
  199. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; error: string&lt;/span&gt;&lt;br /&gt;
  200. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; loading: boolean&lt;/span&gt;&lt;br /&gt;
  201. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; initialized: boolean&lt;/span&gt;&lt;br /&gt;
  202. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
  203. &lt;br /&gt;
  204. So in our example above, the user content have to be accessed through the &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;&quot;&gt;data&lt;/span&gt; property:&lt;br /&gt;
  205. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp; const u = getEntry(&#39;user&#39;).data&lt;/span&gt;&lt;br /&gt;
  206. &lt;br /&gt;
  207. Similarly in a component template:&lt;br /&gt;
  208. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;p&amp;gt;{user.data.firstName} {user.data.lastName}&amp;lt;/p&amp;gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
  209. &lt;br /&gt;
  210. Because an entry contains &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;{error, loading, initialized}&lt;/span&gt;, a consumer can also check if:&lt;br /&gt;
  211. &lt;ul style=&quot;text-align: left;&quot;&gt;
  212. &lt;li&gt;An error happened during initialization. &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;&quot;&gt;error&lt;/span&gt; will then contain the error message.&lt;/li&gt;
  213. &lt;li&gt;The data is still being loaded&lt;/li&gt;
  214. &lt;li&gt;The data has already be initialized once&lt;/li&gt;
  215. &lt;/ul&gt;
  216. The difference between &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;loading&lt;/span&gt; and &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;initialized&lt;/span&gt; is subtle: as a store entry can be reloaded, loading can happen while data has already been loaded once. In that case, the developer can choose to provide a slightly different UI (ex: the previous data is still displayed with a loading icon while the entry is being reloaded).&lt;br /&gt;
  217. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  218. Putting the Store in Action&lt;/h2&gt;
  219. &lt;div style=&quot;text-align: left;&quot;&gt;
  220. The sample-app accompanying the library on Github shows a commerce cart built using this library: &lt;a href=&quot;https://github.com/LWC-Essentials/wire-adapters/tree/master/packages/sample-app&quot;&gt;https://github.com/LWC-Essentials/wire-adapters/tree/master/packages/sample-app&lt;/a&gt;&lt;/div&gt;
  221. &lt;div style=&quot;text-align: left;&quot;&gt;
  222. &lt;br /&gt;&lt;/div&gt;
  223. &lt;div style=&quot;text-align: left;&quot;&gt;
  224. All the components (cart, checkout, basket icon) are sharing the same &#39;cart&#39; store entry. They all update when cart changes:&lt;/div&gt;
  225. &lt;div style=&quot;text-align: left;&quot;&gt;
  226. &lt;br /&gt;&lt;/div&gt;
  227. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  228. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLxiFBrvLcte-l63kRqr-CA2QuVqMnfM057NQ25x7cv-3gNshvyn-1x9vIM9caicxEJmhzKBjls4RwvE7GmFkg3ISOgB6US6Fw4KrM_COGM0LUlr2kantZEUrLzk65KKdBIZw6oyPRi04/s1600/store-example.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;444&quot; data-original-width=&quot;1109&quot; height=&quot;256&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLxiFBrvLcte-l63kRqr-CA2QuVqMnfM057NQ25x7cv-3gNshvyn-1x9vIM9caicxEJmhzKBjls4RwvE7GmFkg3ISOgB6US6Fw4KrM_COGM0LUlr2kantZEUrLzk65KKdBIZw6oyPRi04/s640/store-example.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  229. &lt;div style=&quot;text-align: left;&quot;&gt;
  230. &lt;br /&gt;&lt;/div&gt;
  231. &lt;div style=&quot;text-align: left;&quot;&gt;
  232. As all the store entries are reactive, simply changing some values within the cart makes all the components re-render.&lt;/div&gt;
  233. &lt;br /&gt;
  234. &lt;br /&gt;&lt;/div&gt;
  235. </description><link>http://blog.riand.com/2020/02/lwc-reactive-state-manager.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6iuIhlBwqO8inkDv33AA1YQ9NAGP9H3o-17dhqF3VtiQZBBcgprIRF4Wou_h6MYBSkzA-yqIrhm3NrplxED9xPx8fVeSp63madAVkcD_1gnvf3SjAv-0cSvtKGBqcFr-DTiR5x0s2rGQ/s72-c/reactive-state.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-4829046412628380241</guid><pubDate>Fri, 31 Jan 2020 22:02:00 +0000</pubDate><atom:updated>2020-01-31T14:02:37.125-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">lwc</category><category domain="http://www.blogger.com/atom/ns#">web components</category><title>LWC Wire Adapters #2 - Producing Data Streams</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  236. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  237. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtI8lqNsgR4Jvmk-l9Tac3Ir37mg0cdSmaIuRk3eXpfLClTwCcWf0iOmrKgkQMUXlzHowC4oKpaA1DZxedN7nzpnBiMJi5Tew3RoZzm4QlM5nqLYUI1LSaarjDeH09iocBlCZpwOjMEMg/s1600/wireadapters-providers.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;304&quot; data-original-width=&quot;745&quot; height=&quot;130&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtI8lqNsgR4Jvmk-l9Tac3Ir37mg0cdSmaIuRk3eXpfLClTwCcWf0iOmrKgkQMUXlzHowC4oKpaA1DZxedN7nzpnBiMJi5Tew3RoZzm4QlM5nqLYUI1LSaarjDeH09iocBlCZpwOjMEMg/s320/wireadapters-providers.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  238. &lt;br /&gt;
  239. We saw in the previous post that wire adapters are a very elegant and efficient way to consume data streams. Let&#39;s see now how we can produce these data streams. If it seems magic, it is actually a pretty easy API.&lt;br /&gt;
  240. &lt;br /&gt;
  241. &lt;b&gt;*Disclaimers*&lt;/b&gt;&lt;br /&gt;
  242. &lt;b&gt;#1&lt;/b&gt; - This post talks about the original wire adapter API, as described &lt;a href=&quot;https://github.com/salesforce/lwc-rfcs/blob/master/text/0103-wire-adapters.md&quot;&gt;here&lt;/a&gt;.&amp;nbsp; There is an upcoming new implementation that keeps the same consumer interface, but where the provider API is a bit different, see &lt;a href=&quot;https://github.com/salesforce/lwc-rfcs/blob/master/text/0000-wire-reform.md&quot;&gt;wire-reform&lt;/a&gt;. This blog post talks about the former, and will be updated when the later will be released.&lt;br /&gt;
  243. &lt;b&gt;#2&lt;/b&gt; -&amp;nbsp; Custom wire adapters are currently only available to the Open Source version of &lt;a href=&quot;https://lwc.dev/&quot;&gt;LWC&lt;/a&gt;, not to LWC running in Salesforce Core.&lt;br /&gt;
  244. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  245. Our First Wire Adapter&lt;/h2&gt;
  246. To introduce the wire adapter API, let&#39;s implement a &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;stopWatch&lt;/span&gt; component that behind the scene uses a wire adapter streaming the time elapsed. This wire adapter offers some callbacks to start, stop and reset the timer:&lt;br /&gt;
  247. &lt;br /&gt;
  248. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  249. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgosr_OzqUknxROnbBcX7eZFT2YbeKVkGzcX0As3T2yRr-eDo0NrKjB0p7RXzXpRnR0K0capdShtGvY2nTnBTOn3G8Cirh1Argxhl0SB2DUooWo8BiYVUv39cl-sO3r6IqnDzv6NC55JBg/s1600/stopwatch.gif&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;129&quot; data-original-width=&quot;433&quot; height=&quot;118&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgosr_OzqUknxROnbBcX7eZFT2YbeKVkGzcX0As3T2yRr-eDo0NrKjB0p7RXzXpRnR0K0capdShtGvY2nTnBTOn3G8Cirh1Argxhl0SB2DUooWo8BiYVUv39cl-sO3r6IqnDzv6NC55JBg/s400/stopwatch.gif&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  250. For the impatient, the source code is provided through the LWC playground:&lt;div style=&quot;text-align: left;&quot;&gt;
  251. &amp;nbsp; &amp;nbsp;&lt;a href=&quot;https://developer.salesforce.com/docs/component-library/tools/playground/8peP8wto/8/edit&quot;&gt; https://developer.salesforce.com/docs/component-library/tools/playground/8peP8wto/8/edit&lt;/a&gt; &lt;/div&gt;
  252. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  253. Wire Adapters Registry&lt;/h2&gt;
  254. &lt;div style=&quot;text-align: left;&quot;&gt;
  255. LWC maintains a global registry of wire adapters, so each wire adapter has to be registered before it can be consumed by a component. It is uniquely identified by a key, which can be a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol&quot;&gt;Symbol&lt;/a&gt; or a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions&quot;&gt;Function&lt;/a&gt;. As a good practice, this key is exported from the wire adapter JavaScript module. The registration is done through the register method:&lt;br /&gt;
  256. &lt;br /&gt;
  257. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; register(key&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;, myAdapterFunction);&lt;/span&gt;&lt;br /&gt;
  258. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
  259. &lt;br /&gt;
  260. &lt;div style=&quot;text-align: left;&quot;&gt;
  261. The
  262. current implementation also requires a global, LWC provided, &quot;wire service&quot; to be registered
  263. before any wire adapter can be used. The main file of an application is
  264. generally a good place for it.&lt;/div&gt;
  265. &lt;div style=&quot;text-align: left;&quot;&gt;
  266. &lt;br /&gt;&lt;/div&gt;
  267. &lt;div style=&quot;text-align: left;&quot;&gt;
  268. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; import { register } from &quot;lwc&quot;;&lt;br /&gt;&amp;nbsp; // The wire service has to be registered once&lt;br /&gt;&amp;nbsp; import { registerWireService } from &#39;wire-service&#39;;&lt;br /&gt;&amp;nbsp; registerWireService(register);&lt;/span&gt;&lt;/div&gt;
  269. &lt;br /&gt;
  270. Note that the example above does not have to do this, as the Playground does it automatically for any code snippet.&lt;br /&gt;
  271. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  272. Wire Adapter Implementation&lt;/h2&gt;
  273. &lt;/div&gt;
  274. &lt;div style=&quot;text-align: left;&quot;&gt;
  275. A wire adapter is defined by a function that is called once when the wire adapter is initialized. It has a unique parameter, &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;eventTarget&lt;/span&gt;, which represents the consumer of this wire adapter.&lt;br /&gt;
  276. &lt;br /&gt;
  277. To get started, here is how our simple wire adapter skeleton &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;stopWatch&lt;/span&gt; looks like:&lt;/div&gt;
  278. &lt;div style=&quot;text-align: left;&quot;&gt;
  279. &lt;br /&gt;&lt;/div&gt;
  280. &lt;div style=&quot;text-align: left;&quot;&gt;
  281. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; // Define the Symbol identifying the wire adapter&lt;/span&gt;&lt;br /&gt;
  282. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; export const stopWatch = Symbol(&#39;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;stop-watch&lt;/span&gt;&#39;);&lt;/span&gt;&lt;br /&gt;
  283. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
  284. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; // Register the wire adapter implementation&lt;br /&gt;&amp;nbsp; register(&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;stopWatch&lt;/span&gt;, eventTarget =&amp;gt; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // wire adapter implementation&lt;/span&gt;&lt;/div&gt;
  285. &lt;div style=&quot;text-align: left;&quot;&gt;
  286. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // ...&lt;br /&gt;&amp;nbsp; });&lt;/span&gt;&lt;/div&gt;
  287. &lt;div style=&quot;text-align: left;&quot;&gt;
  288. &lt;ul style=&quot;text-align: left;&quot;&gt;
  289. &lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;stopWatch&lt;/span&gt;&lt;br /&gt;This is the wire adapter key, exported so components can reference it.&lt;/li&gt;
  290. &lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;eventTarget&lt;/span&gt;&lt;br /&gt;The context passed by the engine, see bellow.&lt;/li&gt;
  291. &lt;/ul&gt;
  292. &lt;/div&gt;
  293. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  294. Initialization &lt;/h3&gt;
  295. &lt;div style=&quot;text-align: left;&quot;&gt;
  296. The wire adapter API is event based: it receives notifications when it has to take an action. In fact it can register handlers for the following 3 different events: &lt;/div&gt;
  297. &lt;ul style=&quot;text-align: left;&quot;&gt;
  298. &lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;connect&lt;/span&gt;&lt;br /&gt;This event is emitted when the component owning the wire adapter is connected to the DOM. At that time, it is safe for the wire adapter to send data to the component.&lt;/li&gt;
  299. &lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;disconnect&lt;/span&gt;&lt;br /&gt;This event is emitted when the component owning the wire adapter is disconnected from the DOM. When the component is disconnected, the wire adapter can cleanup its internal data. For example, if it was observing a data stream, it can disconnect from it. Once a component is disconnected, the wire adapter should no longer send it data, until it connects again.&lt;/li&gt;
  300. &lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;config&lt;/span&gt;&lt;br /&gt; This is emitted once initially, and thereafter when the wire adapter options have different values (&quot;reactive options&quot;). The options value are passed as an argument to the handler.&lt;/li&gt;
  301. &lt;/ul&gt;
  302. To handle these events, the wire adapter has to register handlers to the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;eventTarget&lt;/span&gt;:&lt;br /&gt;
  303. &lt;br /&gt;
  304. &lt;div style=&quot;text-align: left;&quot;&gt;
  305. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; register(stopWatch, eventTarget =&amp;gt; {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; function handleConfig(options) {&lt;/span&gt;&lt;/div&gt;
  306. &lt;div style=&quot;text-align: left;&quot;&gt;
  307. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Do something with the config&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; function handleConnect() {&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
  308. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // The component is connected, send it some values!&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
  309. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; function handleDisconnect() {&lt;/span&gt;&lt;/div&gt;
  310. &lt;div style=&quot;text-align: left;&quot;&gt;
  311. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // The component is disconnected, cleanup the adapter&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Connect the wire adapter&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; eventTarget.addEventListener(&#39;config&#39;, handleConfig);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; eventTarget.addEventListener(&#39;connect&#39;, handleConnect);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; eventTarget.addEventListener(&#39;disconnect&#39;, handleDisconnect);&lt;br /&gt;&amp;nbsp; })&lt;/span&gt;&lt;/div&gt;
  312. &lt;div style=&quot;text-align: left;&quot;&gt;
  313. &lt;br /&gt;
  314. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  315. Sending data to the component&lt;/h3&gt;
  316. &lt;/div&gt;
  317. &lt;div style=&quot;text-align: left;&quot;&gt;
  318. Once the wire adapter receives the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;connect&lt;/span&gt; event, it can send data to the component. For this, it dispatches a &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;ValueChangedEvent&lt;/span&gt; to the component with the new value as an argument:&lt;br /&gt;
  319. &lt;br /&gt;
  320. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; const result = ...&lt;br /&gt;&amp;nbsp; eventTarget.dispatchEvent(new ValueChangedEvent(result));&lt;/span&gt;&lt;br /&gt;
  321. &lt;br /&gt;
  322. Once connected, the wire adapter can send new data to the component at anytime. For example, if the component is listening to a websocket, it can update the component when its gets new data from that websocket.&lt;br /&gt;
  323. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  324. Assembling the pieces&amp;nbsp;&lt;/h3&gt;
  325. Well, it is assembled in the playground sample code:&lt;br /&gt;
  326. &lt;ul style=&quot;text-align: left;&quot;&gt;
  327. &lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;stopwatch.js&lt;/span&gt; contains the wire adapter code&lt;/li&gt;
  328. &lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;app.js/.html&lt;/span&gt; defines the component.&lt;/li&gt;
  329. &lt;/ul&gt;
  330. &lt;br /&gt;
  331. Note: there are 2 differences between this code running within the playground and a potential standalone application:&lt;br /&gt;
  332. &lt;ol style=&quot;text-align: left;&quot;&gt;
  333. &lt;li&gt;The wire service does not have to be registered, the playground does it&lt;/li&gt;
  334. &lt;li&gt;The wire adapter in the playground imports &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;wire-service&lt;/span&gt;, while it should be &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;@lwc/wire-service&lt;/span&gt; in an app using LWC Open Source.&lt;/li&gt;
  335. &lt;/ol&gt;
  336. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  337. A few implementation details &lt;/h3&gt;
  338. The wire adapter uses a configuration option to set the refresh interval. In the example, the value is passed through a reactive property. This makes the &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;&quot;&gt;handleConfig()&lt;/span&gt; handler called with a new value when this property changes:&lt;br /&gt;
  339. &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;&quot;&gt;&amp;nbsp; @track interval = 50;&lt;br /&gt;&amp;nbsp; @wire(timeWatch,{interval:&#39;$interval&#39;}) stopWatch;&lt;/span&gt;&lt;br /&gt;
  340. &lt;br /&gt;
  341. The data sent by the wire adapter to the component contains both the timer values (hour, mins, ...) as well as some callback functions to interact with the stopwatch adapter. Here is a mock of that object:&lt;br /&gt;
  342. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Timer values&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; hour: &#39;00&#39;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; mins: &#39;00&#39;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; secs: &#39;00&#39;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; mils: &#39;000&#39;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Callback functions&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; start: function() {...},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; stop&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;: function() {...},&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
  343. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp; reset&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;: function() {...},&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
  344. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;}&lt;/span&gt;&lt;br /&gt;
  345. &lt;br /&gt;
  346. To make the wire adapter robust, it maintains a &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;connected&lt;/span&gt; flag and only sends data to the component when it this flag is &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;true&lt;/span&gt;.&lt;/div&gt;
  347. &lt;div style=&quot;text-align: left;&quot;&gt;
  348. &lt;/div&gt;
  349. &lt;div style=&quot;text-align: left;&quot;&gt;
  350. &lt;br /&gt;&lt;/div&gt;
  351. &lt;div style=&quot;text-align: left;&quot;&gt;
  352. This wire adapter implementation creates and sends a copy of the date to the component.&amp;nbsp; Sending the same object instance multiple times to the component does not make the component to re-render, while a copy does.&lt;br /&gt;
  353. &lt;br /&gt;
  354. Finally, wire adapters are simple to implement, aren&#39;t they? &lt;br /&gt;
  355. &lt;br /&gt;&lt;/div&gt;
  356. &lt;/div&gt;
  357. </description><link>http://blog.riand.com/2020/01/lwc-wire-adapters-2-producing-data.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtI8lqNsgR4Jvmk-l9Tac3Ir37mg0cdSmaIuRk3eXpfLClTwCcWf0iOmrKgkQMUXlzHowC4oKpaA1DZxedN7nzpnBiMJi5Tew3RoZzm4QlM5nqLYUI1LSaarjDeH09iocBlCZpwOjMEMg/s72-c/wireadapters-providers.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-7635303777182617212</guid><pubDate>Fri, 17 Jan 2020 17:07:00 +0000</pubDate><atom:updated>2020-01-17T09:08:25.188-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">lwc</category><category domain="http://www.blogger.com/atom/ns#">web components</category><title>LWC Wire Adapters #1 - Consuming Data Streams</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  358. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  359. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXgRXqF1DOOWqyhtdIHlZwt8h9jNUqAebCXO6Ah7p9ni3sIAi-131hTsYot-hS9UjsmUKSSgEsHp1GumaV0uvSayDz-0RoixTQleDwz6cOPuk7YoVL6vwUTgKgvKpZyJI5mFBD0nPEtAk/s1600/wireadapters-consumers.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;304&quot; data-original-width=&quot;745&quot; height=&quot;130&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXgRXqF1DOOWqyhtdIHlZwt8h9jNUqAebCXO6Ah7p9ni3sIAi-131hTsYot-hS9UjsmUKSSgEsHp1GumaV0uvSayDz-0RoixTQleDwz6cOPuk7YoVL6vwUTgKgvKpZyJI5mFBD0nPEtAk/s320/wireadapters-consumers.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  360. &lt;br /&gt;
  361. The previous post talked about LWC reactivity using membranes, which re-render the components when the tracked data is changed, like bellow:&lt;br /&gt;
  362. &lt;br /&gt;
  363. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;class MyComponent extends LightningElement {&lt;/span&gt;&lt;br /&gt;
  364. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; @track value;&lt;/span&gt;&lt;br /&gt;
  365. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;}&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
  366. &lt;br /&gt;
  367. But how does the data get to the component property, &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;value&lt;/span&gt; in the example above? It can come from an external data source like a REST service, and it might even update over time. Think about a stream of data feeding the component with new values when they become available.&lt;br /&gt;
  368. &lt;br /&gt;
  369. Part of the standard web component &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements&quot;&gt;lifecycle&lt;/a&gt;, the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;connectedCallback()&lt;/span&gt; method is called when the component is added to a document connected element. In short, when the custom element is added to the DOM of your page. At that time, it has access to the DOM element with its attributes, so it can call a service using these values. This is the recommended way for developers to connect the component with data sources.&lt;br /&gt;
  370. &lt;br /&gt;
  371. LWC goes further than that, as it makes it simpler, thanks to the &quot;wire adapters&quot;. Technically, a wire adapter is a data provider that streams data to a component. The notion of stream is important: it is not only a one time data retrieval, like a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot;&gt;Promise&lt;/a&gt;, but the data can be continuously updated and the component notified. For the JS experts, it is much like an &lt;a href=&quot;https://rxjs-dev.firebaseapp.com/&quot;&gt;RxJS&lt;/a&gt; &lt;a href=&quot;https://www.stackchief.com/tutorials/JavaScript%20Observables%20in%205%20Minutes&quot;&gt;Observable&lt;/a&gt;. &lt;br /&gt;
  372. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  373. Wire Adapters from a Consumer Standpoint&lt;/h2&gt;
  374. Wire adapters are objects instantiated using the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;@wire&lt;/span&gt; decorator. This decorator uses two parameters:&lt;br /&gt;
  375. &lt;ul style=&quot;text-align: left;&quot;&gt;
  376. &lt;li&gt;The wire adapter key, that uniquely identifies the adapter to use &lt;/li&gt;
  377. &lt;li&gt;A set of options, specific to the wire adapter implementation&lt;/li&gt;
  378. &lt;/ul&gt;
  379. Let&#39;s use a fictitious wire adapter to access a list of books. With the use of a parameter, this wire adapter can even filter the books based on the author. Assigning a list of books to a component property is done with the following syntax:&lt;br /&gt;
  380. &lt;br /&gt;
  381. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; class Books extends LightningElement {&lt;/span&gt;&lt;br /&gt;
  382. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @wire(BookProvider, {author: &#39;Mark Twain&#39;}) bookList; &lt;/span&gt;&lt;br /&gt;
  383. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; }&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
  384. &lt;ul style=&quot;text-align: left;&quot;&gt;
  385. &lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;BookProvider&lt;/span&gt; is the wire adapter key. It is generally a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol&quot;&gt;Symbol&lt;/a&gt;, exported by the module defining the wire adapter.&amp;nbsp;&lt;/li&gt;
  386. &lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;{author: &#39;Mark Twain&#39;}&lt;/span&gt; is the set of options passed to the wire adapter.&amp;nbsp; This wire adapter only handles one, which is the author filter.&lt;/li&gt;
  387. &lt;/ul&gt;
  388. When the component is added to the DOM, the wire adapter is connected and executed. Obviously, it will retrieve a list of books and assign it to &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;bookList&lt;/span&gt;. The result can be assigned synchronously or asynchronously, depending on the wire adapter implementation. With the later, the component should not assume that the data is available right away, but it can come later.&lt;br /&gt;
  389. &lt;br /&gt;
  390. What the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;bookList&lt;/span&gt; variable contains is up to the wire adapter: it can be&amp;nbsp; raw data, like an array of books, of it can be a richer structure giving more information &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;{data,error,loading, ...}&lt;/span&gt;. The later is a good practice. It allows, for example, the component to display an icon while the data is loading asynchronously. Moreover, the property content is not limited to values, but can also contain any kind of JS objects, including callback functions (ex: &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;reload()&lt;/span&gt;, &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;fetchMore()&lt;/span&gt;, ...). Again, it is up to the wire adapter to define the shape of the data being assigned to the property. &lt;br /&gt;
  391. &lt;br /&gt;
  392. The wired properties are automatically reactive, similarly to properties decorated with &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;@track&lt;/span&gt;.&lt;br /&gt;
  393. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  394. Dynamic Wire Adapters &lt;/h2&gt;
  395. The wire adapter options can be dynamic. Let&#39;s, for example, filter the book list using an author passed as an attribute to the component. This way, we can use the same component multiple times while displaying different lists:&lt;br /&gt;
  396. &lt;br /&gt;
  397. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;my-books author=&#39;Mark Twain&#39;&amp;gt;&lt;/span&gt;&lt;br /&gt;
  398. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;my-books author=&#39;Stephen King&#39;&amp;gt;&lt;/span&gt;&lt;br /&gt;
  399. &lt;br /&gt;
  400. Our &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;Books&lt;/span&gt; component becomes:&lt;br /&gt;
  401. &lt;br /&gt;
  402. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; class Books extends LightningElement {&lt;/span&gt;&lt;br /&gt;
  403. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; @api author; &lt;/span&gt;&lt;br /&gt;
  404. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @wire(BookProvider, {author: &#39;$author&#39;}) bookList; &lt;/span&gt;&lt;br /&gt;
  405. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; }&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
  406. &lt;br /&gt;
  407. The &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;@api&lt;/span&gt; decorator in LWC means that the &#39;author&#39; property is &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc/js_props_public&quot;&gt;public&lt;/a&gt;: it can be set through an attribute in the markup, or programmatically from outside of the component. The &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;$author&lt;/span&gt; value is an LWC convention: it means that the component&#39;s &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;author&lt;/span&gt; property value should be used instead of a static string value. Note that this only applies to root values. Options like &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;{user: {name:&#39;$name&#39;}}&lt;/span&gt; will not be recognized as a reference to a property value, because &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;name&lt;/span&gt; is not a root property.&lt;br /&gt;
  408. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  409. Advanced Wire Adapters&lt;/h2&gt;
  410. If the value provided by a wire adapter can be assigned to a component property, it can also be streamed to a component method. In this case, the value is passed to the method as an argument.&lt;br /&gt;
  411. Let&#39;s assume, as an example, that we want to split a book properties into different component tracked properties, we can do something like:&lt;br /&gt;
  412. &lt;br /&gt;
  413. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; class Books extends LightningElement {&lt;/span&gt;&lt;br /&gt;
  414. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; @track title;&lt;/span&gt;&lt;br /&gt;
  415. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;@track &lt;/span&gt;author; &lt;/span&gt;&lt;br /&gt;
  416. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @wire(BookByISBN, {ISBN: &#39;xxxxx&#39;}) book(&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;value&lt;/span&gt;) {&lt;/span&gt;&lt;br /&gt;
  417. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; this.title = value.title;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; this.&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;author &lt;/span&gt; = value.author; &lt;/span&gt;&lt;br /&gt;
  418. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }; &lt;/span&gt;&lt;br /&gt;
  419. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; }&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
  420. &lt;br /&gt;
  421. Of course, the code of such a method can do a lot more, like calculation, sending updates, ...&lt;br /&gt;
  422. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  423. Imperative Access &lt;/h2&gt;
  424. To be complete, the wire adapter key can be either a Symbol or a Function. When it is a function (a &quot;callable&quot;), it means that it can be called by code, simply by calling that function with parameters. If the parameters could match the wire adapter options, this is not a requirement. Also, the returned value is adapter specific: it can be anything, and does not have to be a Promise. This is reserved to very specific use cases.&lt;br /&gt;
  425. &lt;br /&gt;
  426. &lt;br /&gt;
  427. &lt;br /&gt;
  428. The journey continues. Now that we know how to consume a wire adapter, let&#39;s figure out how to build one. That will be the topic of the next post.&lt;/div&gt;
  429. </description><link>http://blog.riand.com/2020/01/lwc-wire-adapters-1-consuming-data.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXgRXqF1DOOWqyhtdIHlZwt8h9jNUqAebCXO6Ah7p9ni3sIAi-131hTsYot-hS9UjsmUKSSgEsHp1GumaV0uvSayDz-0RoixTQleDwz6cOPuk7YoVL6vwUTgKgvKpZyJI5mFBD0nPEtAk/s72-c/wireadapters-consumers.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-3160025878112141983</guid><pubDate>Mon, 13 Jan 2020 13:37:00 +0000</pubDate><atom:updated>2020-01-29T08:41:13.414-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">lwc</category><category domain="http://www.blogger.com/atom/ns#">web components</category><title>LWC Reactivity Using Membranes</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  430. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  431. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJDp2FRijfxpvpuqTSXZw7gaz2xTYajsN3trFZwJ3Fz-ng91xkgJ19VNePHyMDhYwdZYxcHNf_dBu-YXQiYxKF9vbY1cmExN6G8OajJgiQF4qwcqvntj-duY0b-j1xvSxKvJcph5tZGWw/s1600/membrane_main.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;506&quot; data-original-width=&quot;1204&quot; height=&quot;134&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJDp2FRijfxpvpuqTSXZw7gaz2xTYajsN3trFZwJ3Fz-ng91xkgJ19VNePHyMDhYwdZYxcHNf_dBu-YXQiYxKF9vbY1cmExN6G8OajJgiQF4qwcqvntj-duY0b-j1xvSxKvJcph5tZGWw/s320/membrane_main.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  432. &lt;br /&gt;
  433. LWC allows you to create reactive components that automatically update their UI when they get new data, or when existing data is modified. At the heart of this reactive capability is the implementation of a pattern called &quot;membrane&quot;. Let&#39;s dive into that pattern and how it powers LWC.&lt;br /&gt;
  434. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  435. What is a membrane&lt;/h2&gt;
  436. &lt;div style=&quot;text-align: left;&quot;&gt;
  437. Basically a membrane is a thin layer between the consumer of an object and that object. In other words, a membrane exposes consumer facing proxies for any existing object. From a consumer standpoint, accessing the object through the proxy is indistinguisable from intereacting directly with the object. But the membrane can execute business logic (&quot;hooks&quot;) whenever the consumer interacts with the object. It can track property access (read/write), method calls, properties enumeration and so on... It can also hide some content to the consumer, or even distort existing content (e.g. change the values being returned). It is a very powerful pattern used for many different purposes, from content isolation to change tracking.&lt;br /&gt;
  438. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  439. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_OqfZYX9Z3YGla46zQqg_VCIA7GKQ1VYPaJ2MD5gWIVfCtjz9oIbZgsQ5Fe7EcUHgKAw9WkGsFG2IL3qOoX6G9f2uBVBcU6r2mJXtzoykh4b_UVWN-5FJ0CkWhWVAV3mp63uWhsRXBlc/s1600/membrane_1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;604&quot; data-original-width=&quot;1014&quot; height=&quot;237&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_OqfZYX9Z3YGla46zQqg_VCIA7GKQ1VYPaJ2MD5gWIVfCtjz9oIbZgsQ5Fe7EcUHgKAw9WkGsFG2IL3qOoX6G9f2uBVBcU6r2mJXtzoykh4b_UVWN-5FJ0CkWhWVAV3mp63uWhsRXBlc/s400/membrane_1.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  440. &lt;/div&gt;
  441. &lt;div style=&quot;text-align: left;&quot;&gt;
  442. &lt;br /&gt;&lt;/div&gt;
  443. &lt;div style=&quot;text-align: left;&quot;&gt;
  444. Another important point with a membrane is that any object accessed through a proxy is automatically proxied by the membrane as well. Let&#39;s note a proxied object &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;X&lt;/span&gt; as &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;P(X)&lt;/span&gt;.&lt;br /&gt;
  445. If &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;A&lt;/span&gt; has a property &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;b&lt;/span&gt; of type &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;B&lt;/span&gt;, then &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;A.b&lt;/span&gt; returns a object of type &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;B&lt;/span&gt;. But &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;P(A).b &lt;/span&gt;returns &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;P(B)&lt;/span&gt;. The membrane hooked the access to the property &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;b&lt;/span&gt; and distorted the result to return a proxy object for &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;b&lt;/span&gt;. This is obviously transparent to the consumer:&lt;/div&gt;
  446. &lt;div style=&quot;text-align: left;&quot;&gt;
  447. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  448. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipV2XTojW1gAdDIeKEXAJLjV_ZfQu9Hj5ZEPn5oh5fGW4pwDvLisn8-hgl-mHRh47ciIEwCltDYf-SD5tG4HssmP5nZpS7tZjlI2YV_uHPOGGDcm7Hmhu36PqEvSevy5jX8CvgfKdLI00/s1600/membrane_2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;564&quot; data-original-width=&quot;954&quot; height=&quot;236&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipV2XTojW1gAdDIeKEXAJLjV_ZfQu9Hj5ZEPn5oh5fGW4pwDvLisn8-hgl-mHRh47ciIEwCltDYf-SD5tG4HssmP5nZpS7tZjlI2YV_uHPOGGDcm7Hmhu36PqEvSevy5jX8CvgfKdLI00/s400/membrane_2.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  449. Because of this, if one starts from a proxied root object and only access the graph from this root object, then it guarantees that all the objects accessed are proxies, and are thus managed by the membrane.&lt;br /&gt;
  450. &lt;br /&gt;
  451. Finally, membranes have another important property: there is a one to one relationship between an object and its proxy for a given membrane. It means that the same &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;P(A)&lt;/span&gt; object is returned for a given object &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;A&lt;/span&gt;, regardless how it is retrieved. Suppose, for example, that a component has list of child components and a reference to its parent. The equality bellow is true:&lt;br /&gt;
  452. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; comp.children[0].parent === comp&lt;/span&gt; &lt;br /&gt;
  453. That equality is preserved with proxies as well:&lt;br /&gt;
  454. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; P(comp).children[0].parent === P(comp)&lt;/span&gt;&lt;br /&gt;
  455. More detailed information on membranes can be found here: &lt;a href=&quot;https://tvcutsem.github.io/membranes&quot;&gt;membrane&lt;/a&gt;.&lt;/div&gt;
  456. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  457. Salesforce Membrane Implementation&lt;/h2&gt;
  458. &lt;div style=&quot;text-align: left;&quot;&gt;
  459. As said earlier, LWC is using membranes to implement reactivity. But, instead of coding membranes within LWC, Salesforce created a separate general purpose, open source library called &lt;a href=&quot;https://github.com/salesforce/observable-membrane&quot;&gt;observable-membrane&lt;/a&gt;. Like other JavaScript implementations, it is based on top of the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy&quot;&gt;Proxy API&lt;/a&gt;.&lt;/div&gt;
  460. &lt;div style=&quot;text-align: left;&quot;&gt;
  461. &lt;br /&gt;&lt;/div&gt;
  462. &lt;div style=&quot;text-align: left;&quot;&gt;
  463. With this library, a developer can create a membrane and proxy objects with the simple code bellow:&lt;/div&gt;
  464. &lt;div style=&quot;text-align: left;&quot;&gt;
  465. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; const membrane = new ObservableMembrane();&lt;br /&gt;&amp;nbsp; ...&lt;br /&gt;&amp;nbsp; const o = membrane.getProxy(o); &lt;/span&gt;&lt;/div&gt;
  466. &lt;div style=&quot;text-align: left;&quot;&gt;
  467. &lt;br /&gt;&lt;/div&gt;
  468. &lt;div style=&quot;text-align: left;&quot;&gt;
  469. The &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;membrane&lt;/span&gt; object holds a set of functions called when a caller interacts with a proxied object. Thus, the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;membrane&lt;/span&gt; can track when a value is read/updated and eventually distort that value. In short, the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;ObservableMembrane&lt;/span&gt; knows about any property access/change in the graph starting from any proxied object, obtained by calling &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;getProxy()&lt;/span&gt;.&lt;/div&gt;
  470. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  471. LWC and Membrane &lt;/h2&gt;
  472. &lt;div style=&quot;text-align: left;&quot;&gt;
  473. This is where it becomes interesting: if a &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;membrane&lt;/span&gt; can track any change in an object graph, it means that it can re-render a component when it detects a change in the data graph it consumes. This is exactly what the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;@track&lt;/span&gt; property decorator is for:&lt;/div&gt;
  474. &lt;div style=&quot;text-align: left;&quot;&gt;
  475. &lt;br /&gt;&lt;/div&gt;
  476. &lt;div style=&quot;text-align: left;&quot;&gt;
  477. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; class Banner extends LightningElement {&lt;/span&gt;&lt;/div&gt;
  478. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @track user&lt;/span&gt;&lt;br /&gt;
  479. &lt;div style=&quot;text-align: left;&quot;&gt;
  480. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
  481. &lt;div style=&quot;text-align: left;&quot;&gt;
  482. &lt;br /&gt;&lt;/div&gt;
  483. &lt;div style=&quot;text-align: left;&quot;&gt;
  484. This decorator ensures that any value assigned to the decorated property is actually proxied using the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;reactiveMembrane&lt;/span&gt;. Typically, assigning a value to a tracked property like:&lt;/div&gt;
  485. &lt;div style=&quot;text-align: left;&quot;&gt;
  486. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; this.user={ name: &#39;Paul&#39;, ...}&lt;/span&gt;&lt;/div&gt;
  487. &lt;div style=&quot;text-align: left;&quot;&gt;
  488. is internally equivalent to:&lt;/div&gt;
  489. &lt;div style=&quot;text-align: left;&quot;&gt;
  490. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; this.user=reactiveMembrane.getProxy({&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;name: &#39;Paul&#39;, &lt;/span&gt;...})&lt;/span&gt;.&lt;/div&gt;
  491. &lt;div style=&quot;text-align: left;&quot;&gt;
  492. &lt;br /&gt;&lt;/div&gt;
  493. &lt;div style=&quot;text-align: left;&quot;&gt;
  494. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;reactiveMembrane&lt;/span&gt; is a singleton, defined in the LWC engine library. It tracks the value changes in any object it created proxies for, find the components impacted by the changes and re-render them when necessary.&amp;nbsp; For example, if a component has a onclick event handler like bellow:&lt;/div&gt;
  495. &lt;div style=&quot;text-align: left;&quot;&gt;
  496. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; handleClick() {&lt;/span&gt;&lt;/div&gt;
  497. &lt;div style=&quot;text-align: left;&quot;&gt;
  498. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.user.name = &#39;Bob&#39;;&lt;/span&gt;&lt;/div&gt;
  499. &lt;div style=&quot;text-align: left;&quot;&gt;
  500. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; }&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
  501. &lt;div style=&quot;text-align: left;&quot;&gt;
  502. The user name change is detected by the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;reactiveMembrane&lt;/span&gt; and the component is re-rendered without any further work from the developer.&lt;br /&gt;
  503. &lt;br /&gt;
  504. Note that the re-rendering does not happen synchronously when the change is detected. The component is internally tagged as &#39;dirty&#39; and the rendering will happen later. Thus, sequentially changing multiple tracked values for a component only triggers a single re-rendering.&lt;/div&gt;
  505. &lt;div style=&quot;text-align: left;&quot;&gt;
  506. &lt;br /&gt;&lt;/div&gt;
  507. &lt;div style=&quot;text-align: left;&quot;&gt;
  508. Multiple components can track changes of the same object. Fortunately, every component will be updated when a change happens to this object, regardless where and how it was updated.&lt;br /&gt;
  509. &lt;br /&gt;
  510. Finally, all the fields in a component are reactive. Adding &lt;a href=&quot;https://lwc.dev/guide/reference#%40track&quot;&gt;@track&lt;/a&gt; to a component property is only needed for values you&#39;d like to track deeper, like objects or array, not for basic strings, numbers or booleans.&lt;/div&gt;
  511. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  512. Conclusion&lt;/h2&gt;
  513. The use of membranes within LWC makes it very easy to track changes and react when they happen. The developers don&#39;t have to care about notifying the system when some data is changed, it is &quot;magic&quot;. From an implementation standpoint, the use of the Proxy API also makes it very efficient at runtime.&lt;br /&gt;
  514. &lt;br /&gt;
  515. But wait: it reveals all its power when coupled with other LWC technologies. Let&#39;s explore that in the next post!&lt;br /&gt;
  516. &lt;br /&gt;&lt;/div&gt;
  517. </description><link>http://blog.riand.com/2020/01/lwc-reactivity-using-membranes.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJDp2FRijfxpvpuqTSXZw7gaz2xTYajsN3trFZwJ3Fz-ng91xkgJ19VNePHyMDhYwdZYxcHNf_dBu-YXQiYxKF9vbY1cmExN6G8OajJgiQF4qwcqvntj-duY0b-j1xvSxKvJcph5tZGWw/s72-c/membrane_main.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-3604885788907635470</guid><pubDate>Wed, 08 Jan 2020 17:57:00 +0000</pubDate><atom:updated>2020-01-15T08:05:35.255-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">lwc</category><category domain="http://www.blogger.com/atom/ns#">web components</category><title>Introducing LWC - Lightning Web Components</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  518. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  519. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIEO8NSoALEEPzbmMuYi9jGQEJAnuqctB6_0bkaMuvlunrTu40O4aZYTPd4nHJ7nCNV7Ww2kwSU1E1H87Bb7aoYrfuaxRxNVfO3m6YjSSi-4t8176uPStOohQpxdq7CxBrqqvvZFvnd1g/s1600/lightning-web-components.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;803&quot; data-original-width=&quot;1600&quot; height=&quot;160&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIEO8NSoALEEPzbmMuYi9jGQEJAnuqctB6_0bkaMuvlunrTu40O4aZYTPd4nHJ7nCNV7Ww2kwSU1E1H87Bb7aoYrfuaxRxNVfO3m6YjSSi-4t8176uPStOohQpxdq7CxBrqqvvZFvnd1g/s320/lightning-web-components.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  520. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  521. &lt;/div&gt;
  522. &lt;br /&gt;
  523. &lt;br /&gt;
  524. In the previous &lt;a href=&quot;https://blog.riand.com/2020/01/the-rise-of-web-components.html&quot;&gt;post&lt;/a&gt;, I talked about Web Components in general. But Salesforce released at &lt;a href=&quot;https://www.salesforce.com/company/news-press/press-releases/2019/05/192915-e/&quot;&gt;TrailheadDX 2019&lt;/a&gt; a new open source technology called Lightning Web Components, aka &lt;a href=&quot;https://lwc.dev/&quot;&gt;LWC&lt;/a&gt;. Basically, LWC is a set of tools, including a compiler, a runtime engine, some build utilities that you can use to create both reusable components and applications.&lt;br /&gt;
  525. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  526. Why a tool for creating Web Components?&lt;/h2&gt;
  527. Actually, Web Components are defined through a set of new browser specifications, so they don&#39;t require any framework. There are many examples showing how to create such reusable components just using the bare APIs!&lt;br /&gt;
  528. &lt;br /&gt;
  529. If this is correct, you&#39;ll quickly find out that these APIs are very low level. As a result, creating an even moderately complex component requires a bunch of boilerplate code: you need to maintain the synchronization between HTML attributes and JavaScript properties, you need to keep the DOM up-to-date on data changes, ... and many other boring stuff!&lt;br /&gt;
  530. &lt;br /&gt;
  531. That&#39;s the reason why several technologies appeared in the past years: they provide you an easier experience with Web Components. They are kind of lightweight frameworks on top of the Web Components APIs. They are often called &quot;invisible&quot; frameworks as, once the components are created, the technology used to create the components is not apparent. As a consumer of the components, you don&#39;t really care what has been used behind the scene to create them.&lt;br /&gt;
  532. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  533. What does LWC bring on the table?&lt;/h2&gt;
  534. &lt;div style=&quot;text-align: left;&quot;&gt;
  535. &lt;b&gt;Enterprise ready&lt;/b&gt; &lt;/div&gt;
  536. &lt;div style=&quot;text-align: left;&quot;&gt;
  537. LWC is an enterprise ready technology, designed to support Salesforce business in the long term. When I think about it, I can find a lot of similarities between &lt;a href=&quot;https://www.salesforce.com/&quot;&gt;Salesforce&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Lotus_Software&quot;&gt;Lotus/IBM&lt;/a&gt;: my latest &lt;a href=&quot;https://en.wikipedia.org/wiki/IBM_Notes&quot;&gt;Notes&lt;/a&gt; client can still execute applications from the &#39;90, and so Salesforce can execute apps built with older releases (or seamlessly convert them). Such a backward compatibility is critical for businesses! It is even more emphasized with cloud architectures, as the customers are upgraded automatically without the choice to stay on an older release. From an LWC standpoint, it means that every feature added to the tool will have to be supported &quot;forever&quot; and thus is carefully evaluated with a risk assessment. Any new feature has to go through an RFC, see: &lt;a href=&quot;https://github.com/salesforce/lwc-rfcs&quot;&gt;lwc-rfcs&lt;/a&gt;.&amp;nbsp;&lt;/div&gt;
  538. &lt;br /&gt;
  539. &lt;div style=&quot;text-align: left;&quot;&gt;
  540. &lt;b&gt;ES6+/Typescript&lt;/b&gt;&lt;/div&gt;
  541. &lt;div style=&quot;text-align: left;&quot;&gt;
  542. We are in 2020 now, I can&#39;t see myself back using ES5 and JQuery. I believe you&#39;re on the same boat! LWC eases the use of ES6+, making your code future proof even when staging features like &lt;a href=&quot;https://tc39.es/proposal-decorators/&quot;&gt;decorators&lt;/a&gt; are involved. It is using &lt;a href=&quot;https://babeljs.io/&quot;&gt;Babel&lt;/a&gt; behind the scene, for which &lt;a href=&quot;https://engineering.salesforce.com/&quot;&gt;Salesforce Engineering&lt;/a&gt; is a proud sponsor.&lt;/div&gt;
  543. &lt;div style=&quot;text-align: left;&quot;&gt;
  544. &lt;br /&gt;&lt;/div&gt;
  545. &lt;div style=&quot;text-align: left;&quot;&gt;
  546. &lt;b&gt;Browser support&amp;nbsp;&lt;/b&gt;&lt;/div&gt;
  547. &lt;div style=&quot;text-align: left;&quot;&gt;
  548. If the latest &quot;evergreen&quot; browsers natively support the Web Components specification, older browsers should&amp;nbsp; use polyfills and/or the compiler should generate specific code for these browsers. LWC does a bit of both, including some tailored polyfills to achieve performance goals. &lt;a href=&quot;https://www.npmjs.com/package/@lwc/synthetic-shadow&quot;&gt;@lwc/synthetic-shadow&lt;/a&gt; is a good example of such a polyfill.&lt;/div&gt;
  549. &lt;div style=&quot;text-align: left;&quot;&gt;
  550. &lt;br /&gt;&lt;/div&gt;
  551. &lt;div style=&quot;text-align: left;&quot;&gt;
  552. &lt;b&gt;Templating&lt;/b&gt;&lt;/div&gt;
  553. &lt;div style=&quot;text-align: left;&quot;&gt;
  554. LWC provides a template engine that renders markup from an HTML template. Somehow, it is similar to &lt;a href=&quot;https://lit-html.polymer-project.org/guide&quot;&gt;lit-html&lt;/a&gt;, except that it is compiled to Javascript at build time, for a faster execution. In that respect, it is similar to the &lt;a href=&quot;https://babeljs.io/docs/en/babel-plugin-transform-react-jsx&quot;&gt;JSX compiler&lt;/a&gt;.&lt;/div&gt;
  555. &lt;div style=&quot;text-align: left;&quot;&gt;
  556. &lt;br /&gt;&lt;/div&gt;
  557. &lt;div style=&quot;text-align: left;&quot;&gt;
  558. &lt;b&gt;Reactivity&lt;/b&gt;&lt;/div&gt;
  559. &lt;div style=&quot;text-align: left;&quot;&gt;
  560. If the template syntax supports one-way data binding, the component reactivity comes from &lt;a href=&quot;https://github.com/salesforce/observable-membrane&quot;&gt;observable-membrane&lt;/a&gt;, which is another Salesforce open source project. In a nutshell, it allows objects to be observed for changes. Typically, Components are redrawn when at least one of their properties is changed (can be the root property, or another object referenced by that property).&lt;/div&gt;
  561. &lt;div style=&quot;text-align: left;&quot;&gt;
  562. &lt;br /&gt;&lt;/div&gt;
  563. &lt;div style=&quot;text-align: left;&quot;&gt;
  564. &lt;b&gt;Data Binding&lt;/b&gt;&lt;/div&gt;
  565. &lt;div style=&quot;text-align: left;&quot;&gt;
  566. Associated with the above membrane is the notion of wire adapters. Basically, a wire adapter streams data to a component and makes it re-render when needed. It can be used similarly to &lt;a href=&quot;https://reactjs.org/docs/hooks-intro.html&quot;&gt;React hooks&lt;/a&gt;. More information here: &lt;a href=&quot;https://github.com/salesforce/lwc-rfcs/blob/master/text/0103-wire-adapters.md&quot;&gt;wire adapters,&lt;/a&gt; superseded by the &lt;a href=&quot;https://github.com/salesforce/lwc-rfcs/blob/master/text/0000-wire-reform.md&quot;&gt;wire-reform.&lt;/a&gt;&lt;/div&gt;
  567. &lt;div style=&quot;text-align: left;&quot;&gt;
  568. &lt;br /&gt;&lt;/div&gt;
  569. &lt;div style=&quot;text-align: left;&quot;&gt;
  570. &lt;b&gt;Performance&lt;/b&gt;&amp;nbsp;&lt;/div&gt;
  571. &lt;div style=&quot;text-align: left;&quot;&gt;
  572. Performance is a key characteristic of LWC. It is constantly, and deeply, tuned to provide the best client side performance.&lt;/div&gt;
  573. &lt;div style=&quot;text-align: left;&quot;&gt;
  574. &lt;br /&gt;&lt;/div&gt;
  575. &lt;b&gt;Static analysis&lt;/b&gt;&lt;br /&gt;
  576. For advanced users, the LWC compiler can be used to extract meta-data from the components. For example, a plugin could extract the data sources being accessed and generate some initialization code.&lt;br /&gt;
  577. &lt;div style=&quot;text-align: left;&quot;&gt;
  578. &lt;br /&gt;&lt;/div&gt;
  579. &lt;div style=&quot;text-align: left;&quot;&gt;
  580. And several others, like matching attribute and properties, ...: a set of goodies that makes your life easier as a developer! &lt;/div&gt;
  581. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  582. The flavors of LWC&lt;/h2&gt;
  583. &lt;div style=&quot;text-align: left;&quot;&gt;
  584. Well, if there is one LWC technology, there are two deployments:&lt;/div&gt;
  585. &lt;ul style=&quot;text-align: left;&quot;&gt;
  586. &lt;li&gt;Salesforce platform&lt;br /&gt;The Salesforce platform handles many things for free, like the LWC compilation, so you don&#39;t have to manage rollup/webpack builds, nor you have to maintain package.json. On the other hand, it runs within the constraints of the Salesforce platform, including &lt;a href=&quot;https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/security_code.htm&quot;&gt;Locker&lt;/a&gt; and other differences.&lt;/li&gt;
  587. &lt;li&gt;Open source, aka LWC OSS (&lt;a href=&quot;https://github.com/salesforce/lwc&quot;&gt;Github&lt;/a&gt;)&lt;br /&gt;LWC can be used similarly to any other Javascript technology. To get started, you can use the nice &lt;a href=&quot;https://github.com/muenzpraeger/create-lwc-app&quot;&gt;lwc-create-app&lt;/a&gt; tools from &lt;a href=&quot;https://twitter.com/muenzpraeger?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor&quot;&gt;Rene Winkelmeyer (&lt;/a&gt;&lt;a href=&quot;https://twitter.com/muenzpraeger&quot;&gt;@muenzpraeger&lt;/a&gt;&lt;a href=&quot;https://twitter.com/muenzpraeger?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor&quot;&gt;)&lt;/a&gt;, that some of you know from our prior IBM life.&lt;br /&gt;This is LWC unleashed!&lt;/li&gt;
  588. &lt;/ul&gt;
  589. The differences are detailed &lt;a href=&quot;https://developer.salesforce.com/blogs/2019/06/differences-between-building-lightning-web-components-on-lightning-platform-and-open-source.html&quot;&gt;here&lt;/a&gt;, thanks again to Rene.&lt;br /&gt;
  590. &lt;ul style=&quot;text-align: left;&quot;&gt;
  591. &lt;/ul&gt;
  592. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  593. And what next?&lt;/h2&gt;
  594. &lt;div style=&quot;text-align: left;&quot;&gt;
  595. Now that I introduced the LWC technology (I hope it got you hooked!), it is time to get our hands dirty. I&#39;m not going to provide any tutorial or getting started document because they already exist. I&#39;ll rather focus on some specific aspect of Web Components and LWC.&lt;/div&gt;
  596. &lt;div style=&quot;text-align: left;&quot;&gt;
  597. &lt;br /&gt;&lt;/div&gt;
  598. &lt;div style=&quot;text-align: left;&quot;&gt;
  599. By the meantime, you can get started here: &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc&quot;&gt;Introducing Lightning Web Components&lt;/a&gt; or here: &lt;a href=&quot;https://lwc.dev/&quot;&gt;lwc.dev.&lt;/a&gt;&lt;br /&gt;
  600. &lt;br /&gt;&lt;/div&gt;
  601. &lt;/div&gt;
  602. </description><link>http://blog.riand.com/2020/01/introducing-lwc-lightning-web-components.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIEO8NSoALEEPzbmMuYi9jGQEJAnuqctB6_0bkaMuvlunrTu40O4aZYTPd4nHJ7nCNV7Ww2kwSU1E1H87Bb7aoYrfuaxRxNVfO3m6YjSSi-4t8176uPStOohQpxdq7CxBrqqvvZFvnd1g/s72-c/lightning-web-components.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-3704689864711678915</guid><pubDate>Sat, 04 Jan 2020 20:41:00 +0000</pubDate><atom:updated>2020-01-04T12:41:26.878-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">lwc</category><category domain="http://www.blogger.com/atom/ns#">web components</category><title>The rise of Web Components</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  603. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  604. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmF8b6Tmj3P6oG06A6XdmLHR7HpFDMguKU2z8WF6QPSJei0l5bmlymGqx2dTyIoYoSbliBtm3cRTMgcd2czglHJvOuRi_FYyr5LMhxKLyTk93xGdSdeKZbJPbLaph7D0o1mwvpRFWyEdM/s1600/webcomponents.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;192&quot; data-original-width=&quot;192&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmF8b6Tmj3P6oG06A6XdmLHR7HpFDMguKU2z8WF6QPSJei0l5bmlymGqx2dTyIoYoSbliBtm3cRTMgcd2czglHJvOuRi_FYyr5LMhxKLyTk93xGdSdeKZbJPbLaph7D0o1mwvpRFWyEdM/s1600/webcomponents.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  605. &lt;br /&gt;
  606. It is now been a while since people started to talk about &lt;a href=&quot;https://en.wikipedia.org/wiki/Web_Components&quot; target=&quot;_blank&quot;&gt;Web Components&lt;/a&gt;. But it finally became a reality. As of the beginning of 2020, all the major browsers implement most of the specification. Moreover, the need to support some of the ancient browsers, like the older IEs, is &lt;a href=&quot;https://gs.statcounter.com/browser-market-share&quot;&gt;drastically decreasing&lt;/a&gt;. Fortunately, there are&amp;nbsp; &lt;a href=&quot;https://www.webcomponents.org/polyfills&quot; target=&quot;_blank&quot;&gt;polyfills&lt;/a&gt; to still support IE11 and other not too old generation. So what are web components and should you use them?&lt;br /&gt;
  607. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  608. Web Components&lt;/h2&gt;
  609. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  610. &lt;/h2&gt;
  611. &lt;div style=&quot;text-align: left;&quot;&gt;
  612. Web Components is actually a generic term that encompasses several different specifications:&lt;/div&gt;
  613. &lt;ul style=&quot;text-align: left;&quot;&gt;
  614. &lt;li&gt;Custom Elements&lt;br /&gt;Define new HTML element types (e.g. new HTML tags). They are easily identifiable from the stock HTML elements as their name must include an hyphen &#39;-&#39;. &lt;br /&gt;Support: &lt;a href=&quot;https://caniuse.com/#feat=custom-elementsv1&quot;&gt;https://caniuse.com/#feat=custom-elementsv1&lt;/a&gt;&lt;/li&gt;
  615. &lt;li&gt;Shadow DOM&lt;br /&gt;Encapsulates the markup and the styles of a component. Basically, the shadow DOM isolates the implementation of your component from the rest of the page.&lt;br /&gt;Support: &lt;a href=&quot;https://caniuse.com/#feat=shadowdomv1&quot;&gt;https://caniuse.com/#feat=shadowdomv1&lt;/a&gt;&lt;/li&gt;
  616. &lt;li&gt;ES6 modules&lt;br /&gt;As Web Components heavily rely on JavaScript, ES6 modules define a elegant way to load JavaScript code within the browser.&lt;br /&gt;Support: &lt;a href=&quot;https://caniuse.com/#feat=es6-module&quot;&gt;https://caniuse.com/#feat=es6-module&lt;/a&gt;&lt;/li&gt;
  617. &lt;li&gt;HTML template&lt;br /&gt;Used to define fragments of markup that are not rendered by the page, but can be reused later in particular from JavaScript.&lt;br /&gt;Support: &lt;a href=&quot;https://caniuse.com/#feat=template&quot;&gt;https://caniuse.com/#feat=template&lt;/a&gt;&lt;/li&gt;
  618. &lt;/ul&gt;
  619. Here is a great introduction to these specifications: &lt;a href=&quot;https://www.webcomponents.org/introduction&quot;&gt;https://www.webcomponents.org/introduction&lt;/a&gt;&lt;br /&gt;
  620. &lt;br /&gt;
  621. &lt;div style=&quot;text-align: left;&quot;&gt;
  622. Not that the idea of natively extending the HTML markup and isolating components is not new. There is prior art, like &lt;a href=&quot;https://en.wikipedia.org/wiki/HTML_Components&quot; target=&quot;_blank&quot;&gt;Microsoft HTC&lt;/a&gt; or &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Archive/Mozilla/XUL&quot; target=&quot;_blank&quot;&gt;Mozilla XUL&lt;/a&gt;. Web Components, instead of being vendor specific, are part of the W3C specification and work similarly on every browser following that spec.&lt;/div&gt;
  623. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  624. Web components vs Frameworks&lt;/h2&gt;
  625. Let&#39;s put it this way: they do not compete, they rather complement each other. The Web Components specification provides native behaviors implementation, thus helping the frameworks to be simpler and lightweight. On the other hand, the frameworks make it easier to write Web Components. If you were brave enough to write a web component manually, then you know what I&#39;m talking about. What would take a few lines of React code could lead to hundreds using the bare Web Components APIs.&lt;br /&gt;
  626. &lt;br /&gt;
  627. The Web Components specification is an evolving one, that still has gaps to fill. But it reached a state where it provides real value in term of performance and browser compatibility.&amp;nbsp; &lt;br /&gt;
  628. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  629. How to write Web Components&lt;/h2&gt;
  630. Ok, let&#39;s forget about solely using the raw browser APIs, but let&#39;s get help from Frameworks, as they handle a lots of things for us (data binding, event handlers, ...).&lt;br /&gt;
  631. &lt;br /&gt;
  632. Even though existing frameworks came before the Web Components specification, they all have ways to create and consume custom elements. On the other hand, they generally do not (yet?) properly handle advanced capabilities like Shadow DOM:&lt;br /&gt;
  633. &lt;ul style=&quot;text-align: left;&quot;&gt;
  634. &lt;li&gt;&lt;a href=&quot;https://reactjs.org/&quot; target=&quot;_blank&quot;&gt;React&lt;/a&gt;&lt;br /&gt;React Components are actually fairly different from Web Components. That being said, a React application can both consume custom elements and expose its components as custom elements, see: &lt;a href=&quot;https://reactjs.org/docs/web-components.html&quot;&gt;https://reactjs.org/docs/web-components.html&lt;/a&gt;. &lt;/li&gt;
  635. &lt;li&gt;&lt;a href=&quot;https://angular.io/&quot; target=&quot;_blank&quot;&gt;Angular&lt;/a&gt;&lt;br /&gt;Angular, if not built on top of the Web Components specification, is actually closer than React. The Angular team came with &lt;a href=&quot;https://angular.io/guide/elements&quot; target=&quot;_blank&quot;&gt;Angular Elements&lt;/a&gt;, which let&#39;s you package your components as custom elements.&lt;/li&gt;
  636. &lt;li&gt;&lt;a href=&quot;https://vuejs.org/&quot;&gt;Vue.JS&lt;/a&gt;&lt;br /&gt;Similarly, Vue has its own library to expose custom element: &lt;a href=&quot;https://github.com/vuejs/vue-web-component-wrapper&quot; target=&quot;_blank&quot;&gt;@vue/web-component-wrapper.&lt;/a&gt;&lt;/li&gt;
  637. &lt;/ul&gt;
  638. If using these well known technologies feels appealing, they come with some caveats/limitations when it is about Web Components: the component models have different life cycles, they carry more code than necessary, the features are not matching 1-1...&lt;br /&gt;
  639. &lt;ul style=&quot;text-align: left;&quot;&gt;
  640. &lt;/ul&gt;
  641. Fortunately a new generation of frameworks, based on top of the Web Components specification, appeared. Will they replace the legacy ones is a good question. It won&#39;t certainly happen soon, although they make it easier and more efficient when leveraging Web Components.&lt;br /&gt;
  642. Part of these frameworks are:&lt;br /&gt;
  643. &lt;ul style=&quot;text-align: left;&quot;&gt;
  644. &lt;li&gt;&lt;a href=&quot;https://lwc.dev/&quot;&gt;Lighning Web Components (LWC)&lt;/a&gt;&lt;br /&gt;This is one of the latest, but promising framework brought by &lt;a href=&quot;https://www.salesforce.com/&quot;&gt;Salesforce&lt;/a&gt;. Ok, my judgment is certainly biased because I&#39;m a Salesforce employee, and because we are using this technology on a daily basis. But trust me, it deserves a look :-) Most of my upcoming posts going further will be around on that technology!&lt;/li&gt;
  645. &lt;li&gt;&lt;a href=&quot;https://stenciljs.com/&quot;&gt;Stencil.JS&lt;/a&gt;&lt;br /&gt;This technology is developed by the &lt;a href=&quot;https://ionicframework.com/&quot;&gt;Ionic&lt;/a&gt; team, basically to fulfill their own needs. In short, the first Ionic release was Angular based and they got many requests for both a React and a Vue version. So they decided to standardize on a common Web Components implementation and then wrap them within each of these framework. &lt;/li&gt;
  646. &lt;li&gt;&lt;a href=&quot;https://www.polymer-project.org/&quot;&gt;Polymer&lt;/a&gt;&lt;br /&gt;That&#39;s one of the original frameworks brought by Google. It evolved a lot in the past 5 years, following the browser implementations, finally making it an efficient framework for writing Web Components. Finally, instead of the initial large monolithic framework, it now provides several projects like &lt;a href=&quot;https://lit-element.polymer-project.org/&quot;&gt;LitElement&lt;/a&gt; or &lt;a href=&quot;https://lit-html.polymer-project.org/&quot;&gt;lit-html.&lt;/a&gt;&lt;/li&gt;
  647. &lt;li&gt;&lt;a href=&quot;https://svelte.dev/&quot;&gt;Svelte&lt;/a&gt;&lt;br /&gt;Svelte is an efficient &lt;a href=&quot;https://svelte.dev/blog/svelte-3-rethinking-reactivity&quot;&gt;reactive&lt;/a&gt; library, oriented towards performance. It brings some innovation, particularly with the drop of the virtual DOM. Its community development model is very interesting, with an impressive list of involved people.&lt;/li&gt;
  648. &lt;li&gt;And many more!&lt;br /&gt;&lt;a href=&quot;http://./&quot;&gt;Hybrids&lt;/a&gt;, &lt;a href=&quot;https://slimjs.com/#/getting-started&quot;&gt;Slim.JS&lt;/a&gt;, &lt;a href=&quot;https://bosonic.github.io/&quot; target=&quot;_blank&quot;&gt;Bosonic,&lt;/a&gt; &lt;a href=&quot;https://riot.js.org/&quot;&gt;Riot.JS&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/JQWidgets#Smart_HTML_Elements&quot;&gt;Smart HTML Elements&lt;/a&gt;... And yes, I&#39;m missing some, in particular the ones that do not yet exist. &lt;/li&gt;
  649. &lt;/ul&gt;
  650. Choosing one framework or the other can be matter of preferences, skills or actual framework capabilities. But we should not forget a non negligible aspect, which is the sustainability of the technology:&lt;br /&gt;
  651. &lt;ul style=&quot;text-align: left;&quot;&gt;
  652. &lt;li&gt;Will the technology remain compatible over time?&lt;/li&gt;
  653. &lt;li&gt;How will it adapt to the new standards?&lt;/li&gt;
  654. &lt;li&gt;Who is behind it? Is it another open source project that will loose attention&amp;amp; love in a foreseeable future?&lt;/li&gt;
  655. &lt;li&gt;What is the community? &lt;/li&gt;
  656. &lt;/ul&gt;
  657. These are fair questions that we need to answer before we choose a technology for business purposes. Of course, your mileage may vary.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
  658. </description><link>http://blog.riand.com/2020/01/the-rise-of-web-components.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmF8b6Tmj3P6oG06A6XdmLHR7HpFDMguKU2z8WF6QPSJei0l5bmlymGqx2dTyIoYoSbliBtm3cRTMgcd2czglHJvOuRi_FYyr5LMhxKLyTk93xGdSdeKZbJPbLaph7D0o1mwvpRFWyEdM/s72-c/webcomponents.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-8449612503091481358</guid><pubDate>Mon, 03 Sep 2018 14:42:00 +0000</pubDate><atom:updated>2018-09-03T07:42:07.280-07:00</atom:updated><title>Give a new life to your old Android devices</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  659. As many mobile developers, I own a fair amount of devices that now start to show their age. They are still ok from an hardware standpoint, but Android is stuck at a 5 yrs old version. In particular, I own a few Samsung tablets (SM-T520, SM-T900...) blocked at 4.4 or 5.1. Samsung is really not good at upgrading their devices which is a shame. Yeah, I don&#39;t like Apple a lot, but my venerable iPad is smoothly running the latest iOS.&lt;br /&gt;
  660. &lt;br /&gt;
  661. I took the labor day week-end opportunity to see if there is a way to upgrade these devices. &lt;a href=&quot;https://en.wikipedia.org/wiki/CyanogenMod&quot; target=&quot;_blank&quot;&gt;Cyanogenmod&lt;/a&gt; is now gone, but the team started &lt;a href=&quot;https://lineageos.org/&quot; target=&quot;_blank&quot;&gt;LineageOS&lt;/a&gt;, which is an up-to-date Android distribution based on the former one. The T-520 being 4.4, I decided to give it a try as I have almost nothing to loose. I also found that we can always recover from a bricked device by downloading the original ROM from Samsung. Ok, let&#39;s take the risk!&lt;br /&gt;
  662. &lt;br /&gt;
  663. I used the instructions from here:&amp;nbsp;&lt;a href=&quot;https://wiki.lineageos.org/devices/n2awifi&quot;&gt;https://wiki.lineageos.org/devices/n2awifi&lt;/a&gt;. There are good so there is no need for me to repeat them, but just throw in a few findings:&lt;br /&gt;
  664. &lt;br /&gt;
  665. 1- I started using a Windows 10 machine, but it always failed installing the &lt;a href=&quot;https://glassechidna.com.au/heimdall/#downloads&quot; target=&quot;_blank&quot;&gt;Heimdall&lt;/a&gt; driver. I made different attempts, got different errors until I gave up. Maybe because I already had several Android related drivers installed on that machine? On the other hand, I did the same using my Macbook and it worked like a charm! Conclusion: use a a Mac, my friend!&lt;br /&gt;
  666. &lt;br /&gt;
  667. 2- The link to download TWRP is broken. I finally found mine here:&amp;nbsp;&lt;a href=&quot;https://dl.twrp.me/picassowifi/&quot;&gt;https://dl.twrp.me/picassowifi/&lt;/a&gt;. The name of the device was different as n2awifi became picassowifi.&lt;br /&gt;
  668. &lt;br /&gt;
  669. 3- Have an SD card ready on the device so you can copy the OS images on /sdcard. Not sure if it works with the main storage, as all the instructions refer to /sdcard.&lt;br /&gt;
  670. &lt;br /&gt;
  671. 4- The recovery UI is a bit tricky. To get the Google Apps installed, you have to flash them at the same time than the OS, before you even boot to LineageOS. If you don&#39;t do that, they will be unstable. The Android initial setup process is different with or without the Google apps, I tried both :-)&lt;br /&gt;
  672. When you flash the OS, first add the LineageOS image then, before flashing, click &#39;Add a new File&#39; and select the GApps files for you architecture. They will be installed in order.&lt;br /&gt;
  673. &lt;br /&gt;
  674. 5- I got multiple crashes while flashing the OS. Yes this is scary, but the tablet crashed and rebooted during that operation at different flashing steps. It happened to me 4 times before it finally completed successfully. Also, after a crash, the special reboot key sequence (home+volume up/down) did not always worked. Try to shutdown the tablet often restart it it, without offefing the opportunity to enter the recovery mode. Be patient, try as many times as needed, and you&#39;re eventually get there.&lt;br /&gt;
  675. &lt;br /&gt;
  676. The tablet is now working Android 7.1 smoothy, with all the apps I need. I have not seen any issue so far. I have not tried 8 yet as it is &lt;a href=&quot;https://download.lineageos.org/n2awifi&quot; target=&quot;_blank&quot;&gt;not officially supported&lt;/a&gt; (you can find the ROM anyway if you&#39;re adventurous). Maybe my next jump will be Lineage 16 (Android 9). I&#39;ll try that with the SM-T900!&lt;br /&gt;
  677. &lt;br /&gt;
  678. LineageOS is a great way to revitalize your own devices, or buy some old but good enough hardware and upgrade the software version. The SM-T520 has a great 10.1 bright screen (2560x1600) at nowadays a cheap price!&lt;br /&gt;
  679. &lt;br /&gt;
  680. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  681. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG-gCKe_PP1LVkFCLMrJPQs7WsJIXco3tW2bjdNgnqoqhZ0ihsFBBdzBecxs-nuL5mj_e0UJmFgNlZb9ZbjmljErTqvAEQ6mmlOYKKzbNx3ohxoIjTekYyfgI7ndWUX_xtdlTWzgKCiXs/s1600/lineageos-15.webp&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;581&quot; data-original-width=&quot;854&quot; height=&quot;217&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG-gCKe_PP1LVkFCLMrJPQs7WsJIXco3tW2bjdNgnqoqhZ0ihsFBBdzBecxs-nuL5mj_e0UJmFgNlZb9ZbjmljErTqvAEQ6mmlOYKKzbNx3ohxoIjTekYyfgI7ndWUX_xtdlTWzgKCiXs/s320/lineageos-15.webp&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  682. &lt;br /&gt;&lt;/div&gt;
  683. </description><link>http://blog.riand.com/2018/09/give-new-life-to-your-old-android.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG-gCKe_PP1LVkFCLMrJPQs7WsJIXco3tW2bjdNgnqoqhZ0ihsFBBdzBecxs-nuL5mj_e0UJmFgNlZb9ZbjmljErTqvAEQ6mmlOYKKzbNx3ohxoIjTekYyfgI7ndWUX_xtdlTWzgKCiXs/s72-c/lineageos-15.webp" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-8679579691808715808</guid><pubDate>Wed, 17 Jan 2018 16:10:00 +0000</pubDate><atom:updated>2018-01-17T08:10:34.143-08:00</atom:updated><title>Darwino makes OpenNTF mobile!</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  684. &lt;div&gt;
  685. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  686. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrv6AKICvneLyMYlbFoqA9JnS8-9PzQtq8IKEXHkVxcjwo6XM1DxL1D2fAtIZ86AuIWhO5B3R80B6wS00VTx9jb-JWMNRSHOIpfFDXlz6kiLeY19MHD_1vEiFACoW5CDkea-P30Rwl5Xw/s1600/screenshot.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;464&quot; data-original-width=&quot;456&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrv6AKICvneLyMYlbFoqA9JnS8-9PzQtq8IKEXHkVxcjwo6XM1DxL1D2fAtIZ86AuIWhO5B3R80B6wS00VTx9jb-JWMNRSHOIpfFDXlz6kiLeY19MHD_1vEiFACoW5CDkea-P30Rwl5Xw/s320/screenshot.png&quot; width=&quot;314&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  687. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  688. &lt;br /&gt;&lt;/div&gt;
  689. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  690. &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.darwino.domino.client.app&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;50&quot; data-original-width=&quot;150&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZSeZcc9yTdXBehtxGijAn4DQ_M_bIxQHCnJ2KdxGVz2OQzr1zWKTS9EcJpL1CTUdAu7LXhfMaDv3vNU_2fR4AZKhUtp_84uiAVbZB4P5g_jINGl8_m90gj8qODlVwObtaSKj_yBODKes/s1600/google-play-store.png&quot; /&gt;
  691. &lt;/a&gt;
  692. &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.darwino.domino.client.app&quot; style=&quot;display: none;&quot;&gt;
  693. &lt;img border=&quot;0&quot; data-original-height=&quot;50&quot; data-original-width=&quot;150&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1kyELr4w4qQcSC5LnhTlj2CUISODs883d-oCGaK-RlyWo8TVwLBXPusSHG6uykGyeoO3LYzD9y6I-jkxsz_isF_FLR0I4dXOin99Kj4WBvEOnE2KphEcp8zsLUcxDgLvTrAlEYroewig/s1600/apple-app-store.png&quot; /&gt;&lt;/a&gt;
  694. &lt;/div&gt;
  695. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  696. &lt;br /&gt;&lt;/div&gt;
  697. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
  698. &lt;i&gt;&lt;b&gt;[Latest News] The iOS app works perfectly, but we are struggling with the great fruity company to get it accepted to the store, because they claim there is not enough mobile content on OpenNTF... We&#39;ll do our best with the OpenNTF team to get more content leveraging mobile capabilities.&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
  699. &lt;br /&gt;
  700. &lt;b&gt;Darwino 2.0&lt;/b&gt; features a mobile micro application architecture well integrated with the IBM Domino server. It can render any existing Domino web application (classic &amp;amp; XPages) on a mobile device while taking care of the painful details:&lt;/div&gt;
  701. &lt;div&gt;
  702. &lt;ul style=&quot;text-align: left;&quot;&gt;
  703. &lt;li&gt;Real mobile applications available from both Google Play and Apple Store&lt;/li&gt;
  704. &lt;li&gt;Authentication management, to avoid prompting the user when the server requires credentials&lt;/li&gt;
  705. &lt;li&gt;Opening external pages in a separate browser window to provide the best user experience&lt;/li&gt;
  706. &lt;li&gt;Mobile notifications, to alert users when something new arrived and let him/her act appropriately&lt;/li&gt;
  707. &lt;li&gt;Plus many implementation details, like error management and recovery&lt;/li&gt;
  708. &lt;/ul&gt;
  709. &lt;/div&gt;
  710. &lt;div&gt;
  711. To quickly experiment with the capability, we are providing a generic mobile client that one can use to connect to any Darwino enabled Domino server. This client is available on both application stores and is fully configurable to point to any server. As a default configuration, we make it point to OpenNTF!&lt;br /&gt;
  712. &lt;br /&gt;&lt;/div&gt;
  713. As most of you know, the main OpenNTF applications, like &lt;a href=&quot;http://collaborationtoday.info/home.xsp?filter=all&quot; target=&quot;_blank&quot;&gt;Collaboration Today&lt;/a&gt;, are XPages based with a responsive design. This make them very easy to integrate with the micro application architecture. Moreover, Darwino extends Domino Designer with a new design element to detect database changes and send mobile notifications. With that in place on the OpenNTF servers, we are able to send new notifications when a new entry is available in Collaboration Today. By the way, these notifications use the mobile first IBM Cloud service (a.k.a. Bluemix).&lt;br /&gt;
  714. &lt;div&gt;
  715. &lt;br /&gt;&lt;/div&gt;
  716. &lt;div&gt;
  717. The implementation on the OpenNTF server was straight forward:&lt;/div&gt;
  718. &lt;div&gt;
  719. &lt;ul style=&quot;text-align: left;&quot;&gt;
  720. &lt;li&gt;Get the Darwino OSGi runtime installed&lt;/li&gt;
  721. &lt;li&gt;Set some configuration properties&lt;/li&gt;
  722. &lt;li&gt;Create a new design element for mobile notifications. To leave the existing databases untouched, we did that in brand new, separate database&lt;/li&gt;
  723. &lt;/ul&gt;
  724. &lt;/div&gt;
  725. &lt;div&gt;
  726. &lt;div&gt;
  727. Voila, in less than an hour it can be all setup. The same can happen to your own applications!&lt;/div&gt;
  728. &lt;div&gt;
  729. &lt;div&gt;
  730. &lt;br /&gt;&lt;/div&gt;
  731. &lt;div&gt;
  732. What are the next steps then? Well, a few things:&lt;/div&gt;
  733. &lt;div&gt;
  734. &lt;ul style=&quot;text-align: left;&quot;&gt;
  735. &lt;li&gt;We need to work with the OpenNTF team to mobile enable other parts of the site. Ideally, the whole site should be mobile enabled!&lt;/li&gt;
  736. &lt;li&gt;Why not white labeling the generic client with a pure OpenNTF branding (icon, title, ...)? Darwino offers this options.&lt;/li&gt;
  737. &lt;li&gt;Move forward and create Darwino single page applications, using web or mobile native technologies, to get the data offline and provide the best user experience without connectivity!&lt;/li&gt;
  738. &lt;/ul&gt;
  739. Stay tuned, OpenNTF becomes mobile!&lt;br /&gt;
  740. &lt;ul style=&quot;text-align: left;&quot;&gt;
  741. &lt;/ul&gt;
  742. &lt;/div&gt;
  743. &lt;/div&gt;
  744. &lt;/div&gt;
  745. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  746. &lt;br /&gt;&lt;/div&gt;
  747. &lt;div&gt;
  748. &lt;br /&gt;&lt;/div&gt;
  749. &lt;/div&gt;
  750. </description><link>http://blog.riand.com/2018/01/darwino-makes-openntf-mobile.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrv6AKICvneLyMYlbFoqA9JnS8-9PzQtq8IKEXHkVxcjwo6XM1DxL1D2fAtIZ86AuIWhO5B3R80B6wS00VTx9jb-JWMNRSHOIpfFDXlz6kiLeY19MHD_1vEiFACoW5CDkea-P30Rwl5Xw/s72-c/screenshot.png" height="72" width="72"/><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-7405653292475992649</guid><pubDate>Tue, 09 Jan 2018 18:46:00 +0000</pubDate><atom:updated>2018-01-09T10:46:10.526-08:00</atom:updated><title>Dynamic document based security</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  751. Domino provides document based security through the use of readers and authors fields. This is a great capability that is fairly unique in the NoSQL industry heavily used by Domino developer. One caveat though: you have to explicitly manage the list of users/group/roles in each document, generally based on the document state or content. How many times did you write an agent (background task) to update these fields nightly?&lt;br /&gt;
  752. &lt;br /&gt;
  753. Since the beginning, Darwino supports readers and authors (as well as excluded readers and authors to remove specific users from the authorized list). It is implemented by a hook in the SQL generator that systematically adds a condition to the queries. Here is how it looks like&amp;nbsp;in pseudo SQL:&lt;br /&gt;
  754. &lt;blockquote class=&quot;tr_bq&quot;&gt;
  755. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;SELECT D.JSON FROM DOC D WHERE (....) AND (D.READERS IN (:userid, :userroles, :usergroups...)&lt;/span&gt;&lt;/blockquote&gt;
  756. &lt;br /&gt;
  757. &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;userid&lt;/span&gt;, &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;userroles&lt;/span&gt; and &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;usergroups&lt;/span&gt; are values related to the user making the request.&lt;br /&gt;
  758. All of this is implemented (and by the way optimized) at the core Darwino level. So it applies regardless of API you use: Java, JavaScript, REST services, GraphQL...&lt;br /&gt;
  759. &lt;br /&gt;
  760. But in 2.1, it goes beyond this simple capability: it allows an application to insert its own SQL filter:&lt;br /&gt;
  761. &lt;blockquote class=&quot;tr_bq&quot;&gt;
  762. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;SELECT D.JSON FROM DOC D WHERE (....) AND (&amp;lt;custom condition&amp;gt;)&lt;/span&gt;&lt;/blockquote&gt;
  763. &lt;br /&gt;
  764. For example, the custom condition can be based on fields within the document. Let&#39;s suppose that the current user object has an attribute &#39;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;country&lt;/span&gt;&#39; set in LDAP. Let&#39;s also suppose that the documents in a JSON store also have a field name &#39;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;country&lt;/span&gt;&#39;. From a security standpoint, we only want to allow the users to see the documents tagged with their country. With classic readers fields, you&#39;ll need an agent that periodically updates the document readers, or use groups and assign users to groups. In both cases, there is some maintenance to do.&lt;br /&gt;
  765. &lt;br /&gt;
  766. With Darwino, it becomes as easy as implementing a runtime extension that returns a condition for the current user (again, simplified pseudo code):&lt;br /&gt;
  767. &lt;br /&gt;
  768. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;String getDynamicSecurity() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; User u = getCurrentUser();&lt;br /&gt;&amp;nbsp; &amp;nbsp; return &quot;{country: &#39;&quot;+u.getCountryAttribute()+&quot;&#39;}&quot;;&lt;/span&gt;&lt;br /&gt;
  769. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;}&lt;/span&gt;&lt;br /&gt;
  770. &lt;br /&gt;
  771. There is no need to maintain a list of readers here. If a user is moved to a different country, then the query will use the latest user country (or any other needed value), immediately!&lt;br /&gt;
  772. &lt;br /&gt;
  773. The dynamic security condition can be expressed using the built-in, MongoDB like, query language as above. This platform independent query is transpiled to SQL by the core Darwino code.&lt;br /&gt;
  774. But it can also be expressed as raw SQL, allowing the filter to be as complex as desired. For example, it can lookup in another SQL table, view or stored procedure for a list of authorized ids for the current user:&lt;br /&gt;
  775. &lt;blockquote class=&quot;tr_bq&quot;&gt;
  776. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;... AND (D.DOCID IN SELECT ID FROM SECURITY SEC WHERE SEC.USERID=&#39;currentuserid&#39;)&lt;/span&gt;&lt;/blockquote&gt;
  777. This is limitless. Let&#39;s suppose that you have a users database where each user document has the name of its manager. Now suppose that user documents can only be accessed by the user and his managers. Well, just get the list of authorized IDs from a recursive SELECT statement&lt;br /&gt;
  778. &lt;blockquote class=&quot;tr_bq&quot;&gt;
  779. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;... AND (D.USERID IN WITH RECURSIVE SELECT ...)&lt;/span&gt;&lt;/blockquote&gt;
  780. If the subquery becomes too complex and leads to performance issues, you can create materialized views that will be managed by the database server.&lt;br /&gt;
  781. &lt;br /&gt;
  782. Come on MongoDB, you cannot compete against the power of relational databases :-)&lt;br /&gt;
  783. &lt;br /&gt;
  784. &lt;br /&gt;
  785. &lt;br /&gt;
  786. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  787. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3RhyphenhypheneRf5yFBmoL_D4qV5320xUXndsR-9ebsxu8ZKlOTK5dT0H1qdRQTZbIMIPA5OoJ63H5_dPdbwfkHiAxK5oWPg3DL2jkbxzg0byUbjpPtEZNeW36HbnGWSe0KPTVwTEwr-u4MDhKVw/s1600/document-lock.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;256&quot; data-original-width=&quot;256&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3RhyphenhypheneRf5yFBmoL_D4qV5320xUXndsR-9ebsxu8ZKlOTK5dT0H1qdRQTZbIMIPA5OoJ63H5_dPdbwfkHiAxK5oWPg3DL2jkbxzg0byUbjpPtEZNeW36HbnGWSe0KPTVwTEwr-u4MDhKVw/s1600/document-lock.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  788. &lt;br /&gt;&lt;/div&gt;
  789. </description><link>http://blog.riand.com/2018/01/dynamic-document-based-security.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3RhyphenhypheneRf5yFBmoL_D4qV5320xUXndsR-9ebsxu8ZKlOTK5dT0H1qdRQTZbIMIPA5OoJ63H5_dPdbwfkHiAxK5oWPg3DL2jkbxzg0byUbjpPtEZNeW36HbnGWSe0KPTVwTEwr-u4MDhKVw/s72-c/document-lock.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-1557572243689491955</guid><pubDate>Thu, 04 Jan 2018 17:24:00 +0000</pubDate><atom:updated>2018-01-04T09:24:31.360-08:00</atom:updated><title>Offline your data with React-Native applications</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  790. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  791. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQHImtwWMtQznUT1xUoE-BB8loZbXkhn63dnOZ_oJVqIydLIvjn1jzvqz_PUO2DATHGH9B_mRLfmgoy6OnHnh9MY1oLYZU3VfqEmmyz_R9qMz0v3Kv46_kLrw7z-isbU-SJf6bvyrJkp0/s1600/react-native.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;369&quot; data-original-width=&quot;404&quot; height=&quot;182&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQHImtwWMtQznUT1xUoE-BB8loZbXkhn63dnOZ_oJVqIydLIvjn1jzvqz_PUO2DATHGH9B_mRLfmgoy6OnHnh9MY1oLYZU3VfqEmmyz_R9qMz0v3Kv46_kLrw7z-isbU-SJf6bvyrJkp0/s200/react-native.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  792. &lt;br /&gt;
  793. In a previous post, I mentioned how we are leveraging ReactJS to create state-of-the-art web applications. We are now moving a step forward by also leveraging its sister technology, React-Native, to create portable, yet native, mobile applications. With all the benefits of Darwino, which includes a local JSON store that replicates for the best offline experience, and all the other services provided by Darwino!&lt;br /&gt;
  794. &lt;br /&gt;
  795. To make this as easy as possible, we are providing a simple service based API, that can be called from any native language (Java, Objective-C, Swift).&lt;br /&gt;
  796. &lt;br /&gt;
  797. &lt;b&gt;1- Darwino service API&lt;/b&gt;&lt;br /&gt;
  798. &lt;b&gt;&lt;br /&gt;&lt;/b&gt;
  799. The API is basically made of one class, exposed bellow in pseudo code:&lt;br /&gt;
  800. &lt;br /&gt;
  801. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;class DarwinoApis {&lt;/span&gt;&lt;br /&gt;
  802. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; ...&lt;/span&gt;&lt;br /&gt;
  803. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; Object fetch(url, params, headers, content)&lt;/span&gt;&lt;br /&gt;
  804. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; ...&lt;/span&gt;&lt;br /&gt;
  805. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;}&lt;/span&gt;&lt;br /&gt;
  806. &lt;br /&gt;
  807. The &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;fetch&lt;/span&gt; method calls the service identified by the URL and associated parameters, and returns the result. Behind the hood, such the API call will be either translated to a remote HTTP service call (online mode), or to a local API call (offline mode). This is done transparently by a dispatcher, so the developer does not have to worry about the current mode. It just works! This API can be called from *any* native application, regardless of the library/framework it uses.&lt;br /&gt;
  808. &lt;br /&gt;
  809. This shows the beauty of the Darwino architecture: the services are all written independently from the HTTP stack, and thus can be executed locally without the need of HTTP calls. It makes them truly available offline, while providing the best possible performance as it also avoids the data serialization/deserialization needed by the HTTP transport.&lt;br /&gt;
  810. &lt;br /&gt;
  811. Here is a basic architecture picture:&lt;br /&gt;
  812. &lt;br /&gt;
  813. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  814. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsrzFdHaw1JAe3UEjzovpNSHvgX92r8FTUPhO2YXEAfC6RvpPTDDVa7NLiBeQk9hEY5bGadr8Q5CYltUNv8ABOkNuks5oG75-iWBn3whvMJtslq6fUv6sPd2VTp8v-0KSA8TRLnuIKeZw/s1600/mobile+architecture.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;480&quot; data-original-width=&quot;449&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsrzFdHaw1JAe3UEjzovpNSHvgX92r8FTUPhO2YXEAfC6RvpPTDDVa7NLiBeQk9hEY5bGadr8Q5CYltUNv8ABOkNuks5oG75-iWBn3whvMJtslq6fUv6sPd2VTp8v-0KSA8TRLnuIKeZw/s320/mobile+architecture.png&quot; width=&quot;299&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  815. &lt;br /&gt;
  816. &lt;br /&gt;
  817. &lt;b&gt;2- ReactNative integration&lt;/b&gt;&lt;br /&gt;
  818. &lt;br /&gt;
  819. As the API is exposed using the platform native languages, it is easy to bridge it with ReactNative. Moreover, the bridge enforces the use of &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;promises&lt;/span&gt;, thus enabling the new &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;aync&lt;/span&gt;&amp;nbsp;&amp;amp; &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;await&lt;/span&gt;&amp;nbsp;capability for the best developer experience.&lt;br /&gt;
  820. &lt;br /&gt;
  821. Here is, for example, how to retrieve the number of documents from a database, regardless if it is local or remote:&lt;br /&gt;
  822. &lt;br /&gt;
  823. &lt;div style=&quot;background-color: #1e1e1e; color: #d4d4d4; font-family: Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; white-space: pre;&quot;&gt;
  824. &lt;span style=&quot;color: #569cd6;&quot;&gt;async&lt;/span&gt; &lt;span style=&quot;color: #dcdcaa;&quot;&gt;databaseCount&lt;/span&gt;() {
  825. &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #569cd6;&quot;&gt;let&lt;/span&gt; &lt;span style=&quot;color: #9cdcfe;&quot;&gt;url&lt;/span&gt; = [&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;$darwino-jstore&quot;&lt;/span&gt;,&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;databases&quot;&lt;/span&gt;,&lt;span style=&quot;color: #569cd6;&quot;&gt;this&lt;/span&gt;.&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getDatabase&lt;/span&gt;(),&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;documentscount&quot;&lt;/span&gt;]
  826. &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #569cd6;&quot;&gt;let&lt;/span&gt; &lt;span style=&quot;color: #9cdcfe;&quot;&gt;count&lt;/span&gt; = &lt;span style=&quot;color: #c586c0;&quot;&gt;await&lt;/span&gt; &lt;span style=&quot;color: #9cdcfe;&quot;&gt;darwinoApis&lt;/span&gt;.&lt;span style=&quot;color: #dcdcaa;&quot;&gt;fetch&lt;/span&gt;(&lt;span style=&quot;color: #9cdcfe;&quot;&gt;url&lt;/span&gt;);
  827. &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #569cd6; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace;&quot;&gt;println&lt;/span&gt;(&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;This database contains {0}&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt; documents&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace;&quot;&gt;count&lt;/span&gt;);&lt;br /&gt;
  828. }&lt;/div&gt;
  829. Easy, isn&#39;t it?&lt;br /&gt;
  830. &lt;br /&gt;
  831. Triggering the database replication isn&#39;t more difficult, see:&lt;br /&gt;
  832. &lt;br /&gt;
  833. &lt;div style=&quot;background-color: #1e1e1e; font-family: Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; white-space: pre;&quot;&gt;
  834. &lt;span style=&quot;color: #569cd6;&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;apis&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;replicate&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getDatabase&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(),&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);
  835. &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;println&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;{0}&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
  836. &lt;b&gt;&lt;br /&gt;&lt;/b&gt;
  837. &lt;b&gt;3- Darwino API distribution&lt;/b&gt;&lt;br /&gt;
  838. &lt;br /&gt;
  839. The APIs is provided as a library to be added to any existing application. On Android, it is a maven artifact that you can reference from your gradle build file. On iOS, it is a Cocoa framework that you can add to your XCode project. Just add these dependencies and you&#39;re ready to go!&lt;br /&gt;
  840. &lt;br /&gt;
  841. Android:&lt;br /&gt;
  842. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
  843. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_eYcSHG7M3ezzUuO3aPH9vF25VYDZpv0HSOb9MPdsBPPNOjdpj5R9BPNZ2sowWtSU0t2r7b7-Kt5wGtVcUJGjL3JXZ2AO6LSkNLGBA4D6Fkxs_rWTFZJ2aOybTeXvmSBMY_M6QlUPdiE/s1600/demomac-android.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;148&quot; data-original-width=&quot;626&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_eYcSHG7M3ezzUuO3aPH9vF25VYDZpv0HSOb9MPdsBPPNOjdpj5R9BPNZ2sowWtSU0t2r7b7-Kt5wGtVcUJGjL3JXZ2AO6LSkNLGBA4D6Fkxs_rWTFZJ2aOybTeXvmSBMY_M6QlUPdiE/s1600/demomac-android.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  844. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  845. &lt;br /&gt;&lt;/div&gt;
  846. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
  847. iOS:&lt;/div&gt;
  848. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  849. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkc19CzTLYcNeW2Q63p0UjBD6v-mb5S5mtLj2TSNSOUN4yeHFZ-vJUNbAKhQ1QKfKLxQ1UuzMB9leUf8YWw8OwDtuSidHBxivi4FrG_tvPM5A9ZkVxcNN1Iz2HpcQ9mbN5Fah6lD4Axyo/s1600/demomac-ios.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;146&quot; data-original-width=&quot;563&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkc19CzTLYcNeW2Q63p0UjBD6v-mb5S5mtLj2TSNSOUN4yeHFZ-vJUNbAKhQ1QKfKLxQ1UuzMB9leUf8YWw8OwDtuSidHBxivi4FrG_tvPM5A9ZkVxcNN1Iz2HpcQ9mbN5Fah6lD4Axyo/s1600/demomac-ios.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  850. &lt;b&gt;&lt;br class=&quot;Apple-interchange-newline&quot; /&gt;&lt;/b&gt;
  851. &lt;b&gt;4- Under the hood&lt;/b&gt;&lt;br /&gt;
  852. &lt;br /&gt;
  853. Wait, Darwino is Java based, so how can we provide a native Objective-C or Swift API? How can we get all the Darwino services, built-in and customs, run natively on any device? As the goal is to have one single Java source code to ensure the consistency between all the platforms, so how is is exposed as an Objective-C API?&lt;br /&gt;
  854. &lt;br /&gt;
  855. The magic comes from &lt;a href=&quot;https://developers.google.com/j2objc/&quot; target=&quot;_blank&quot;&gt;J2OBJC&lt;/a&gt;. Right from the beginning, we have been very careful to write &quot;portable&quot; Java, meaning Java that can be compiled or transpiled to many different environments (JRE, Dalvik/Art, RoboVM, Multi-OS Engine...). As J2OBJC made a lot of progress these past years, it became easy to get our code transpiled to J2OBJC. The result is a tiny, efficient, native Objective-C library while sharing the same Java source code.&lt;br /&gt;
  856. With the potential to target other platforms in the future, like .NET: what about having your Domino data replicated natively to .NET? :-)&lt;br /&gt;
  857. &lt;br /&gt;&lt;/div&gt;
  858. </description><link>http://blog.riand.com/2018/01/offline-your-data-with-react-native.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQHImtwWMtQznUT1xUoE-BB8loZbXkhn63dnOZ_oJVqIydLIvjn1jzvqz_PUO2DATHGH9B_mRLfmgoy6OnHnh9MY1oLYZU3VfqEmmyz_R9qMz0v3Kv46_kLrw7z-isbU-SJf6bvyrJkp0/s72-c/react-native.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-6572401689923569116</guid><pubDate>Tue, 02 Jan 2018 21:07:00 +0000</pubDate><atom:updated>2018-01-04T14:13:26.642-08:00</atom:updated><title>Darwino 2.0 is out!</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  859. &lt;div style=&quot;text-align: center;&quot;&gt;
  860. I&#39;m very exited to announce that the official&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #3d85c6;&quot;&gt;&lt;a href=&quot;https://www.darwino.com/&quot; target=&quot;_blank&quot;&gt;Darwino 2.0&lt;/a&gt;&lt;/span&gt;&lt;/b&gt; has just been released!&lt;/div&gt;
  861. &lt;br /&gt;
  862. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  863. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5LYpOlmY5yic3_nTh7pXYmaD3BEwT9eQjpoFNMJhOf_Se2FrQbFR-nboh4VM2Y1aWi7r9cGPO54itIg6a0tV_JuXULLytEdSZ8z1rJl8BLPYiAGmnmmUvxueJfR_YLOI6wJr-Hb9kMdM/s1600/darwino-logo.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;90&quot; data-original-width=&quot;197&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5LYpOlmY5yic3_nTh7pXYmaD3BEwT9eQjpoFNMJhOf_Se2FrQbFR-nboh4VM2Y1aWi7r9cGPO54itIg6a0tV_JuXULLytEdSZ8z1rJl8BLPYiAGmnmmUvxueJfR_YLOI6wJr-Hb9kMdM/s1600/darwino-logo.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  864. &lt;br /&gt;
  865. &lt;br /&gt;
  866. It had been a long journey since the previous release but most of our customers are already using the pre 2.0 release, aka 2.0.0-SNAPSHOT in maven terms. This means that this release has already been tested in many contexts and is thus very stable.&lt;br /&gt;
  867. &lt;br /&gt;
  868. The main topics for this major release are:&lt;br /&gt;
  869. &lt;br /&gt;
  870. &lt;ul style=&quot;text-align: left;&quot;&gt;
  871. &lt;li&gt;&lt;b&gt;Creation of new web and mobile applications&lt;/b&gt;&lt;br /&gt;- Provide the best user experience even when the device is offline&lt;br /&gt;- Leverage of the latest technologies, including ReactJS&lt;/li&gt;
  872. &lt;li&gt;&lt;b&gt;Modernization of existing applications, in particular Notes &amp;amp; Domino&lt;/b&gt;&lt;br /&gt;- Liberate the Domino data and take advantage of any data reporting/analytics tool&lt;br /&gt;- Get your existing Notes/Domino apps on mobile device with notifications&lt;br /&gt;- Create modern mobile applications connected to your IBM Domino data&lt;/li&gt;
  873. &lt;li&gt;&lt;b&gt;Migration of IBM Notes/Domino applications&lt;/b&gt;&lt;br /&gt;- Incremental migration to minimize the risk&lt;br /&gt;- Use of modern technologies and development tools&lt;br /&gt;- Integrate with MS Sharepoint, Office 365 or other platforms, like Oracle Cloud&lt;/li&gt;
  874. &lt;li&gt;&lt;b&gt;Many additions and enhancements to the existing APIs&lt;/b&gt;&lt;/li&gt;
  875. &lt;/ul&gt;
  876. &lt;br /&gt;
  877. I bet that the mobile client is going to be popular in our community because... Well, read my next post :-)&lt;br /&gt;
  878. &lt;br /&gt;
  879. Bellow is a list of the most noticeable features in 2.0:&lt;br /&gt;
  880. &lt;br /&gt;
  881. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  882. Darwino for mobile devices&lt;/h3&gt;
  883. &lt;b&gt;&lt;br /&gt;&lt;/b&gt;
  884. &lt;b&gt;Darwino mobile client&lt;/b&gt;&lt;br /&gt;
  885. Darwino provides a generic client that renders server side web applications and handles IBM Cloud notifications. This client is available for both iOS and Android, in their respective application stores.&lt;br /&gt;
  886. This client can also be fully customized as the full source code is available. For example, one can white label the application with a custom icon, look&amp;amp;feel, options...&lt;br /&gt;
  887. &lt;br /&gt;
  888. &lt;b&gt;Multi-OS-Engine instead of RoboVM&lt;/b&gt;&lt;br /&gt;
  889. Darwino switched to &lt;a href=&quot;https://multi-os-engine.org/&quot; target=&quot;_blank&quot;&gt;MOE &lt;/a&gt;as RoboVM is no longer available since it has been acquired by Microsoft. Darwino 2.0 supports the latest version of MOE. Thanks &lt;a href=&quot;https://www.migeran.com/&quot; target=&quot;_blank&quot;&gt;Migeran &lt;/a&gt;for doing this great product!&lt;br /&gt;
  890. &lt;br /&gt;
  891. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  892. &lt;/h3&gt;
  893. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  894. Integration&lt;/h3&gt;
  895. &lt;b&gt;&lt;br /&gt;&lt;/b&gt;
  896. &lt;b&gt;Notification framework&lt;/b&gt;&lt;br /&gt;
  897. Darwino provides a notification framework that executes actions based on events. The framework can detect data changes in a database, including domino databases, and run actions like a mobile push notifications, notifying IBM Workspace, sending an email or whatever a piece of code can do...&lt;br /&gt;
  898. &lt;br /&gt;
  899. &lt;b&gt;Microsoft Azure active directory support&lt;/b&gt;&lt;br /&gt;
  900. Darwino features a driver to consume Microsoft Azure active directory services&lt;br /&gt;
  901. &lt;br /&gt;
  902. &lt;b&gt;Microsoft Sharepoint integration&lt;/b&gt;&lt;br /&gt;
  903. Darwino integration with Sharepoint, including directory access and single sign-on.&lt;br /&gt;
  904. &lt;br /&gt;
  905. &lt;div&gt;
  906. &lt;b&gt;More database support&lt;/b&gt;&lt;br /&gt;
  907. The MS SQL server support is complete, as well as MySQL (the full JSQL requires MySQL 8+).&lt;/div&gt;
  908. &lt;div&gt;
  909. &lt;br /&gt;&lt;/div&gt;
  910. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  911. Darwino DB&lt;/h3&gt;
  912. &lt;b&gt;&lt;br /&gt;&lt;/b&gt;
  913. &lt;b&gt;JSQL&lt;/b&gt;&lt;br /&gt;
  914. JSQL is an SQL dialect dedicated to JSON databases. It allows the full power of SQL queries on top of JSON documents: joins, unions, groups, recursive queries, ... while honoring the document level security. It eventually translates to the native database SQL for maximum performance.&lt;br /&gt;
  915. &lt;a href=&quot;https://playground.darwino.com/playground.nsf/JsqlSnippets.xsp&quot;&gt;https://playground.darwino.com/playground.nsf/JsqlSnippets.xsp&lt;/a&gt;&lt;br /&gt;
  916. &lt;br /&gt;
  917. &lt;b&gt;Registered queries&lt;/b&gt;&lt;br /&gt;
  918. Database queries can be defined on the server side and consumed by name from the client. This is also true for JSQL queries.&lt;br /&gt;
  919. &lt;a href=&quot;https://playground.darwino.com/playground.nsf/JavaSnippets.xsp#snippet=Json_Store_Cursor_Run_Predefined_Query&quot;&gt;https://playground.darwino.com/playground.nsf/JavaSnippets.xsp#snippet=Json_Store_Cursor_Run_Predefined_Query&lt;/a&gt;&lt;br /&gt;
  920. &lt;br /&gt;
  921. &lt;b&gt;Domino @formula&lt;/b&gt;&lt;br /&gt;
  922. An extension to the query language uses the new keyword $atFormula and allows the use of a subset of the Notes/Domino @formula language in Darwino queries. That makes the migration of Notes/Domino views a lot easier!&lt;br /&gt;
  923. &lt;a href=&quot;https://playground.darwino.com/playground.nsf/DarwinoDbSnippets.xsp#snippet=@Formula_Simple&quot;&gt;https://playground.darwino.com/playground.nsf/DarwinoDbSnippets.xsp#snippet=@Formula_Simple&lt;/a&gt;&lt;br /&gt;
  924. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  925. &lt;/h3&gt;
  926. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  927. &lt;/h3&gt;
  928. &lt;div&gt;
  929. &lt;br /&gt;&lt;/div&gt;
  930. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  931. REST services&lt;/h3&gt;
  932. &lt;b&gt;&lt;br /&gt;&lt;/b&gt;
  933. &lt;b&gt;JSON store services&lt;/b&gt;&lt;br /&gt;
  934. Added some new services to retrieve document content without the meta-data.&lt;br /&gt;
  935. &lt;a href=&quot;https://playground.darwino.com/playground.nsf/OpenApiExplorer.xsp#openApi=Json_Store_Document_CRUD&quot;&gt;https://playground.darwino.com/playground.nsf/OpenApiExplorer.xsp#openApi=Json_Store_Document_CRUD&lt;/a&gt;&lt;br /&gt;
  936. &lt;br /&gt;
  937. &lt;b&gt;GraphQL&lt;/b&gt;&lt;br /&gt;
  938. &lt;b&gt;&lt;br /&gt;&lt;/b&gt;
  939. Darwino comes with a innovative implementation of FaceBook&#39;s GraphQL, allowing both schema based and schema less queries. Moreover, GraphQL queries can be pre-defined on the server side to make them easier to consume&lt;br /&gt;
  940. &lt;a href=&quot;https://playground.darwino.com/playground.nsf/GraphqlSnippets.xsp&quot;&gt;https://playground.darwino.com/playground.nsf/GraphqlSnippets.xsp&lt;/a&gt;&lt;br /&gt;
  941. &lt;br /&gt;
  942. &lt;b&gt;Microservices&lt;/b&gt;&lt;br /&gt;
  943. Darwino provides a easy to use JSON microservice infrastructure. The services can be written in Java, Groovy, JavaScript, Darwino Script or any language supported by the JVM.&lt;br /&gt;
  944. &lt;a href=&quot;https://playground.darwino.com/playground.nsf/ServicesSnippets.xsp&quot;&gt;https://playground.darwino.com/playground.nsf/ServicesSnippets.xsp&lt;/a&gt;&lt;br /&gt;
  945. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  946. &lt;/h3&gt;
  947. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  948. &lt;/h3&gt;
  949. &lt;div&gt;
  950. &lt;br /&gt;&lt;/div&gt;
  951. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  952. Darwino Studio&lt;/h3&gt;
  953. &lt;b&gt;&lt;br /&gt;&lt;/b&gt;
  954. &lt;b&gt;Application Wizard&lt;/b&gt;&lt;br /&gt;
  955. The application creation wizard can now use templates, also known as boilerplate, to get the developer started quickly. A new template using ReactJS and Bootstrap is provided as part of the studio. The architecture is open and any developer can contribute new templates.&lt;br /&gt;
  956. &lt;br /&gt;
  957. &lt;b&gt;Android studio&lt;/b&gt;&lt;br /&gt;
  958. The wizard can also leverage Android studio for the mobile applications, instead of Eclipse. This allows the developers to benefit from the latest features provided by Google and Multi-OS-Engine.&lt;br /&gt;
  959. &lt;br /&gt;
  960. &lt;b&gt;UI Builder&lt;/b&gt;&lt;br /&gt;
  961. Darwino studio features an easy form builder that targets mobile devices. This builder is fully extensible to support more targets in the future.&lt;br /&gt;
  962. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  963. &lt;/h3&gt;
  964. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  965. &lt;/h3&gt;
  966. &lt;div&gt;
  967. &lt;br /&gt;&lt;/div&gt;
  968. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  969. Darwino for IBM Notes/Domino&lt;/h3&gt;
  970. &lt;b&gt;&lt;br /&gt;&lt;/b&gt;
  971. &lt;b&gt;Domino Designer&lt;/b&gt;&lt;br /&gt;
  972. Darwino provides a new Domino Designer plugin that adds a new design element dealing with the notifications. It uses a groovy based DSL (Domain Specific Language) .&lt;br /&gt;
  973. &lt;br /&gt;
  974. &lt;b&gt;Darwino client&lt;/b&gt;&lt;br /&gt;
  975. The Darwino mobile client detects if the server is an IBM Domino server and adapts some behaviors accordingly, like error handling.&lt;br /&gt;
  976. &lt;br /&gt;
  977. &lt;b&gt;Darwino application&lt;/b&gt;&lt;br /&gt;
  978. Darwino provides new runtime capabilities to supplement IBM Domino missing features, like JEE filters. Moreover, Darwino applications can be auto-started, for example to monitor events.&lt;br /&gt;
  979. &lt;br /&gt;
  980. &lt;div&gt;
  981. &lt;b&gt;IBM Domino import&lt;/b&gt;&lt;br /&gt;
  982. The Darwino studio can generate a basic UI based on existing IBM Domino NSFs. This feature is a preview, but this is something we&#39;d like to enhance moving forward, to get the mobilization or migration of Notes/Domino import easier.&lt;br /&gt;
  983. It also generates JSON Schemas based on the Form definition in an NSF.&lt;/div&gt;
  984. &lt;div&gt;
  985. &lt;h3&gt;
  986. &lt;/h3&gt;
  987. &lt;h3&gt;
  988. &lt;/h3&gt;
  989. &lt;div&gt;
  990. &lt;br /&gt;&lt;/div&gt;
  991. &lt;h3&gt;
  992. Developer tools&lt;/h3&gt;
  993. &lt;b&gt;&lt;br /&gt;&lt;/b&gt;
  994. &lt;b&gt;Commands framework&lt;/b&gt;&lt;br /&gt;
  995. Darwino features a command line framework that developer can add to any application to execute administration commands or debug an application.&lt;br /&gt;
  996. It also includes a built-in application profiler.&lt;br /&gt;
  997. &lt;br /&gt;&lt;/div&gt;
  998. &lt;div style=&quot;text-align: left;&quot;&gt;
  999. &lt;b&gt;Playground&lt;/b&gt;&lt;/div&gt;
  1000. The Playground now show cases the new features like JSQL or GraphQL, but also exposes the REST apis using OpenAPI/Swagger: &lt;a href=&quot;https://playground.darwino.com/playground.nsf/OpenApiExplorer.xsp&quot;&gt;https://playground.darwino.com/playground.nsf/OpenApiExplorer.xsp&lt;/a&gt;&lt;/div&gt;
  1001. </description><link>http://blog.riand.com/2018/01/darwino-20-is-out.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5LYpOlmY5yic3_nTh7pXYmaD3BEwT9eQjpoFNMJhOf_Se2FrQbFR-nboh4VM2Y1aWi7r9cGPO54itIg6a0tV_JuXULLytEdSZ8z1rJl8BLPYiAGmnmmUvxueJfR_YLOI6wJr-Hb9kMdM/s72-c/darwino-logo.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-2209506527472084293</guid><pubDate>Fri, 15 Dec 2017 23:26:00 +0000</pubDate><atom:updated>2017-12-15T15:26:52.092-08:00</atom:updated><title>Ubiquity of Java and relational databases</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  1002. &lt;div&gt;
  1003. &lt;br /&gt;&lt;/div&gt;
  1004. &lt;div&gt;
  1005. I&#39;m sometimes being asked about our platform choices of Java and RDBMS for Darwino. More precisely, I&#39;m getting questions about Node.js or existing JSON document databases, like MongoDB or CouchDB.&lt;br /&gt;
  1006. Well, I can certainly expand on the technical value of our choices, and I&#39;ll probably will. But, if the technical aspect is important, this is not the only driver. &amp;nbsp;It is not even the main one: what made us choose these technologies can be summarized in 3 words: &lt;b&gt;ubiquity&lt;/b&gt;, &lt;b&gt;diversity&lt;/b&gt; and &lt;b&gt;skill and assets&lt;/b&gt;.&lt;br /&gt;
  1007. &lt;br /&gt;&lt;/div&gt;
  1008. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  1009. Ubiquity&lt;/h2&gt;
  1010. &lt;div&gt;
  1011. Most of our customers have a Java application server and a relational database already in place. Both are almost everywhere. If small companies can be flexible with technology, bigger companies tend to be more conservative. And not to talk about administrations. With Java and RDBMS you can get in right away without going through technology validation processes.&lt;br /&gt;
  1012. &lt;br /&gt;&lt;/div&gt;
  1013. &lt;div&gt;
  1014. The cloud is not different: whenever you want to use MS Azure, IBM Bluemix, Oracle Cloud, Amazon AWS, they all offer you PAAS services with a Java application server and an RDBMS. As a result, your not locked with a vendor. For example, and despite its value, if you choose IBM Cloudant, you&#39;re locked with IBM Cloud. For the good and the bad... Did I say the bad?&lt;br /&gt;
  1015. &lt;br /&gt;&lt;/div&gt;
  1016. &lt;div&gt;
  1017. It similarly expands to mobile devices. There is no MongoDB implementation running on these devices, providing offline capabilities with the same set of features. Ok, Cloudant/CouchDB has TouchDB or PouchDB for offline, but these are totally different implementations with different APIs and capabilities. On the other hand, SQLite can be run on any mobile devices from Java.&lt;br /&gt;
  1018. &lt;br /&gt;
  1019. With Darwino encapsulating the platform differences, like SQL dialects, you write the application once and you can run everywhere, including on mobile devices.&lt;br /&gt;
  1020. &lt;br /&gt;&lt;/div&gt;
  1021. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  1022. Diversity&lt;/h2&gt;
  1023. &lt;div&gt;
  1024. There is not a single implementation of Java and RDBMS. Rather, you can find several vendors providing their own JVM, web application servers or RDBMS, with different characteristics. You pick and choose the one that better fits your need, based on both technical or business decisions.&lt;br /&gt;
  1025. &lt;br /&gt;&lt;/div&gt;
  1026. &lt;div&gt;
  1027. Diversity fosters innovation because of the competition: the vendors have to innovate to keep the leadership. Moreover, when a great innovation is brought by one vendor, the other vendors will follow shortly. This is exactly what happened recently with the support of JSON data type, or the in-memory RDBMS. Same with Java processing optimization, like async/actor programming (Akka, Vert.x), or distributed VMs, ... In both spaces, the innovation has been rocking these past years, for our own benefit!&lt;br /&gt;
  1028. &lt;br /&gt;&lt;/div&gt;
  1029. &lt;div&gt;
  1030. On the other hand, if you choose Node.js, well you&#39;re locked to one vendor, and even one JS engine (v8). I&#39;m not saying that these are bad, just that you&#39; don&#39;t have any choice. Same, and even worse, with MongoDB: do you want to put your business critical data into the hand of a single database vendor? If something goes wrong with these guys, what are your options beyond re-coding your applications to another API/vendor? This is about risk management, a point that every CIO office has in mind.&lt;br /&gt;
  1031. &lt;br /&gt;
  1032. Of course, diversity implies differences. But it is pretty easy to overcome them or, better, to leverage the strength of a particular implementation when it makes sense.&lt;br /&gt;
  1033. &lt;br /&gt;&lt;/div&gt;
  1034. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  1035. Skills and assets&lt;/h2&gt;
  1036. &lt;div&gt;
  1037. Because of their ubiquity, both RDBMS and Java are already well known. This is not only about development skills, but administration, management, backups, and even procurement. The processes are in place. The IT teams are trained: they have to figure day to day issues like performance, network congestion, server availability, quick backup/restore... And they know how to deal with that.&lt;br /&gt;
  1038. &lt;br /&gt;
  1039. Finally, the Java ecosystem is far more mature than the Node.js one. Yes, Node is vibrant but go to npm: how do you &quot;separate the wheat from the chaff&quot;? You can find many libraries made by individuals, staled at version 0.x and asking for a new owner. There are some open source organizations around this technology but nothing yet that matches Apache, Eclipse or even OpenNTF. You might find licenses coming with the code but this is not enough: what about the certificates of originality? What guarantees you that the code you&#39;re using has not been stolen?&lt;br /&gt;
  1040. As bigger companies are chiming in, this will certainly evolve to a more controlled world. IBM, for example, has it own internal npm server. But I bet that many smaller companies, or companies not in the IT business cannot afford doing the same.&lt;br /&gt;
  1041. &lt;br /&gt;
  1042. &lt;br /&gt;
  1043. In short, choosing the technologies that power a framework goes way beyond comparing their technical characteristics. There are other factors that can be even more important, and that&#39;s the reason why we chose Java and RDBMS. Now, on the pure technical side, both also have very competitive advantage, but I&#39;ll expand on that later!&lt;br /&gt;
  1044. &lt;br /&gt;
  1045. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  1046. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEJ4Douy46ueCb9HuxFEfwm9aFrnOpePp7Ubk3JICeMhgcTtekYxDOVaHLeybAeAqdRd3lmTKr2JDNdPtGoGXeNxO2zcxm0JUVCKjfXrcC5yUg8w1fgkyg0Gf9BgCVktEwk8BUsbSvmLs/s1600/javardbms.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;263&quot; data-original-width=&quot;331&quot; height=&quot;254&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEJ4Douy46ueCb9HuxFEfwm9aFrnOpePp7Ubk3JICeMhgcTtekYxDOVaHLeybAeAqdRd3lmTKr2JDNdPtGoGXeNxO2zcxm0JUVCKjfXrcC5yUg8w1fgkyg0Gf9BgCVktEwk8BUsbSvmLs/s320/javardbms.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  1047. &lt;br /&gt;&lt;/div&gt;
  1048. &lt;/div&gt;
  1049. </description><link>http://blog.riand.com/2017/12/ubiquity-of-java-and-relational.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEJ4Douy46ueCb9HuxFEfwm9aFrnOpePp7Ubk3JICeMhgcTtekYxDOVaHLeybAeAqdRd3lmTKr2JDNdPtGoGXeNxO2zcxm0JUVCKjfXrcC5yUg8w1fgkyg0Gf9BgCVktEwk8BUsbSvmLs/s72-c/javardbms.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-5820947084364327996</guid><pubDate>Sun, 12 Nov 2017 21:00:00 +0000</pubDate><atom:updated>2017-11-12T14:40:51.822-08:00</atom:updated><title>Develop a Domino application using any modern tools</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  1050. In my previous &lt;a href=&quot;http://blog.riand.com/2017/11/modern-notes-like-ui-using-reactjs.html&quot;&gt;post&lt;/a&gt;, I talked about a ReactJS library of components that can be used to create Notes like UI. It allows developers to create modern web and mobile UIs in a flash. Fine but, if Domino Designer is a great tool for rapidly developing applications, it is unfortunately not designed to create such modern applications, mostly because:&lt;br /&gt;
  1051. &lt;br /&gt;
  1052. &lt;ul style=&quot;text-align: left;&quot;&gt;
  1053. &lt;li&gt;It does not integrate with the modern tool chain required by these libraries, like maven, npm,&amp;nbsp; babel, webpack...&lt;/li&gt;
  1054. &lt;li&gt;It has a very limited JavaScript editor that cannot compete against Atom or MS Visual Code. Not talking about the latest standards support, like ES6.&lt;/li&gt;
  1055. &lt;li&gt;Source control and continuous build/integration are very limited&lt;/li&gt;
  1056. &lt;li&gt;Well, it is Windows only&lt;/li&gt;
  1057. &lt;/ul&gt;
  1058. &lt;br /&gt;
  1059. We&#39;ve been thinking about these problems for a while and we worked to provide a solution. Part of the problem comes from the fact that the NSF is both the development and the deployment container, with no third party development tool in the market being able deal with the NSF content. The on-disk projects were an attempt to fix that issue, but this is for source control only.&lt;br /&gt;
  1060. &lt;br /&gt;
  1061. With Darwino, we go a step further: we allow developers to create projects using the tools they love (Eclipse, IDEAJ, Ms Visual Studio Code, Atom, ...), then use any of these tools during development and deploy the final result into an NSF.&lt;br /&gt;
  1062. &lt;br /&gt;
  1063. Ok, better to first see it in action:&lt;br /&gt;
  1064. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  1065. &lt;iframe allowfullscreen=&quot;&quot; class=&quot;YOUTUBE-iframe-video&quot; data-thumbnail-src=&quot;https://i.ytimg.com/vi/F4nrmLpPI98/0.jpg&quot; frameborder=&quot;0&quot; height=&quot;266&quot; src=&quot;https://www.youtube.com/embed/F4nrmLpPI98?feature=player_embedded&quot; width=&quot;320&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
  1066. &lt;br /&gt;
  1067. &lt;br /&gt;
  1068. In this example, I used the Darwino studio to create a new ReactJS application within Eclipse and I&#39;m using MS Visual Studio Code to develop the client side JavaScript. I can use source control, maven, npm, without any limitation, as I&#39;m outside of Domino Designer. Moreover, the Darwino ReactJS library allows me to seamlessly call the Domino REST services, even though I&#39;m running the Webpack dev server. Ah, you might have noticed, I did all the development on a Mac...&lt;br /&gt;
  1069. &lt;br /&gt;
  1070. When I build the project by running maven, the relevant built files (JavaScript files, Jar files for the business logic...) are seamlessly deployed to the NSF, making the application instantly available from the Domino server. The source files are by default *not* deployed to the NSF, making it smaller and not triggering a build process when opened within Designer. Finally, your IP is also protected.&lt;br /&gt;
  1071. &lt;br /&gt;
  1072. The magic behind that is a design element server installed right into Domino Designer, as part of the Darwino extension for Darwino Designer. This server exposes some REST services on port 8800: list of projects, access to design elements, ... These services are called by a client, like the Darwino studio in Eclipse, to deploy the runtime files when desired. Because it is all based on standard REST services, other clients, like pure JavaScript ones, can be developed as well. Seems easy right? Well, the evil is as always in the implementation details, but hopefully this is all written using the Darwino runtime, which drastically simplified the development.&lt;br /&gt;
  1073. &lt;br /&gt;
  1074. The cherry on the cake: the code for these services is isolated from Domino Designer itself, so it could be installed directly within a Domino server. This would remove the requirement of using Domino Designer for development, at least when not developing&quot;classic&quot; notes applications -&amp;gt; it can enable a pure Web based Designer for citizen developers.&lt;br /&gt;
  1075. &lt;br /&gt;
  1076. Let me know what you&amp;nbsp; think about it!&lt;br /&gt;
  1077. &lt;br /&gt;
  1078. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  1079. Darwino Studio Screenshots&lt;/h3&gt;
  1080. Configuring the Design Server&lt;br /&gt;
  1081. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  1082. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsWlSHmm6fDRqFH-8gO5lZ5O_HyQwYmtIhfoUUxitUD6d5wZ59sZKIs-mmg_0WImJuJA76iiyBjPx5Ub9huNk2OHBnF9rvM0W_IvJSsRISbAbCvuLY_UQ0ZkpVdjebxfBxE1XXeMWolN0/s1600/global+prefs.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;246&quot; data-original-width=&quot;658&quot; height=&quot;236&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsWlSHmm6fDRqFH-8gO5lZ5O_HyQwYmtIhfoUUxitUD6d5wZ59sZKIs-mmg_0WImJuJA76iiyBjPx5Ub9huNk2OHBnF9rvM0W_IvJSsRISbAbCvuLY_UQ0ZkpVdjebxfBxE1XXeMWolN0/s640/global+prefs.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  1083. &lt;br /&gt;
  1084. Configuring the project files to synchronize:&lt;br /&gt;
  1085. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  1086. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwdBU8zfg5j1vKNU_7xp4CYCe4riD48TCMEf68uV7fncRF8dr-vfFoGG5F-uKtk06m6aGvxxW7n-U0X53pAlcn6DH0PQ5jmWr7RdDonVHJYN1D7ZL6bvnWID8y1uZnoOY6AZctzXPNJ7g/s1600/project+prefs.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;416&quot; data-original-width=&quot;527&quot; height=&quot;502&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwdBU8zfg5j1vKNU_7xp4CYCe4riD48TCMEf68uV7fncRF8dr-vfFoGG5F-uKtk06m6aGvxxW7n-U0X53pAlcn6DH0PQ5jmWr7RdDonVHJYN1D7ZL6bvnWID8y1uZnoOY6AZctzXPNJ7g/s640/project+prefs.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  1087. &lt;br /&gt;&lt;/div&gt;
  1088. </description><link>http://blog.riand.com/2017/11/develop-domino-applications-using-ms.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/F4nrmLpPI98/default.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-2022992917252218489</guid><pubDate>Thu, 09 Nov 2017 18:21:00 +0000</pubDate><atom:updated>2017-11-09T10:21:06.459-08:00</atom:updated><title>Modern Notes like UI using ReactJS</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  1089. The world is evolving fast, and so technologies are. Today, for now a little while, when we talk about building a new web UI or a mobile hybrid one, we think about using pure client technologies without markup generation on the server side. JSF, JSP, ASP.NET are being replaced by Angular, ReactJS, VueJS and services...&amp;nbsp; I personally think this is a great evolution. But, are these technologies easy enough to use? Can a developer be as productive as he/she is with XPages, for example? Well, the quick answer is no, at least without any addition to these core libraries, in term of components and tooling.&lt;br /&gt;
  1090. &lt;br /&gt;
  1091. This is the problem we are currently tackling with Darwino: let&#39;s make it easy to consume these new JavaScript technologies without compromising their power. For these, Darwino 2.0 is featuring a brand new ReactJS component libraries as well as some new templates (or boilerplates, or pods, ...) that make the creation of a project easy.&lt;br /&gt;
  1092. &lt;br /&gt;
  1093. The components should give you all the necessary UI constructs to create a form based application. Of course, all of this is responsive to adapt to mobile devices. In short, everything you need to build Notes/Domino like applications in minutes, but with pure standard ReactJS.&lt;br /&gt;
  1094. Curious? Have a look a running instance here&amp;nbsp;&lt;a href=&quot;http://playground.darwino.com:8080/contacts-react&quot;&gt;http://playground.darwino.com:8080/contacts-react&lt;/a&gt;&amp;nbsp;(use demo/demo to log in)&lt;br /&gt;
  1095. &lt;br /&gt;
  1096. The applications should also be data source agnostic, in a sense that the same application should,&amp;nbsp; almost seamlessly, connect the Darwino JSON store, or to Notes/Domino backend, or even other DB like LiveGrid data store. With this architecture in mind, these applications can work online, connecting to any server data, but also offline, connecting to the locally Darwino replicated data in your mobile device.&lt;br /&gt;
  1097. &lt;br /&gt;
  1098. There are different steps in this journey. Let&#39;s go through them.&lt;br /&gt;
  1099. &lt;br /&gt;
  1100. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  1101. Choosing the JS library/framework&lt;/h2&gt;
  1102. &lt;div&gt;
  1103. There is nothing wrong picking up one of the major library currently available. In a &lt;a href=&quot;http://blog.riand.com/2017/01/reactjs-or-angularjs-what-about.html&quot;&gt;previous post&lt;/a&gt;, I explained why we choose VueJS for ProjExec and how our developers quickly became productive using this library. It was great with this goal in mind.&lt;/div&gt;
  1104. &lt;div&gt;
  1105. Darwino has different goals. As a development tool, it should target the broader set of use cases. This implies using a library with a bigger eco-systems, a large set of third party additions, several rendering libraries (bootstrap, material design...). For these reasons, we choose ReactJS, as it is adopted by the many vendors like IBM or Microsoft. The later is interesting, as we can quickly create apps with the Office look &amp;amp; feel, thanks to the &lt;a href=&quot;https://developer.microsoft.com/en-us/fabric&quot;&gt;Office UI Fabric&lt;/a&gt;.&amp;nbsp;&lt;/div&gt;
  1106. &lt;div&gt;
  1107. &lt;br /&gt;&lt;/div&gt;
  1108. &lt;div&gt;
  1109. ReactJS it is.&lt;/div&gt;
  1110. &lt;div&gt;
  1111. &lt;br /&gt;&lt;/div&gt;
  1112. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  1113. Choosing the right ReactJS components&lt;/h2&gt;
  1114. ReactJS by itself is low-level: let&#39;s consider it as a powerful, core engine that can be extended through components. For example, ReactJS has *no* built-in data binding capabilities like the one you&#39;ll find in Angular or VueJS. You should either implement them yourself, or use a third party library.&lt;br /&gt;
  1115. To get a decent set of capabilities without reinventing the wheel, you have to pick-up libraries from one of the large JavaScript repositories. Nowadays, I would advise you to use npm (or eventually yarn) and leave tools like Bower to the historian.&lt;br /&gt;
  1116. Gasp, this is where the troubles come in: the npm repository features a plethora of libraries designed for the same job. How would you pickup one over the others? Number of downloads? Stars on Github? Recent activity? Return on experience from other developers? Making the right choices takes time as there is no obvious ones.&lt;br /&gt;
  1117. &lt;br /&gt;
  1118. For Darwino, we choose the following:&lt;br /&gt;
  1119. &lt;ul style=&quot;text-align: left;&quot;&gt;
  1120. &lt;li&gt;react-redux&lt;/li&gt;
  1121. &lt;li&gt;react-router&lt;/li&gt;
  1122. &lt;li&gt;redux-form&lt;/li&gt;
  1123. &lt;li&gt;react-intl&lt;/li&gt;
  1124. &lt;li&gt;react-data-grid&lt;/li&gt;
  1125. &lt;li&gt;react-bootstrap (or eqv for other UI targets)&lt;/li&gt;
  1126. &lt;li&gt;... and a few others&lt;/li&gt;
  1127. &lt;/ul&gt;
  1128. &lt;br /&gt;
  1129. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  1130. Project boilerplates&lt;/h2&gt;
  1131. If, for you, building an app if about saving a design element in an NSF, then your life is good. The JavaScript world is way more complex! Even if JavaScript is an interpreted language, you&#39;ll quickly feel the need of running build tools to use the latest JS specifications, consume libraries, produce an optimized/compressed version of the code... Again, you &#39;ll have to make some choices between Grunt, Gulp, Webpack, Browserify, ... and many other tools. This is another time consuming research.&lt;br /&gt;
  1132. Once your choice is made, you have to configure the tools, generally using a JavaScript based DSL (e.g. a JavaScript piece of code driving the tool). This is fairly complex as the DSLs feature many options. To get started, developers created templates, also called boilerplates. They contains the configuration files with default behaviors that you then have to customize. But again you have many available, with some clearly developed by people without a deep knowledge of the technology. Furthermore, they are generally tailored for a particular version of the tool and will not function properly with yours...&lt;br /&gt;
  1133. Ok, we spent many days of try/fail/redo and we are coming with our own template. Well, it is even easier: go to the Darwino studio, create a new project, select the target technology and voila, you&#39;re ready to go. Pfuuuu...&lt;br /&gt;
  1134. &lt;br /&gt;
  1135. &lt;h2&gt;
  1136. Component library&lt;/h2&gt;
  1137. As we&#39;d like to make the developer experience as smooth as possible, we are providing a series of ready to use components covering most of the developer needs. What started as a demo library became a very useful set of components, covering:&lt;br /&gt;
  1138. &lt;br /&gt;
  1139. &lt;ul style=&quot;text-align: left;&quot;&gt;
  1140. &lt;li&gt;Forms with document access (load, save, validation)&lt;/li&gt;
  1141. &lt;li&gt;Data binding controls, including read-only mode, multiple values, pickers, rich text, attachments upload &amp;amp; download&lt;/li&gt;
  1142. &lt;li&gt;Computed fields (yes, not that obvious with ReactJS!)&lt;/li&gt;
  1143. &lt;li&gt;Data grid with Notes view like behaviors: categorization, totals, response doc&lt;/li&gt;
  1144. &lt;li&gt;Navigators, action bars, sub-forms&lt;/li&gt;
  1145. &lt;li&gt;Dialogs, messages&lt;/li&gt;
  1146. &lt;li&gt;Easy access to server side business logic using micro services&lt;/li&gt;
  1147. &lt;/ul&gt;
  1148. &lt;br /&gt;
  1149. And more... But again, these components are just regular ReactJS components, that any ReactJS developer would consume without fear. They do not carry any idiosyncrasies from the original Notes ones. They exist to create Notes like UI for Web and Mobile, not to replicate 1-1 what the Notes UI does.&lt;br /&gt;
  1150. &lt;br /&gt;
  1151. Ah, I foresee a question: can you automatically convert an existing Notes UI to this technology? The answer is... yes! Feel interested?&lt;br /&gt;
  1152. &lt;br /&gt;&lt;/div&gt;
  1153. </description><link>http://blog.riand.com/2017/11/modern-notes-like-ui-using-reactjs.html</link><author>noreply@blogger.com (Philippe Riand)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-2964948901338965155</guid><pubDate>Wed, 18 Oct 2017 19:21:00 +0000</pubDate><atom:updated>2017-10-18T12:21:18.081-07:00</atom:updated><title>Universal Data Replication</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  1154. One of the Darwino piece of code that I&#39;m the most proud of is the replication engine. If a majority of our customers see it as a Domino to JSON replication engine, it goes far beyond that. In reality, it can replicate between virtually any data sources.&lt;br /&gt;
  1155. It is a true, two ways, multi-point replication engine, borrowing some ideas from IBM Domino but going beyond its venerable ancestor in multiple places.&lt;br /&gt;
  1156. &lt;br /&gt;
  1157. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  1158. The architecture&lt;/h2&gt;
  1159. &lt;div&gt;
  1160. The main idea is that any data set can be represented as a set decorated JSON objects. Decorations include meta-data (ex: data security, replication attributes...) and binary attachments. The latest are stored apart from the JSON itself for performance reasons.&lt;/div&gt;
  1161. &lt;div&gt;
  1162. As an example, a Domino document can be transformed to a JSON object where every Domino document item is converted to a JSON attribute in the object, along with the attachments.&lt;/div&gt;
  1163. &lt;div&gt;
  1164. A relational table can also be transformed to a set of JSON objects or arrays, one per row. Even more interesting, a set of rows coming from multiple tables can be grouped to create a consistent, atomic&amp;nbsp; JSON object. This way, the replication granularity is not limited to a row, but can be a whole business object even if the physical storage split it into multiple records&lt;/div&gt;
  1165. &lt;div&gt;
  1166. &lt;br /&gt;&lt;/div&gt;
  1167. &lt;div&gt;
  1168. Dawino&#39;s engine is mostly made of two components:&lt;br /&gt;
  1169. &lt;br /&gt;
  1170. &lt;ol style=&quot;text-align: left;&quot;&gt;
  1171. &lt;li&gt;A universal replication engine&lt;br /&gt;This engine does *not* know about the data sources. It contains all the replication logic while it accesses the data through a generic connector API&lt;/li&gt;
  1172. &lt;li&gt;Connectors&lt;br /&gt;A connector encapsulates the physical access to the data, via an API. Its API provides a &quot;JSON&quot; vision of the data that we&#39;ll be consumed by the engine&lt;/li&gt;
  1173. &lt;/ol&gt;
  1174. As of today, the Darwino replication engine comes with ready to use connector implementations:&lt;br /&gt;
  1175. &lt;br /&gt;
  1176. &lt;ul style=&quot;text-align: left;&quot;&gt;
  1177. &lt;li&gt;Darwino JSON store, running atop RDBMS&lt;/li&gt;
  1178. &lt;li&gt;IBM Domino&lt;/li&gt;
  1179. &lt;li&gt;FlowBuilder XML store, used by ProjExec&lt;/li&gt;
  1180. &lt;li&gt;HTTP to wrap a local connector and use HTTP/HTTPS as the transport&lt;/li&gt;
  1181. &lt;/ul&gt;
  1182. Some other connectors have been started:&lt;br /&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;
  1183. &lt;li&gt;Pure RDBMS used, for example, to replicate to DashDB&lt;/li&gt;
  1184. &lt;li&gt;MongoDB&lt;/li&gt;
  1185. &lt;li&gt;MS Sharepoint List&lt;/li&gt;
  1186. &lt;/ul&gt;
  1187. &lt;br /&gt;
  1188. We are also waiting for a first beta release of IBM LiveGrid to replicate these data to your mobile devices and get a true offline expereince.&lt;br /&gt;
  1189. &lt;br /&gt;
  1190. Writing a basic connector is a pretty easy operation. Then, the connector can be enhanced incrementally to provide fine tuned capabilities (better conflict detection....)&lt;br /&gt;
  1191. &lt;br /&gt;
  1192. &lt;h2 style=&quot;text-align: left;&quot;&gt;
  1193. Darwino replication strengths&lt;/h2&gt;
  1194. &lt;br /&gt;
  1195. If the concept of replication feels easy to understand, there are many details that make Darwino unique so far:&lt;br /&gt;
  1196. &lt;br /&gt;
  1197. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  1198. Data transformation&lt;/h3&gt;
  1199. &lt;/div&gt;
  1200. &lt;div&gt;
  1201. The data can be transformed two ways during the replication process. These transformations can happen at the basic field level (ex: transforming a Domino name to its LDAP counterpart) or at a more global level, like grouping a set of Domino multiple fields into a JSON array. This way, the replicated JSON is clean, and it does not carry any legacy tricks from the source platform.&lt;/div&gt;
  1202. &lt;div&gt;
  1203. The transformation can obviously be coded, in Java or Groovy, but Darwino already comes with a pretty large set of standard transformations you can apply to any data. Thus, setting up the transformation is mostly about scripting the pre-defined transformers, unless you have more advanced need.&lt;/div&gt;
  1204. &lt;div&gt;
  1205. One of the benefit of data transformation is data normalization: you can ensure that the fields in a JSON document are converted to the expected data type. Domino is well know have inconsistent fields across the whole database, as they evolved over time. Darwino can normalized them.&lt;/div&gt;
  1206. &lt;div&gt;
  1207. &lt;br /&gt;&lt;/div&gt;
  1208. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  1209. Selective replication&lt;/h3&gt;
  1210. &lt;div&gt;
  1211. Sometimes you want to limit the data replicated to a target system. It can be because of storage requirements (ex: your mobile device can only handle a subset of the data), or security (ex: you only want some data to be available on the cloud).&lt;/div&gt;
  1212. &lt;div&gt;
  1213. Darwino handles the selective replication by either providing selection formulas based on data. It also provides a unique feature called &quot;Business Replication&quot; where related documents can be grouped and replicated as a whole.&lt;/div&gt;
  1214. &lt;div&gt;
  1215. Selective replication poses a problem: how to propagate deletions as the data used to select the documents no longer exists, or how to remove a document from the target when the formula no longer selects that document? Well, we solved this problem with an efficient and reliable method that I&#39;ll be happy to talk about around a beer :-) This is a pretty complex problem.&lt;/div&gt;
  1216. &lt;div&gt;
  1217. &lt;br /&gt;&lt;/div&gt;
  1218. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  1219. Transaction and error recovery&lt;/h3&gt;
  1220. &lt;div&gt;
  1221. If the target database supports transactions, then the engine can take advantage of them. If the replication fails for any reason, then it can rollback to the initial state, and eventually attempts another time. But this is not enough: when you have a large data set to replicate, you might not want to restart from scratch while you already replicated 98% of the data. For that purpose, the engine features commit points. If it fails at any time, it only restarts from the latest successful commit point.&lt;/div&gt;
  1222. &lt;div&gt;
  1223. &lt;br /&gt;&lt;/div&gt;
  1224. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  1225. Conflict detection&lt;/h3&gt;
  1226. &lt;div&gt;
  1227. This is one of the area were Darwino excels. It has multiple conflict detection systems, going way beyond Domino.&amp;nbsp;A particular connector might only implement a subset of them, depending on the data source capabilities. In this happens, then the engine will degrade gracefully and deal with what is available.&lt;/div&gt;
  1228. &lt;div&gt;
  1229. The existing mechanisms include: the last modification date, a sequence id (similar to Domino or some RDBMS), a change string carrying the update history, an update id...&lt;/div&gt;
  1230. &lt;div&gt;
  1231. Once a conflict is detected, an API is called and the developer can choose to execute a predefined behavior (ignore, last wins, create a copy data set...) or do a custom resolution like a data merge.&lt;/div&gt;
  1232. &lt;div&gt;
  1233. Finally, Darwino has an interesting mechanism to deal with records deletion. If the source database has deletion stubs, it will effectively use them. Else, it provides some helpers to find the deleted records, in particular within relational tables.&lt;/div&gt;
  1234. &lt;div&gt;
  1235. &lt;br /&gt;&lt;/div&gt;
  1236. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  1237. Performance&lt;/h3&gt;
  1238. &lt;div&gt;
  1239. The whole Darwino replication engine is designed to handle very large datasets. The Domino connector is mostly written in &quot;C&quot;, thus calling the native &quot;C&quot; API. Not only this allows critical features that are not available with the regular back-end classes, but it also provide high performance data replication. On my laptop, it replicates ~400 complex documents/second, including attachments and rich text conversion. For simpler documents, with only basic data, it goes to the range of 1000-2000 a second! In many cases, you can get your existing NSF replicated in minutes.&lt;/div&gt;
  1240. &lt;div&gt;
  1241. &lt;br /&gt;&lt;/div&gt;
  1242. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  1243. Domino data accuracy&lt;/h3&gt;
  1244. &lt;div&gt;
  1245. A replication engine only makes sense if it is high fidelity. From a Domino perspective, the Darwino replication should act like a regular Domino replication, maintaining the same data (sequence id, creation and modification dates, list of authors...). We can do that thanks to the &quot;C&quot; API.&lt;/div&gt;
  1246. &lt;div&gt;
  1247. Darwino can replicate most of the Domino field types: string, number, name, rich text, mime, date/time... with maximum precision. For example, it deals particularly well with the date/time and the time zones.&lt;/div&gt;
  1248. &lt;div&gt;
  1249. Any domino document can be replicated, including design elements or profile documents. If you choose to replicate the database ACL document, then it will then be used by the Darwino runtime to apply the same security. Talking about security, we also handle the reader/author fields.&lt;/div&gt;
  1250. &lt;div&gt;
  1251. &lt;br /&gt;&lt;/div&gt;
  1252. &lt;h3 style=&quot;text-align: left;&quot;&gt;
  1253. Aggregation&lt;/h3&gt;
  1254. &lt;div&gt;
  1255. Data can be aggregated during replication: multiple sources of data can be replicated in one single target database, thus allowing a more global view of the whole data set. Think about it: you can replicate many NSF into a single set of relational tables and then run global queries on top of these tables. Isn&#39;t that a dream?&amp;nbsp;&lt;b&gt;Darwino liberates you Domino data!&lt;/b&gt;&lt;/div&gt;
  1256. &lt;div&gt;
  1257. &lt;br /&gt;&lt;/div&gt;
  1258. &lt;div&gt;
  1259. &lt;br /&gt;&lt;/div&gt;
  1260. &lt;div&gt;
  1261. As you see, the Darwino replication engine is a very complete engine that is flexible to handle many data sources. Out of the box, it can replicate your databases as is. But it also give you a lot of options to select and transform your data.&lt;/div&gt;
  1262. &lt;/div&gt;
  1263. </description><link>http://blog.riand.com/2017/10/universal-data-replication.html</link><author>noreply@blogger.com (Philippe Riand)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-4229824040749920326</guid><pubDate>Thu, 15 Jun 2017 23:03:00 +0000</pubDate><atom:updated>2017-06-15T16:03:47.878-07:00</atom:updated><title>The power of Domain Specific Languages</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  1264. We are all used to configuration files, whenever they are XML, JSON or simply text based. If this is ok for simple configurations, but it falls short when the complexity increases. The biggest issue is that they cannot provide more than what they are designed for, basically static values.&lt;br /&gt;
  1265. &lt;br /&gt;
  1266. Let&#39;s suppose, for example, that we have an XML configuration file exposing a property like:&lt;br /&gt;
  1267. &lt;div style=&quot;text-align: left;&quot;&gt;
  1268. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;visible&amp;gt;true&amp;lt;/visible&lt;/span&gt;&amp;gt;&lt;/div&gt;
  1269. Ok, simple enough. This property can be &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;true &lt;/span&gt;or &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;false&lt;/span&gt;. But now what if I want a more dynamic value, like &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;true &lt;/span&gt;if I&#39;m in development mode, or &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;false &lt;/span&gt;if I&#39;m in production? I&#39;m stuck!&lt;br /&gt;
  1270. &lt;br /&gt;
  1271. Some configuration systems allows expressions to be evaluated within the configuration text, like:&lt;br /&gt;
  1272. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;visible&amp;gt;${runtime.development}&amp;lt;/visible&lt;/span&gt;&amp;gt;&lt;br /&gt;
  1273. &lt;div&gt;
  1274. But this is very specific to every configuration system, when it exists. Moreover, this is limited to value replacement. What if I want to define a set of properties based on a condition? Well, some rare systems provide an advanced syntax, like&lt;/div&gt;
  1275. &lt;div&gt;
  1276. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;sys:if cond=&#39;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;runtime.development&#39;&amp;gt;&lt;/span&gt;&lt;br /&gt;
  1277. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;lt;prop1&amp;gt;val1&amp;lt;/prop1&amp;gt;&lt;/span&gt;&lt;br /&gt;
  1278. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;lt;prop2&amp;gt;val2&amp;lt;/prop2&amp;gt;&lt;/span&gt;&lt;br /&gt;
  1279. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;lt;/&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;sys:if&amp;gt;&lt;/span&gt;&lt;/div&gt;
  1280. &lt;div&gt;
  1281. Looks like the venerable XSL :-) This is about inventing a new ugly programming language that nobody knows about. And it is still have limitations, basically what the programmer provided.&amp;nbsp;&lt;/div&gt;
  1282. &lt;div&gt;
  1283. &lt;br /&gt;&lt;/div&gt;
  1284. &lt;div&gt;
  1285. The use of the programming constructs I know would be more appealing to me. What about:&lt;/div&gt;
  1286. &lt;div&gt;
  1287. &lt;div&gt;
  1288. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; if(&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;runtime.development) {&lt;/span&gt;&lt;br /&gt;
  1289. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;lt;prop1&amp;gt;val1&amp;lt;/prop1&amp;gt;&lt;/span&gt;&lt;br /&gt;
  1290. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;lt;prop2&amp;gt;val2&amp;lt;/prop2&amp;gt;&lt;/span&gt;&lt;br /&gt;
  1291. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
  1292. &lt;/div&gt;
  1293. &lt;div&gt;
  1294. or even better:&lt;/div&gt;
  1295. &lt;div&gt;
  1296. &lt;div&gt;
  1297. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; if(&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;runtime.development) {&lt;/span&gt;&lt;br /&gt;
  1298. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; prop1 val1&lt;/span&gt;&lt;br /&gt;
  1299. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; prop2 val2&lt;/span&gt;&lt;br /&gt;
  1300. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
  1301. &lt;/div&gt;
  1302. &lt;div&gt;
  1303. This is mixing and matching configuration statements with regular programming ones. This allows a total flexibility on what is possible! We just need a programming language that supports this kind of construction, including the use of a dedicated grammar to provide&amp;nbsp;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;prop1 &lt;/span&gt;and &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;prop2&lt;/span&gt;. The programming language, along &amp;nbsp;with this specific, dedicated grammar, is what is called a Domain Specific Language, aka DSL.&amp;nbsp;&lt;/div&gt;
  1304. &lt;div&gt;
  1305. &lt;br /&gt;&lt;/div&gt;
  1306. &lt;div&gt;
  1307. In the Java world, &lt;a href=&quot;http://groovy-lang.org/&quot;&gt;Groovy &lt;/a&gt;is currently the clear leader. It is being used in many systems, with even &lt;a href=&quot;https://www.packtpub.com/application-development/groovy-domain-specific-languages-second-edition&quot;&gt;books &lt;/a&gt;written on this very topic. Its parenthesis less syntax when calling a method, the use of closures (similar to java 8 lambdas) make it particularly suited for the job.&lt;/div&gt;
  1308. &lt;div&gt;
  1309. &lt;br /&gt;&lt;/div&gt;
  1310. &lt;div&gt;
  1311. They are several places where we are using a Groovy based DSL in &lt;a href=&quot;https://www.darwino.com/&quot;&gt;Darwino&lt;/a&gt;. This is what allows, for example, a very flexible data replication configuration between IBM Domino and the Darwino JSON store. Not only we are providing many predefined data transformations but we let the developer insert custom code almost everywhere.&lt;/div&gt;
  1312. &lt;div&gt;
  1313. &lt;br /&gt;&lt;/div&gt;
  1314. &lt;div&gt;
  1315. Recently, we introduced another DSL in our new micro application architecture. In particular, we added triggers and actions, for example to send a notification to mobile devices when a new document has been inserted in a particular database. Or any other action, on any other trigger...&lt;/div&gt;
  1316. &lt;div&gt;
  1317. &lt;br /&gt;&lt;/div&gt;
  1318. &lt;div&gt;
  1319. Here is a mock example bellow. If it seems trivial, it shows that some code can be inserted to lookup a user in the LDAP directory to find its mobile id, or calculate the URL of the page to display. This would have been hard to define these custom processing with a simple static configuration file.&lt;/div&gt;
  1320. &lt;div&gt;
  1321. &lt;br /&gt;&lt;/div&gt;
  1322. &lt;div&gt;
  1323. &lt;div&gt;
  1324. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;DatabaseTrigger {&lt;/span&gt;&lt;/div&gt;
  1325. &lt;div&gt;
  1326. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; database &quot;MyDatabase&quot;&lt;/span&gt;&lt;/div&gt;
  1327. &lt;div&gt;
  1328. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; view &quot;Workflows&quot;&lt;/span&gt;&lt;/div&gt;
  1329. &lt;div&gt;
  1330. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; handler { doc -&amp;gt;&lt;/span&gt;&lt;/div&gt;
  1331. &lt;div&gt;
  1332. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; def approver = LookupUser doc[&quot;Approver&quot;]&lt;/span&gt;&lt;/div&gt;
  1333. &lt;div&gt;
  1334. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; def url = calculateDisplayUrl(doc)&lt;/span&gt;&lt;/div&gt;
  1335. &lt;div&gt;
  1336. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; pushNotification {&lt;/span&gt;&lt;/div&gt;
  1337. &lt;div&gt;
  1338. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;ids approver.mobileid&lt;/span&gt;&lt;/div&gt;
  1339. &lt;div&gt;
  1340. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;page url&lt;/span&gt;&lt;/div&gt;
  1341. &lt;div&gt;
  1342. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;text &quot;Purchase Approval Pending&quot;&lt;/span&gt;&lt;/div&gt;
  1343. &lt;div&gt;
  1344. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
  1345. &lt;div&gt;
  1346. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;
  1347. &lt;div&gt;
  1348. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
  1349. &lt;/div&gt;
  1350. &lt;div&gt;
  1351. &lt;br /&gt;&lt;/div&gt;
  1352. &lt;div&gt;
  1353. Neat, concise and simple, isn&#39;t it?&amp;nbsp;&lt;/div&gt;
  1354. &lt;div&gt;
  1355. In this example, &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;pushNotification&lt;/span&gt; is a predefined action but other actions types &amp;nbsp;are available (&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;sendEmail&lt;/span&gt;,&amp;nbsp;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;watsonWorkNotification&lt;/span&gt;...). Moreover, if Darwino does not provide a trigger or a notification you need, then it is very easy for a developer to inject a custom one into the DSL. Just develop it in Groovy or Java and register it. A DSL is very dynamic and extensible.&lt;/div&gt;
  1356. &lt;div&gt;
  1357. &lt;br /&gt;&lt;/div&gt;
  1358. &lt;/div&gt;
  1359. </description><link>http://blog.riand.com/2017/06/the-power-of-domain-specific-languages-1.html</link><author>noreply@blogger.com (Philippe Riand)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-7185254714052651077</guid><pubDate>Sat, 11 Mar 2017 20:31:00 +0000</pubDate><atom:updated>2017-03-12T09:19:02.717-07:00</atom:updated><title>Darwino as the IBM Domino reporting Graal(*) </title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  1360. Reports, dashboards, data analytics... have been the&amp;nbsp;conundrum of IBM Notes/Domino since the beginning. Its proprietary data structure, the absence of standard APIs and its deficient query capability make it very difficult. This has been ranked as one of the top need for any for business applications. I know several business partners who created great Domino solutions but struggling with poor reporting capabilities.&lt;br /&gt;
  1361. &lt;br /&gt;
  1362. Of course some attempts were made to fix it: &lt;a href=&quot;http://www-03.ibm.com/software/products/en/ibmenterpriseintegrator&quot;&gt;LEI&lt;/a&gt;, &lt;a href=&quot;http://www-01.ibm.com/support/docview.wss?uid=swg21384421&quot;&gt;DB2NSF&lt;/a&gt;,.. all incomplete and anyway defunct. I personally tried a new approach with &lt;a href=&quot;https://github.com/OpenNTF/DomSQL&quot;&gt;DomSQL&lt;/a&gt;, which, to be frank, I was proud of :-) But this was a first shot, more a POC than a product, although I know several customers using it in production. Unfortunately, despite the community interest, IBM didn&#39;t want to move it forward, so it is barely in the state where I left it. On the positive side, it gave me a great low level SQLite experience which is now leveraged in Darwino&#39;s mobile database! Learn, learn, learn, you&#39;ll always reuse it.&lt;br /&gt;
  1363. &lt;br /&gt;
  1364. So we are back 2 decades ago, with no solution to this common business problem. Well, you anticipated, Darwino can help! It is actually the first and immediate benefit than our customers are getting when they start with Darwino.&lt;br /&gt;
  1365. &lt;br /&gt;
  1366. One of the Darwino capability is data replication. It can replicate, two ways and real real time, any Domino data into its own document store. From a Domino standpoint, Darwino is behaving as another Domino server, with high fidelity replication. But Darwino document store is a JSON based store, sitting on top of an RDBMS. As modern RDBMS now understand JSON natively, this is a great fit. The whole replicated data set is then query-able by any tool that understands RDBMS. With no limitation but the relational database itself. Yes, you can join, aggregate, union, calculate, select, sub select... And, contrary to LEI, this is a real incremental replication that can be triggered when a data event occur on the domino server, keeping your data up-to-date at any time. No more nightly/weekly batch data copy, if you know what I mean.&lt;br /&gt;
  1367. &lt;br /&gt;
  1368. From a technical standpoint, Darwino stores a JSON version of the domino document within a column in a relational table. It is very flexible as it does not require a custom table schema per fdata set, and there is no data loss (e.g. a field you forgot to define a column for...). Moreover, if the default field conversion between the two database is 1-1, this is actually fully customizable. The Darwino replication process can transform the data during the replication phase. This is achieved using a Groovy based &lt;a href=&quot;https://www.slideshare.net/darwinodb/11-advanced-replication?qid=85c36ad3-d2cf-4582-a948-e2a2654eb063&amp;amp;v=&amp;amp;b=&amp;amp;from_search=18&quot;&gt;DSL&lt;/a&gt;, which allows the following:&lt;br /&gt;
  1369. &lt;br /&gt;
  1370. &lt;ul style=&quot;text-align: left;&quot;&gt;
  1371. &lt;li&gt;The fields type can be normalized (e.g. make sure that the field in the JSON document is of the expected data type). Common issue with Notes inconsistent data type across documents, right?&lt;/li&gt;
  1372. &lt;li&gt;Domino patterns/workaround can also be normalized. For example, a set a multi value fields describing a table can be transform a true JSON array&lt;/li&gt;
  1373. &lt;li&gt;Computation can be done ahead of time to make the reporting easier (queries, calculated columns...)&lt;/li&gt;
  1374. &lt;li&gt;... well, your imagination&lt;/li&gt;
  1375. &lt;/ul&gt;
  1376. If you feel that directly reporting on the JSON column within the RDBMS is uneasy, then you can create RDBMS views (standard or materialized), to then act on a table like structure. This is what our partner, CMS, chose to do. Going even further, they are creating these relational views out of the Domino view design elements. Introduction to their product is available&amp;nbsp;&lt;a href=&quot;https://www.slideshare.net/darwinodb/16-endeavour-reporter?qid=85c36ad3-d2cf-4582-a948-e2a2654eb063&amp;amp;v=&amp;amp;b=&amp;amp;from_search=20&quot;&gt;here&lt;/a&gt;. This is a great use of Darwino.&lt;br /&gt;
  1377. &lt;div&gt;
  1378. &lt;br /&gt;&lt;/div&gt;
  1379. &lt;div&gt;
  1380. In a previous post, I spoke about &lt;a href=&quot;http://blog.riand.com/2017/01/when-sql-meets-nosql-you-get-best-of.html&quot;&gt;JSQL&lt;/a&gt;, which is a easy way for querying JSON data using SQL. Thinking forward, we can provide a JDBC driver to expose JSQL and allow any JDBC compliant client to access the data. From a technical standpoint, we know how to do it: the above mentioned DomSQL features one we can adapt and reuse. This is not in our current plan, but I&#39;ll be interested to know if you have any interest in this.&lt;/div&gt;
  1381. &lt;div&gt;
  1382. &lt;br /&gt;&lt;/div&gt;
  1383. &lt;div&gt;
  1384. As a summary, using Darwino as a Domino report tool enabler is a fast way to fulfill some crucial, missing Domino capability. It almost instantly makes the existing Domino applications more interesting to the business users. Of course, this is just a subset of what Darwino can bring to the table, but this is a quick, high value one. Please, give it a shot or contact us so we can help you.&lt;br /&gt;
  1385. &lt;br /&gt;
  1386. BTW, &quot;Graal&quot; is the French spelling for Holy Grail&lt;/div&gt;
  1387. &lt;div&gt;
  1388. &lt;br /&gt;&lt;/div&gt;
  1389. &lt;div&gt;
  1390. &lt;br /&gt;&lt;/div&gt;
  1391. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  1392. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjID0RmNkaYDJVaH-p3Kmf-YntqV6Q4LYIpZq7ZQzgpWgJGlSGADaoRfK1P5mFn0qMrizqEAOf7AoWuFYK6jbVAGsHfNrVALCWbF2f_6O_RmziRE-Djgf73Kh-iyigw8vD4exUuc-mVxYM/s1600/3d-graph-clip-art-at-clker-com-vector-clip-art-online-royalty-free-hc41nE-clipart.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjID0RmNkaYDJVaH-p3Kmf-YntqV6Q4LYIpZq7ZQzgpWgJGlSGADaoRfK1P5mFn0qMrizqEAOf7AoWuFYK6jbVAGsHfNrVALCWbF2f_6O_RmziRE-Djgf73Kh-iyigw8vD4exUuc-mVxYM/s320/3d-graph-clip-art-at-clker-com-vector-clip-art-online-royalty-free-hc41nE-clipart.png&quot; width=&quot;282&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  1393. &lt;div&gt;
  1394. &lt;br /&gt;&lt;/div&gt;
  1395. &lt;div&gt;
  1396. &lt;br /&gt;&lt;/div&gt;
  1397. &lt;/div&gt;
  1398. </description><link>http://blog.riand.com/2017/03/darwino-as-ibm-domino-reporting-graal.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjID0RmNkaYDJVaH-p3Kmf-YntqV6Q4LYIpZq7ZQzgpWgJGlSGADaoRfK1P5mFn0qMrizqEAOf7AoWuFYK6jbVAGsHfNrVALCWbF2f_6O_RmziRE-Djgf73Kh-iyigw8vD4exUuc-mVxYM/s72-c/3d-graph-clip-art-at-clker-com-vector-clip-art-online-royalty-free-hc41nE-clipart.png" height="72" width="72"/><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-6786064980338971725</guid><pubDate>Fri, 03 Mar 2017 22:56:00 +0000</pubDate><atom:updated>2017-03-03T14:56:38.966-08:00</atom:updated><title>Schemaless GraphQL</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  1399. FaceBook officially introduced a few months ago a new technology called&amp;nbsp;&lt;a href=&quot;http://graphql.org/&quot;&gt;GraphQL&lt;/a&gt;. Well, rather than really being new, FaceBook made public and open source their internal graph query engine. &lt;span lang=&quot;EN&quot;&gt;It starts to be widely used for exposing APIs. For example, IBM Watson Worskpace makes use
  1400. of it. I also heard that IBM Connections will also use it.&lt;/span&gt;&lt;br /&gt;
  1401. &lt;span lang=&quot;EN&quot;&gt;&lt;br /&gt;&lt;/span&gt;
  1402. &lt;span lang=&quot;EN&quot;&gt;In a nutshell, it allows powerful, tailored queries including navigation between the data sources, in a single query. As a result, it minimizes the
  1403. number of requests sent by the client to the server, which is a performance
  1404. boost. Moreover, it proposes a single query format regardless of the data
  1405. source&lt;/span&gt;. Basically, it means that you can query data from an RDBMS or an LDAP directory almost identically, while getting a consistent response format. All good!&lt;br /&gt;
  1406. &lt;div&gt;
  1407. &lt;div class=&quot;MsoNormal&quot;&gt;
  1408. &lt;span lang=&quot;EN&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
  1409. &lt;div class=&quot;MsoNormal&quot;&gt;
  1410. &lt;span lang=&quot;EN&quot;&gt;To make the queries discoverable, it uses&amp;nbsp;a schema to define the query parameters, the JSON result as well as the
  1411. graph traversal links.&amp;nbsp; If this is&amp;nbsp;appealing, it forces the server to implement
  1412. these schemas before the queries can be emitted by the client. The whole data
  1413. and navigation models have to be known and implemented up front. Hum, not only
  1414. this slows down application prototyping, but it makes it hard to link
  1415. micro-services that don&#39;t know about each other. &amp;nbsp;&lt;/span&gt;It also does not work with truly dynamic services, like a JSON document store, where the document
  1416. schema can be unknown.&amp;nbsp; Crap, this is exactly what Darwino
  1417. offers. So the question is: can we still use GraphQL for dynamic services, where data&amp;nbsp; schema can be
  1418. static, dynamic or a mix of both? I saw a few posts on the subject but no solution so far.&lt;br /&gt;
  1419. &lt;span lang=&quot;EN&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
  1420. &lt;div class=&quot;MsoNormal&quot;&gt;
  1421. &lt;span lang=&quot;EN&quot;&gt;Fortunately, GraphQL has the notion of an optional field alias. An alias renames a
  1422. field, so it can appear multiple times in the same root. As&amp;nbsp;a field can also have parameters, we can
  1423. &quot;tweak&quot; the engine to implement dynamic field accessors. How? Simply
  1424. by inverting the roles: the alias becomes the field name while the field
  1425. is a dynamic, typed, JSON path evaluation on the source document. &lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;br /&gt;
  1426. &lt;span lang=&quot;EN&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
  1427. &lt;div class=&quot;MsoNormal&quot;&gt;
  1428. &lt;span lang=&quot;EN&quot;&gt;Ok, let me give you an example. Suppose that we have the following document
  1429. as a source:&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
  1430. &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;{&lt;br /&gt;&amp;nbsp; name: &quot;MyProject&quot;,&lt;br /&gt;&amp;nbsp; budget: {&lt;br /&gt;&amp;nbsp; &amp;nbsp; amount: 10000,&lt;br /&gt;&amp;nbsp; &amp;nbsp; currency: &quot;USD&quot;&lt;br /&gt;&amp;nbsp; }&lt;br /&gt; }&lt;/span&gt;&lt;div class=&quot;MsoNormal&quot;&gt;
  1431. &lt;span lang=&quot;EN&quot;&gt;The dynamic query to extract the project name and the budget will be like:&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
  1432. &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;{&lt;br /&gt;&amp;nbsp; doc: Document(unid:&quot;projectid&quot;, database:&quot;project&quot;) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; _unid,&lt;br /&gt;&amp;nbsp; &amp;nbsp; name: string(path:&quot;$.name&quot;),&lt;br /&gt;&amp;nbsp; &amp;nbsp; budget: number(path:&quot;$.budget.amount&quot;)&lt;br /&gt;&amp;nbsp; }&lt;br /&gt; }&lt;/span&gt;&lt;div class=&quot;MsoNormal&quot;&gt;
  1433. &lt;span lang=&quot;EN&quot;&gt;&#39;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;string&lt;/span&gt;&#39; and &#39;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;number&lt;/span&gt;&#39; (as well as others, like &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;boolean&lt;/span&gt;...) are typed, pseudo field
  1434. names that are actually evaluating JSON path queries on the source document.
  1435. And because the path is passed as an argument, it can be fully dynamic! We are
  1436. using JSON path here, but any interpreted language could be used, including JavaScript or others. Also note
  1437. that the &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;Document&lt;/span&gt; accessor is also implemented as a field with parameters. This
  1438. triggers the actual load of the JSON document from the database.&lt;/span&gt;&lt;/div&gt;
  1439. &lt;div class=&quot;MsoNormal&quot;&gt;
  1440. &lt;span lang=&quot;EN&quot;&gt;How does that sound? You can find a real example dealing with the JSON
  1441. store in the Darwino playground:&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
  1442. &lt;div class=&quot;MsoNormal&quot;&gt;
  1443. &lt;span lang=&quot;EN&quot;&gt;&lt;a href=&quot;http://playground.darwino.com/playground.nsf/GraphqlSnippets.xsp#snippet=Json_Store_Document_Read_Document_Fields&quot;&gt;&lt;span style=&quot;color: blue;&quot;&gt;http://playground.darwino.com/playground.nsf/GraphqlSnippets.xsp#snippet=Json_Store_Document_Read_Document_Fields&lt;/span&gt;&lt;/a&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
  1444. &lt;div class=&quot;MsoNormal&quot;&gt;
  1445. &lt;span lang=&quot;EN&quot;&gt;&lt;br /&gt;&lt;/span&gt;
  1446. &lt;span lang=&quot;EN&quot;&gt;But this is not enough, as we also need to implement the links to navigate
  1447. the graph. For example, we need to navigate to a JSON document by id, that id
  1448. being a field in the current object. To make this possible, we introduce here the notion of computed parameter, using the &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;${....}&lt;/span&gt; syntax (remember JSF?).&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
  1449. &lt;div class=&quot;MsoNormal&quot;&gt;
  1450. &lt;span lang=&quot;EN&quot;&gt;The pseudo query bellow assumes that you have tasks documents that can be
  1451. accessed using the parent project id:&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
  1452. &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;{&lt;br /&gt;&amp;nbsp; doc: Document(unid:&quot;projectid&quot;, database:&quot;project&quot;) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; _unid,&lt;br /&gt;&amp;nbsp; &amp;nbsp; name: string(path:&quot;$.name&quot;),&lt;br /&gt;&amp;nbsp; &amp;nbsp; budget: number(path:&quot;$.budget.amount&quot;)&lt;br /&gt;&amp;nbsp; &amp;nbsp; tasks: Documents(parent:&quot;${_unid}&quot;, database:&quot;tasks&quot;) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;taskname: string(path:&quot;$.name&quot;),&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; }&lt;br /&gt; }&lt;/span&gt;&lt;div class=&quot;MsoNormal&quot;&gt;
  1453. &lt;span lang=&quot;EN&quot;&gt;The drawback of this solution is that every property becomes a string, to accept a formula. Another solution would duplicate all the fields and have, for example, &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;parent$&lt;/span&gt;&amp;nbsp;as a computed version of &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;parent&lt;/span&gt;. As the whole library is under development, I&#39;m not sure yet what&#39;s best, so any feedback is welcome!&lt;/span&gt;&lt;br /&gt;
  1454. &lt;span lang=&quot;EN&quot;&gt;Note that &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;Documents&lt;/span&gt; returns a collection of documents, as opposed to
  1455. &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;Document&lt;/span&gt; that only returns one.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;br /&gt;
  1456. &lt;span lang=&quot;EN&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
  1457. &lt;div class=&quot;MsoNormal&quot;&gt;
  1458. &lt;span lang=&quot;EN&quot;&gt;Of course, this is a simple introduction to a schemaless implementation of
  1459. GraphQL. There are more details, but it expands the GraphQL technology by
  1460. making it more dynamic, while the retaining the static parts. Look, for
  1461. example, at this snippets mixing static and dynamic attributes:&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
  1462. &lt;div class=&quot;MsoNormal&quot;&gt;
  1463. &lt;span lang=&quot;EN&quot;&gt;&lt;span style=&quot;color: blue;&quot;&gt;&lt;a href=&quot;http://playground.darwino.com/playground.nsf/GraphqlSnippets.xsp#snippet=Mixed_Document_join_on_User_Directory___Unid&quot;&gt;http://playground.darwino.com/playground.nsf/GraphqlSnippets.xsp#snippet=Mixed_Document_join_on_User_Directory___Unid&lt;/a&gt;&lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;br /&gt;
  1464. &lt;span lang=&quot;EN&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
  1465. &lt;div class=&quot;MsoNormal&quot;&gt;
  1466. &lt;span lang=&quot;EN&quot;&gt;The early &lt;a href=&quot;https://www.darwino.com/&quot;&gt;Darwino&lt;/a&gt; 2.0 code includes this capability in its Java based
  1467. implementation, released as open source on &lt;a href=&quot;https://github.com/darwino-org/Darwino-GraphQL&quot;&gt;darwino.org&lt;/a&gt;. Any GraphQL object can
  1468. implement a specific interface that will give it the dynamic capability (json
  1469. path...) on top of its static fields. If you go back to the previous example,
  1470. you&#39;ll see that the document UNID is a static field specific to the Darwino
  1471. JSON document.&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/div&gt;
  1472. &lt;div class=&quot;MsoNormal&quot;&gt;
  1473. &lt;br /&gt;&lt;/div&gt;
  1474. &lt;/div&gt;
  1475. There are more to discover, but hold on: if our session is accepted at &lt;a href=&quot;https://engage.ug/&quot;&gt;Engage 2017&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/paulswithers/&quot;&gt;Paul Withers&lt;/a&gt; and I will give more details on the implementation, along with an IBM Notes/Domino implementation!&lt;/div&gt;
  1476. </description><link>http://blog.riand.com/2017/03/schemaless-graphql.html</link><author>noreply@blogger.com (Philippe Riand)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-2067133848514555149</guid><pubDate>Fri, 17 Feb 2017 03:51:00 +0000</pubDate><atom:updated>2017-02-16T19:51:53.327-08:00</atom:updated><title>Get your apps integrated with IBM Connections, cloud and on-premises!</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  1477. I&#39;ve been using this blog to share some of the techniques we use in ProjExec to get tightly integrated with the Connections platform. I got a lot of feedback from developers who wanted to know more, so I&#39;m moving a step further: &lt;a href=&quot;https://frostillic.us/&quot;&gt;Jesse Gallagher&lt;/a&gt; and I will describe these techniques in a breakout session&amp;nbsp;@Connect 2017!&lt;br /&gt;
  1478. &lt;br /&gt;
  1479. &lt;blockquote class=&quot;tr_bq&quot; style=&quot;text-align: left;&quot;&gt;
  1480. &lt;b&gt;DEV-1430 : IBM Connections Integration: Exploring the Long List of Options&lt;/b&gt;&lt;br /&gt;&lt;i&gt;Program : Development, Design and Tools&lt;br /&gt;Topic : Enterprise collaboration&lt;br /&gt;Session Type : Breakout Session&lt;br /&gt;Date/Time : Wed, 22-Feb, 10:00 AM-10:45 AM&lt;br /&gt;Location : Moscone West, Level 2 - Room 2006&lt;br /&gt;Presenter(s) : Philippe Riand, Triloggroup; Jesse Gallagher, I Know Some Guys, LLC&lt;/i&gt;&lt;/blockquote&gt;
  1481. &lt;br /&gt;
  1482. Basically, we&#39;ll show for both on the cloud and on premises Connections:&lt;br /&gt;
  1483. &lt;br /&gt;
  1484. &lt;ul style=&quot;text-align: left;&quot;&gt;
  1485. &lt;li&gt;how to get your user authenticated by Connections, using OAuth or SSO&lt;/li&gt;
  1486. &lt;li&gt;how to call services in the behalf of that current user&lt;/li&gt;
  1487. &lt;li&gt;how to get your apps in the IBM Connection menu&lt;/li&gt;
  1488. &lt;li&gt;how to integrate the navbar, including on premises!&lt;/li&gt;
  1489. &lt;li&gt;how to share code common to community apps (cloud) and iWidget (on premises)&lt;/li&gt;
  1490. &lt;/ul&gt;
  1491. Beyond live code and demos, we will release a reusable library as open source, with all the necessary code to get your own Java based application integrated effortless. It will be available on &lt;a href=&quot;https://www.darwino.org/darwino/&quot;&gt;darwino.org&lt;/a&gt;, a branch of &lt;a href=&quot;https://www.openntf.org/&quot;&gt;OpenNTF&lt;/a&gt;.&lt;br /&gt;
  1492. &lt;br /&gt;
  1493. To make that possible, &lt;strike&gt;IBM helped by providing a long awaited ask&lt;/strike&gt;, &amp;nbsp;Wait wait wait, I&#39;m not going to reveal that here, but an &lt;strike&gt;IBMer will be on stage to explain&lt;/strike&gt;. Wait again, should not talk about this neither, people have to come to the session!&lt;br /&gt;
  1494. &lt;br /&gt;
  1495. See you&amp;nbsp;@Connect, and don&#39;t miss this session if you want to be an IBM Connections developer hero!&lt;br /&gt;
  1496. &lt;br /&gt;
  1497. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  1498. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBYpxQgiUgtqOcd4_Bwgnz0kdTt3NSVqynyLOQHA0R-EXF2OsgZErde_9ch0k0edPh-1IOFvnDFuIMB1gOxckod9tJ4idetiVf1s3ce0eIvHv-N3dHquth-4RSjhHMbO-2ZagSYR1gguY/s1600/connapp.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;345&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBYpxQgiUgtqOcd4_Bwgnz0kdTt3NSVqynyLOQHA0R-EXF2OsgZErde_9ch0k0edPh-1IOFvnDFuIMB1gOxckod9tJ4idetiVf1s3ce0eIvHv-N3dHquth-4RSjhHMbO-2ZagSYR1gguY/s640/connapp.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  1499. &lt;br /&gt;&lt;/div&gt;
  1500. </description><link>http://blog.riand.com/2017/02/get-your-apps-integrated-with-ibm.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBYpxQgiUgtqOcd4_Bwgnz0kdTt3NSVqynyLOQHA0R-EXF2OsgZErde_9ch0k0edPh-1IOFvnDFuIMB1gOxckod9tJ4idetiVf1s3ce0eIvHv-N3dHquth-4RSjhHMbO-2ZagSYR1gguY/s72-c/connapp.png" height="72" width="72"/><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-8496744837748528527</guid><pubDate>Thu, 26 Jan 2017 23:46:00 +0000</pubDate><atom:updated>2017-01-26T15:47:04.162-08:00</atom:updated><title>When SQL meets NoSQL, you get the best of both worlds!</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  1501. At the heart of Darwino is an advanced, portable JSON document store, implemented on top of any relational database. I&#39;m often being asked the following question &quot;why did you implement that on top of an RDBMS?&quot;. Behind the scene, the real question is: &quot;why are you not using MongoDB or another nosql database?&quot;&lt;br /&gt;
  1502. &lt;div&gt;
  1503. Well, I&#39;m generally answering it with multiple arguments:&lt;/div&gt;
  1504. &lt;div&gt;
  1505. &lt;ul style=&quot;text-align: left;&quot;&gt;
  1506. &lt;li&gt;It leverages all the RDBMS well known capabilities: transactions, data integrity, security, backups, performance, reporting, analytics...&lt;/li&gt;
  1507. &lt;li&gt;Nowadays, RDBMS handle JSON natively, thus providing high performance access to any data in this format.&amp;nbsp;&lt;/li&gt;
  1508. &lt;li&gt;Portability: it deploys on existing infrastructure, whenever it is cloud or on premises. Run your app on IBM Bluemix on top of DB2, or MS Azure on top of SQL Server... And on-premises, I don&#39;t know any organization that does not already have an RDBMS validated by the IT department.&amp;nbsp;&lt;/li&gt;
  1509. &lt;/ul&gt;
  1510. &lt;/div&gt;
  1511. &lt;div&gt;
  1512. But there is now another big reason: queries! This is another step in leveraging the native JSON support available in all major RDBMS.&lt;/div&gt;
  1513. &lt;div&gt;
  1514. &lt;br /&gt;&lt;/div&gt;
  1515. &lt;div&gt;
  1516. From the beginning, Darwino comes with a JSON based, MongoDB like, query language. It fully abstracts the relational database details, by converting the JSON query into SQL for the target database. It hides the relational model and behaves exactly the same on every single relational database. Plus it adds IBM Domino like capabilities, including category records, response documents, full text search... Of course, it honors the document based security. So it is very powerful. You can see it in action &lt;a href=&quot;https://playground.darwino.com/playground.nsf/DarwinoDbSnippets.xsp#snippet=Queries_-_SQL_Optimization_$and&quot;&gt;here&lt;/a&gt;.&lt;br /&gt;
  1517. &lt;br /&gt;
  1518. But it also has its own set of limitations. In particular, because it hides the relational model, it cannot take advantage of it. I&#39;m thinking about joins, subqueries, unions... Remember, it is modeled over MongoDB and IBM Domino, which do not support these capabilities.&lt;/div&gt;
  1519. &lt;div&gt;
  1520. &lt;br /&gt;&lt;/div&gt;
  1521. &lt;div&gt;
  1522. That&#39;s the reason why are introducing JSQL, which stands for JSON SQL. It complements the existing query language. In a nutshell, this is SQL targeting JSON document collections. Think a document collection as relational table, where each document is a row. The columns become &lt;a href=&quot;http://goessner.net/articles/JsonPath/&quot;&gt;JSON paths&lt;/a&gt;&amp;nbsp;on the documents.&lt;br /&gt;
  1523. &lt;br /&gt;&lt;/div&gt;
  1524. &lt;div&gt;
  1525. The beauty of implementing a NoSQL store on top of a RDBMS, is that you don&#39;t have to implement the SQL query engine yourself, but you can rely on the well proven underlying database. I don&#39;t know any NoSQL database that is just approaching what a mature SQL database can do in term of query. With behind it, decades of research and query optimization techniques that will be hard to re-implement! And finally, if a vendor does not give you entire satisfaction, then you can move to the next one. Frankly, isn&#39;t that more trustable than any proprietary NoSQL database, even open source?&lt;/div&gt;
  1526. &lt;div&gt;
  1527. &lt;br /&gt;
  1528. Ok, back to JSQL. Let&#39;s start with a simple example. Suppose that you have a collection of JSON documents named &#39;Pinball&#39;. Each pinball document has a few fields, like these:&lt;/div&gt;
  1529. &lt;div&gt;
  1530. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; { name: &#39;Revenge of Mars&#39;, manufacturer: &#39;Bally&#39;, ....}&lt;/span&gt;&lt;/div&gt;
  1531. &lt;div&gt;
  1532. &lt;br /&gt;&lt;/div&gt;
  1533. &lt;div&gt;
  1534. A JSQL query to list all the pinball machines in the database would be:&lt;/div&gt;
  1535. &lt;div&gt;
  1536. &lt;div&gt;
  1537. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;SELECT $.manufacturer as manufacturer, $.name as name&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
  1538. &lt;div&gt;
  1539. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;FROM pinball&lt;/span&gt;&lt;/div&gt;
  1540. &lt;div&gt;
  1541. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;ORDER BY $.manufacturer, $.name&lt;/span&gt;&lt;/div&gt;
  1542. &lt;/div&gt;
  1543. &lt;div&gt;
  1544. &lt;a href=&quot;https://playground.darwino.com/playground.nsf/JsqlSnippets.xsp#snippet=pinballs_Pinball_List_by_Name&quot;&gt;Live example&lt;/a&gt;&lt;/div&gt;
  1545. &lt;div&gt;
  1546. &lt;br /&gt;&lt;/div&gt;
  1547. &lt;div&gt;
  1548. Easy, isn&#39;t it? The syntax &#39;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;$.a&lt;/span&gt;&#39; is actually a JSON path that extracts the field &#39;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;a&lt;/span&gt;&#39; from the JSON document. It can obviously be more complex, like &#39;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;$.x.y.z&lt;/span&gt;&#39;, to extract hierarchical data.&lt;br /&gt;
  1549. Note that you can also get the whole JSON document with the simple &#39;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;$&lt;/span&gt;&#39; JSON Path:&lt;/div&gt;
  1550. &lt;div&gt;
  1551. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; SELECT $ as doc from pinball&lt;/span&gt;&lt;/div&gt;
  1552. &lt;div&gt;
  1553. &lt;a href=&quot;https://playground.darwino.com/playground.nsf/JsqlSnippets.xsp#snippet=tutorials_Read_Pinball_Documents&quot;&gt;Live example&lt;/a&gt;&lt;/div&gt;
  1554. &lt;div&gt;
  1555. &lt;br /&gt;
  1556. Under the hood, Darwino parses the original JSQL query and generates the final query for the target RDBMS. For example, here is how the query above is converted to Postgresql:&lt;/div&gt;
  1557. &lt;div&gt;
  1558. &lt;div&gt;
  1559. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; WITH TB1 AS&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
  1560. &lt;div&gt;
  1561. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; (SELECT *&lt;/span&gt;&lt;/div&gt;
  1562. &lt;div&gt;
  1563. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; FROM playground_DOC&lt;/span&gt;&lt;/div&gt;
  1564. &lt;div&gt;
  1565. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; WHERE STOREID=&#39;pinball&#39;&lt;/span&gt;&lt;/div&gt;
  1566. &lt;div&gt;
  1567. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; AND INSTID=&#39;&#39;)&lt;/span&gt;&lt;/div&gt;
  1568. &lt;div&gt;
  1569. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; SELECT&lt;br /&gt;&amp;nbsp; &amp;nbsp; jsonb_extract_path_text(JSON,&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&#39;manufacturer&#39;)::text AS manufacturer,&lt;/span&gt;&lt;/div&gt;
  1570. &lt;div&gt;
  1571. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; jsonb_extract_path_text(JSON,&#39;name&#39;)::text AS name&lt;/span&gt;&lt;/div&gt;
  1572. &lt;div&gt;
  1573. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; FROM TB1&lt;/span&gt;&lt;/div&gt;
  1574. &lt;div&gt;
  1575. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; ORDER BY&lt;br /&gt;&amp;nbsp; &amp;nbsp; jsonb_extract_path_text(JSON,&#39;manufacturer&#39;)::text,&lt;br /&gt;&amp;nbsp; &amp;nbsp; jsonb_extract_path_text(JSON,&#39;name&#39;)::text&lt;/span&gt;&lt;/div&gt;
  1576. &lt;/div&gt;
  1577. &lt;div&gt;
  1578. &lt;br /&gt;&lt;/div&gt;
  1579. &lt;div&gt;
  1580. The JSQL queries can be as complex as the database supports: it includes clauses like WHERE, ORDER BY, GROUP BY/HAVING, any JOIN, subqueries, functions, aggregation, UNION... Again, anything supported by the underlying database can be used. Also, during the conversion step, Darwino hides as much as possible the SQL differences between the database vendors. It even works on mobile devices on top of SQLite!&lt;br /&gt;
  1581. &lt;br /&gt;&lt;/div&gt;
  1582. &lt;div&gt;
  1583. Here is, another example: a query joining 3 document collections: pinballs, owners and a relation (owns) between the pinballs and the owners:&lt;/div&gt;
  1584. &lt;div&gt;
  1585. &lt;div&gt;
  1586. &lt;span class=&quot;Apple-tab-span&quot; style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace; white-space: pre;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;SELECT O.$.firstName firstname,&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
  1587. &lt;div&gt;
  1588. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt; &amp;nbsp; O.$.lastName lastname,&lt;/span&gt;&lt;/div&gt;
  1589. &lt;div&gt;
  1590. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt; &amp;nbsp; P.$.brand brand,&lt;/span&gt;&lt;/div&gt;
  1591. &lt;div&gt;
  1592. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt; &amp;nbsp; P.$.name name&lt;/span&gt;&lt;/div&gt;
  1593. &lt;div&gt;
  1594. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;FROM owners O&lt;/span&gt;&lt;/div&gt;
  1595. &lt;div&gt;
  1596. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt;  &lt;/span&gt;LEFT OUTER JOIN owns R ON R.$.owner=O._unid&lt;/span&gt;&lt;/div&gt;
  1597. &lt;div&gt;
  1598. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt;  &lt;/span&gt;LEFT OUTER JOIN pinball P ON R.$.ipdb=P._unid&lt;/span&gt;&lt;/div&gt;
  1599. &lt;div&gt;
  1600. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;ORDER BY firstname, lastname&lt;/span&gt;&lt;/div&gt;
  1601. &lt;/div&gt;
  1602. &lt;div&gt;
  1603. &lt;a href=&quot;https://playground.darwino.com/playground.nsf/JsqlSnippets.xsp#snippet=pinballs_Pinballs_and_Owners&quot;&gt;Live example&lt;/a&gt;&lt;/div&gt;
  1604. &lt;div&gt;
  1605. (The field syntax &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;_xyz&lt;/span&gt; allows access to the document meta-data, stored outside of the JSON document. It includes the document unid, the creation date...)&lt;br /&gt;
  1606. &lt;br /&gt;
  1607. Another one? What about a subquery to find the most expensive pinball:&lt;br /&gt;
  1608. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp; SELECT P.$.name name, P.$.manufacturer manufacturer,&lt;/span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;P.&quot;$.value&quot;::number &quot;value&quot;&lt;/span&gt;&lt;br /&gt;
  1609. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;FROM pinball P,&lt;/span&gt;&lt;br /&gt;
  1610. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;(SELECT MAX(&quot;$.value&quot;::number) val FROM pinball) MT&lt;/span&gt;&lt;br /&gt;
  1611. &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;WHERE P.&quot;$.value&quot;::number=MT.val&lt;/span&gt;&lt;br /&gt;
  1612. &lt;a href=&quot;https://playground.darwino.com/playground.nsf/JsqlSnippets.xsp#snippet=alexa_What_Is_The_Most_Expensive_Pinball&quot;&gt;Live Example&lt;/a&gt;&lt;br /&gt;
  1613. (Because there is no JSON schema, any JSON path is assumed to be a string by default. Darwino supports the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;::&lt;/span&gt; cast operator to specify other data types, like &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;::number&lt;/span&gt;)&lt;br /&gt;
  1614. &lt;br /&gt;
  1615. It, of course, preserves the document based security (a.k.a. readers/editors). Look at this&amp;nbsp;&lt;a href=&quot;https://playground.darwino.com/playground.nsf/JsqlSnippets.xsp#snippet=tutorials_Read_Pinball_Document_With_Security&quot;&gt;example&lt;/a&gt;, and you&#39;ll see that the generated SQL is decorated with the proper security condition on the reader fields.&lt;br /&gt;
  1616. &lt;br /&gt;
  1617. Thinking even further, we can provide a JDBC driver that will allow any JDBC client, like report engines, to connect to the database, while preserving the whole security model! For the record, I already created such a remote JDBC driver for &lt;a href=&quot;https://www.openntf.org/internal/home.nsf/project.xsp?action=openDocument&amp;amp;name=JDBC%20Access%20for%20IBM%20Lotus%20Domino&quot;&gt;DomSQL&lt;/a&gt;, so it would be easy reuse this piece of code.&lt;br /&gt;
  1618. &lt;br /&gt;
  1619. This feature is still under development but can be previewed in the Darwino 2.0 code stream, and live from the Playground. Hope you guys like it, and see the value of having an RDBMS behind the scene! Any feedback is more than welcome.&lt;br /&gt;
  1620. &lt;br /&gt;&lt;/div&gt;
  1621. &lt;div&gt;
  1622. BTW, I&#39;m a fan of Pinball machines, and so are my friends at &lt;a href=&quot;https://en.webgate.biz/&quot;&gt;WebGate&lt;/a&gt;. :-) I can show you my pinball database, augmented with AI, at &lt;a href=&quot;http://www-01.ibm.com/software/collaboration/events/connect/&quot;&gt;Connect 2017&lt;/a&gt;. See you at booth #630 in the showcase.&lt;/div&gt;
  1623. &lt;div&gt;
  1624. &lt;br /&gt;&lt;/div&gt;
  1625. &lt;div&gt;
  1626. &lt;br /&gt;&lt;/div&gt;
  1627. &lt;/div&gt;
  1628. </description><link>http://blog.riand.com/2017/01/when-sql-meets-nosql-you-get-best-of.html</link><author>noreply@blogger.com (Philippe Riand)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-7663859798827800977</guid><pubDate>Wed, 25 Jan 2017 21:25:00 +0000</pubDate><atom:updated>2017-01-25T13:25:04.752-08:00</atom:updated><title>ReactJS or AngularJS? What about something else?</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  1629. So far, ProjExec has been a really good citizen in the IBM/ICS world as we tried to reuse the core Connections stack as much as can (Dojo, OneUI, ...). But these technologies start to age while the browser technologies evolved a lot in the past years: what required a whole bunch of JavaScript using Dojo/JQuery can now be squeezed in a few lines using new libraries! It is time to change gears.&lt;br /&gt;
  1630. &lt;br /&gt;
  1631. We started to look at what technology would better fit our needs. The main requirements are:&lt;br /&gt;
  1632. &lt;ul style=&quot;text-align: left;&quot;&gt;
  1633. &lt;li&gt;Make the developers productive - easy to integrate new developers&lt;/li&gt;
  1634. &lt;li&gt;Have a decent performance when the application grows - ProjExec is large!&lt;/li&gt;
  1635. &lt;li&gt;Can integrate with the existing code, and be used incrementally within ProjExec&lt;/li&gt;
  1636. &lt;li&gt;The tool chain should integrate well with what we currently use (Maven, Eclipse...)&lt;/li&gt;
  1637. &lt;li&gt;Can be debugged easily, with mean full logs&lt;/li&gt;
  1638. &lt;li&gt;Have a eco-system with ready to use libraries (bootstrap, mobile, ...)&lt;/li&gt;
  1639. &lt;/ul&gt;
  1640. We already started to use AngularJS 1.x 2 yrs ago for some new modules. The initial steps were pretty easy, although it started to be more difficult when the complexity increased. And anyway, we know that AngularJS 1 is behind us. We cannot decently bet our future on it.&lt;br /&gt;
  1641. &lt;div&gt;
  1642. &lt;br /&gt;&lt;/div&gt;
  1643. &lt;div&gt;
  1644. The natural next move was to use Angular 2, as it fixes most of the issues found in 1.x. I would say this is true, but the learning curve grew significantly. That includes some technology choices (ex: Observable vs Promises), Typescript, the tool chain required to get an application properly packaged, and the heaviness of the resulting code... I spent time playing with it for Darwino (we&#39;ll release some examples), but I do not see the ProjExec developers being quickly productive with it. This has been further confirmed by one of my friend, a GDE (Google Developer Expert, the equivalent of IBM Champion): his one week training class is barely sufficient to get the developers comfortable. Also, getting it integrated in a venerable existing application raises some challenges.&lt;br /&gt;
  1645. Ok, we&#39;ll might use it later with Ionic 2 for a dedicated mobile UI, but for now, let&#39;s look if something easier can be used on the existing web application.&lt;/div&gt;
  1646. &lt;div&gt;
  1647. &lt;br /&gt;&lt;/div&gt;
  1648. &lt;div&gt;
  1649. The next candidate on the list is Facebook&#39;s ReactJS. Clearly, there is a buzz around this technology. It has very interesting concepts, it creates high performance applications. But the initial learning curve is even bigger than Angular&#39;s, particularly for Java developers. Moreover, it is pretty low level, which provides great flexibility but you then have to choose the proper companion libraries.&lt;br /&gt;
  1650. We&#39;ll keep this in the back burner, let&#39;s look is something else better suits our needs.&lt;br /&gt;
  1651. &lt;br /&gt;
  1652. Finally, we discovered that another library got a lot of traction last year: it is called Vue.js (&lt;a href=&quot;https://vuejs.org/&quot;&gt;https://vuejs.org/&lt;/a&gt;). After a first look at it, it feels like what I hoped Angular 2 would be! Ok, I bought 2 books from &lt;a href=&quot;https://www.packtpub.com/web-development/majesty-vuejs&quot;&gt;Packt ($5 each)&lt;/a&gt;, got the source code from Github and started my deep learning. It features exactly what we need:&lt;br /&gt;
  1653. &lt;ul style=&quot;text-align: left;&quot;&gt;
  1654. &lt;li&gt;Easy learning curve, particularly when you have some Angular 1 background. Anyway easier than Angular or React&lt;/li&gt;
  1655. &lt;li&gt;You don&#39;t need a whole set of tools/CLI to be in place up front. Just include the single JS file and you can get started. It happens that we&#39;ll finally use Webpack, but this was not a requirement from the beginning.&lt;/li&gt;
  1656. &lt;li&gt;It fixes many of the problems/inconsistencies found in Angular JS 1.x (bind vs values, separation of concerns, single file components...)&lt;/li&gt;
  1657. &lt;li&gt;It easily integrates within existing applications, regardless of what they already use (Dojo, JQuery, ...), even if they are not designed as SPAs&lt;/li&gt;
  1658. &lt;li&gt;It performs as good as ReactJS. Moreover, if you&#39;re a JSX fan, it is available as well&lt;/li&gt;
  1659. &lt;li&gt;It is very lightweight and non intrusive&lt;/li&gt;
  1660. &lt;li&gt;Developer&#39;s productivity feels superior to ReactJS&lt;/li&gt;
  1661. &lt;li&gt;A good set of tools/libraries/docs is available&amp;nbsp;&lt;a href=&quot;https://github.com/vuejs/awesome-vue&quot;&gt;https://github.com/vuejs/awesome-vue&lt;/a&gt;&lt;/li&gt;
  1662. &lt;li&gt;See how it compares to the other libraries:&amp;nbsp;&lt;a href=&quot;https://vuejs.org/v2/guide/comparison.html&quot;&gt;https://vuejs.org/v2/guide/comparison.html&lt;/a&gt;&lt;/li&gt;
  1663. &lt;/ul&gt;
  1664. We decided to go with it. The early feedback I got from the development team is very positive. We&#39;ll see in the longer run if it keeps its promises, but so far so good.&lt;/div&gt;
  1665. &lt;div&gt;
  1666. &lt;br /&gt;&lt;/div&gt;
  1667. &lt;div&gt;
  1668. I&#39;ll be interested to hear anybody else experience. Please feel free to comment.&lt;/div&gt;
  1669. &lt;div&gt;
  1670. &lt;br /&gt;&lt;/div&gt;
  1671. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  1672. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTUY-6It6NpgjJN6Y9RGA3x0ryI_xyYiRVTtqt08g_Va9VnSolDDXstcOJ9erlhJHjuioZ9HEdZrUS1aNQYg-w4I9-aN1QgHzVcU_MGmH5KouoFiyjf0EExTZX9oHhjDzZ0a7vdhQ1s_I/s1600/vuelogo.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTUY-6It6NpgjJN6Y9RGA3x0ryI_xyYiRVTtqt08g_Va9VnSolDDXstcOJ9erlhJHjuioZ9HEdZrUS1aNQYg-w4I9-aN1QgHzVcU_MGmH5KouoFiyjf0EExTZX9oHhjDzZ0a7vdhQ1s_I/s200/vuelogo.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  1673. &lt;div&gt;
  1674. &lt;br /&gt;&lt;/div&gt;
  1675. &lt;/div&gt;
  1676. </description><link>http://blog.riand.com/2017/01/reactjs-or-angularjs-what-about.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTUY-6It6NpgjJN6Y9RGA3x0ryI_xyYiRVTtqt08g_Va9VnSolDDXstcOJ9erlhJHjuioZ9HEdZrUS1aNQYg-w4I9-aN1QgHzVcU_MGmH5KouoFiyjf0EExTZX9oHhjDzZ0a7vdhQ1s_I/s72-c/vuelogo.png" height="72" width="72"/><thr:total>11</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-2760493133028578028</guid><pubDate>Tue, 03 Jan 2017 16:53:00 +0000</pubDate><atom:updated>2017-01-03T08:53:35.942-08:00</atom:updated><title>Why AngularJS sounds familiar to XPages developers...</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  1677. When I started to look at AngularJS a few years ago, I surprisingly found myself quickly comfortable with this technology. One of the reason is that many of its concepts are shared with XPages. &amp;nbsp;Of course, there are fundamental differences, the most obvious being AngularJS a pure client technology while XPages, based on JSF, is a server side one. But still, they share a lot! If you know XPages, your experience understanding AngularJS should be similar to mine. I&#39;m basing my experience on &amp;nbsp;AngularJS 1.x, although it is also applies to 2.0. 2.0 is even closer to Java programmers with the use of TypeScript and new concepts familiar to Java developers (classes, ...). But this is a different topic.&lt;br /&gt;
  1678. &lt;br /&gt;
  1679. &lt;div&gt;
  1680. Let me dive into the similarities between the 2 frameworks:&lt;br /&gt;
  1681. &lt;div&gt;
  1682. &lt;div&gt;
  1683. &lt;br /&gt;&lt;/div&gt;
  1684. &lt;div&gt;
  1685. 1- The Document Object Model (DOM) as the page content&lt;/div&gt;
  1686. &lt;div&gt;
  1687. If it is obvious that Angular works on top of a DOM, but XPages is also working on a DOM. If AngularJS leverages the browser HTML DOM, XPages manages an hierarchy of JSF components. Both are hierarchical, with a parent-children relationship, and represent the content of the page.&amp;nbsp;&lt;/div&gt;
  1688. &lt;div&gt;
  1689. &lt;br /&gt;&lt;/div&gt;
  1690. &lt;div&gt;
  1691. 2- Contextual data&lt;/div&gt;
  1692. &lt;div&gt;
  1693. Angular calls that a scope, which is data assigned to a DOM element and its children. If this concept does not exist in the JSF spec, XPages provides it through &quot;complex properties&quot; like data sources, or even the &quot;context&quot; object that lets developers add data to a component and its children.&lt;/div&gt;
  1694. &lt;div&gt;
  1695. &lt;br /&gt;&lt;/div&gt;
  1696. &lt;div&gt;
  1697. 3- Scripts and formulas&lt;/div&gt;
  1698. &lt;div&gt;
  1699. Obviously Angular uses JavaScript as its scripting language, similarly to XPages, plus a JavaScript based formula language for expression evaluation. XPages also uses the JSF EL for basic expression&lt;/div&gt;
  1700. &lt;div&gt;
  1701. &lt;br /&gt;&lt;/div&gt;
  1702. &lt;div&gt;
  1703. 4- Data binding&lt;/div&gt;
  1704. &lt;div&gt;
  1705. Angular does data binding either through attributes to the DOM elements, or expressions within curly braces &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;{{...}}&lt;/span&gt;. Well, XPages does kind of the same thing with value binding expressions, &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;${...}&lt;/span&gt;or &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;#{...}&lt;/span&gt;&lt;/div&gt;
  1706. &lt;div&gt;
  1707. &lt;br /&gt;&lt;/div&gt;
  1708. &lt;div&gt;
  1709. 5- Directives or components&lt;/div&gt;
  1710. &lt;div&gt;
  1711. Angular does great job extending your browser HTML with directives. In short, you can create your own tags (or even attributes one can use on existing tags). XPages does the same with custom controls, adding new tags to the XML describing the page.&lt;br /&gt;
  1712. &lt;br /&gt;
  1713. 6- Managed beans&lt;br /&gt;
  1714. Angular uses services, JSF has managed beans to externalize the business logic outside of the page. In both cases, you access the object by its name, using dependency injection for Angular, or faces-config.xml for XPages.&lt;/div&gt;
  1715. &lt;/div&gt;
  1716. &lt;div&gt;
  1717. &lt;br /&gt;&lt;/div&gt;
  1718. &lt;div&gt;
  1719. Of course, there are real differences. One of them is the processing model, as XPages is running well known phases while Angular uses an event based model, with a queue of events. But still, as an XPages developer, you&#39;ll feel at home pretty quickly when writing an application.&lt;br /&gt;
  1720. &lt;br /&gt;
  1721. How about Angular 2.0? Well we are currently experimenting with it and I&#39;ll share my findings in a little while.&lt;br /&gt;
  1722. &lt;br /&gt;
  1723. &lt;br /&gt;&lt;/div&gt;
  1724. &lt;div&gt;
  1725. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  1726. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCYY29sbo9Uy_0JlSI-IIREhiIz6Sbz3OA8fZ25_ylBOPGws0ApYyrimgsrRAVFpBT35aaLT_h8eCGPuRrNVNPieCUKZ_iDI-7WcfLzVwdekeaiXmgBHBUBHLYX2ExvxOzEMCLnwYSCg0/s1600/angularjs.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCYY29sbo9Uy_0JlSI-IIREhiIz6Sbz3OA8fZ25_ylBOPGws0ApYyrimgsrRAVFpBT35aaLT_h8eCGPuRrNVNPieCUKZ_iDI-7WcfLzVwdekeaiXmgBHBUBHLYX2ExvxOzEMCLnwYSCg0/s1600/angularjs.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  1727. &lt;br /&gt;&lt;/div&gt;
  1728. &lt;/div&gt;
  1729. &lt;/div&gt;
  1730. </description><link>http://blog.riand.com/2017/01/why-angularjs-sounds-familiar-to-xpages.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCYY29sbo9Uy_0JlSI-IIREhiIz6Sbz3OA8fZ25_ylBOPGws0ApYyrimgsrRAVFpBT35aaLT_h8eCGPuRrNVNPieCUKZ_iDI-7WcfLzVwdekeaiXmgBHBUBHLYX2ExvxOzEMCLnwYSCg0/s72-c/angularjs.png" height="72" width="72"/><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7557605395826374162.post-2281323735818327635</guid><pubDate>Thu, 22 Dec 2016 23:08:00 +0000</pubDate><atom:updated>2016-12-22T15:08:31.280-08:00</atom:updated><title>The king is dead, long live to the new iOS Java king!</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
  1731. When presenting &lt;a href=&quot;https://www.darwino.com/&quot;&gt;Darwino&lt;/a&gt;, I&#39;m often asked the question how do we run Java natively on iOS, or other platforms that are not known to be Java friendly? Well, we started this journey a few years ago, but the landscape of cross platform Java evolved a lot since that. Let me go through the history and explain where we stand now, as a new king has just been proclaimed. If you thing the story is too long, then just go to&amp;nbsp;&lt;a href=&quot;https://multi-os-engine.org/&quot;&gt;Multi OS Engine&lt;/a&gt;&amp;nbsp;for the answer.&lt;br /&gt;
  1732. &lt;br /&gt;
  1733. Back to the dark age, the most promising solution was provided by Google, through a &lt;a href=&quot;https://en.wikipedia.org/wiki/Source-to-source_compiler&quot;&gt;transpiler &lt;/a&gt;called &lt;a href=&quot;http://j2objc.org/&quot;&gt;J2OBJC&lt;/a&gt;. Basically, j2objc converts the Java source code to Objective-C source code, that can later be compiled using Apple XCode as any native Objective-C application. Google is successfully using this technology internally for a set of projects, including popular ones like Inbox. With this, along with &lt;a href=&quot;http://www.gwtproject.org/&quot;&gt;GWT&lt;/a&gt;, they claim that 70% of the code is shared between Android, iOS and the web. This is more than decent, but it has some issues:&lt;br /&gt;
  1734. &lt;ul style=&quot;text-align: left;&quot;&gt;
  1735. &lt;li&gt;It works at the source code level, while most of the libraries are currently provided as compiled binaries, through a maven reposioty&lt;/li&gt;
  1736. &lt;li&gt;The runtime libraries (e.g. the set of classes available at runtime, like java.io.*, java.net.*...) is a small subset of the standard JRE libraries. That limits what you can use in your application. Even if you pay attention to only use the less common denominator, it prevents you from using libraries depending of the missing pieces&lt;/li&gt;
  1737. &lt;li&gt;Objective-C (and Swift) memory management. While Java uses a garbage collector, Objective-C uses references count. I can write a whole article on the pros and cons on each of them, but the fact is that Java memory management does not translate properly to Objective-C, leading to hard-to-debug issues&lt;/li&gt;
  1738. &lt;li&gt;Finally, it does not provide an easy access to the native APIs like Cocoa, or even JNI (which we use for to access SQLite, for example)&lt;/li&gt;
  1739. &lt;/ul&gt;
  1740. Ok, despite the all the great things it carries, j2objc didn&#39;t fit with our objective of &quot;write once, run everywhere&quot;.&lt;br /&gt;
  1741. &lt;br /&gt;
  1742. Fortunately, a beta from a new product called &lt;a href=&quot;https://robovm.com/&quot;&gt;RoboVM &lt;/a&gt;appeared soon after. It was not a transpiler but a real compiler that generates true native code from the Java classes. It removed most the limitations of j2objc, by bundling the Android runtime class libraries (minus the specific UI classes), giving access to the whole iOS API through a Java-to-native bridge, full support of JNI... With RoboVM, we migrated our existing Android applications to iOS in a few hours.&lt;br /&gt;
  1743. Another tool, called&amp;nbsp;&lt;a href=&quot;https://www.migeran.com/&quot;&gt;Migeran&lt;/a&gt;, was also emerging&amp;nbsp;with a very similar value proposition. We also moved our apps to Migeran. But, after talking to both teams, we decided to go with RoboVM, mostly because RoboVM was &lt;a href=&quot;https://github.com/robovm&quot;&gt;open source&lt;/a&gt; (well, partly). By the meantime, Migeran was acquired by Intel to extend their &lt;a href=&quot;https://software.intel.com/en-us/intel-inde&quot;&gt;INDE &lt;/a&gt;offerings. They called it Multi OS Engine, aka MOE . That reinforced the validity of our choice.&lt;br /&gt;
  1744. &lt;br /&gt;
  1745. But we knew that RoboVM was also a potential target for an acquisition. And what had to happen happened. Xamarin, the .NET based competitor, acquired it and changed the rules: raised the price up, stopped to deliver to the open source project... Without any inside knowledge of the acquisition terms, I believe that the Xamarin move to Java was not to make the Java community happier, but to awake and fear its main supporter: Microsoft. Which quickly reacted by acquiring Xamarin a couple of months later, and just killed RoboVM. Man, we are orphan!&lt;br /&gt;
  1746. &lt;br /&gt;
  1747. One of the solution was to restart from the open source version of RoboVM, and contribute enhancements. Actually, there is a team of knowledgeable&amp;nbsp;&lt;a href=&quot;https://libgdx.badlogicgames.com/&quot;&gt;LibGDX &lt;/a&gt;developers that cleaned-up the code (see: &lt;a href=&quot;https://github.com/MobiDevelop/robovm&quot;&gt;https://github.com/MobiDevelop/robovm&lt;/a&gt;) and maintains it. That was the best place to start from. We even contributed the maven plug-in, thanks to Jesse. But the effort to make this project an enterprise grade tool is still big. It misses a decent debugger, support of LLVM and a few other things. Despite the value of the people behind it, it lacks the support of a real organization dedicating resources to its development.&lt;br /&gt;
  1748. &lt;br /&gt;
  1749. But events are often chained. Another long awaited event happened: Intel decided to release MOE as an open source project. More than that, they gave the leadership back to the original creator, Migeran! That&#39;s the difference between Intel and Microsoft: instead of just killing and erasing the project, they decided to give it for free to the community. Many thanks to you Intel, you deserve it!&lt;br /&gt;
  1750. &lt;br /&gt;
  1751. This changed the game for us. Instead of investing in the opensource RoboVM, we decided to move to MOE. The switch was actually easy, as the two tools are close enough. We made sure with Migeran that what we need is part of the platform, for example Eclipse and Maven support. Clearly, this team is great and they got the features in very quickly. As a result, since Darwino 1.5.0, we moved to the latest MOE 1.2.x and it appears to be very stable. We now have the same experience we had with the commercial version of RoboVM, including a full debugger! Yes, you can debug the Java code you deployed to your device.&lt;br /&gt;
  1752. Moving forward, we&#39;ll support whatever comes from Migeran: 1.3 is currently in a beta state, while 2.0 with come with a bunch of great new features. In particular the support of LLVM, latest ART runtime, maybe native windows support... Well the dream comes true.&lt;br /&gt;
  1753. &lt;br /&gt;
  1754. Last but not the least, we have great relationship with Migeran and in particular its CTO, the mighty Gergely Kis. Man, this guy is really skilled and he knows what he talks about. We had another opportunity to talk face to face last month, as he came to the first Darwino workshop in Germany. It came out that we are very aligned on the vision, so the future looks very promising. We also exchanged on future projects, like bringing a Java based, fully portable ReactNative to mobile devices.&lt;br /&gt;
  1755. &lt;br /&gt;
  1756. For more information about MOE now and future, Gergely gave us a presentation during the darwino workhop. The slides are now available on slideshare:&amp;nbsp;&lt;a href=&quot;http://www.slideshare.net/migeran/moe-cross-platform-mobile-apps-in-java?qid=cb244ace-ae2f-448d-9482-83ee5fba51e0&amp;amp;v=&amp;amp;b=&amp;amp;from_search=2&quot;&gt;Slides&lt;/a&gt;&lt;br /&gt;
  1757. &lt;br /&gt;
  1758. If you are a Java developer who&#39;d like to develop for iOS, then MOE is definitively what you&#39;re looking for. Start from&amp;nbsp;&lt;a href=&quot;https://multi-os-engine.org/&quot;&gt;Multi Os Engine&lt;/a&gt;&amp;nbsp;web site, install the studio and enjoy.&lt;br /&gt;
  1759. &lt;br /&gt;
  1760. &lt;br /&gt;
  1761. &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  1762. &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSeYP1ArQ29QcTNIWCWPwXdiiv_Sg0AhQL9UN6uK4x3SUltBh9-UK0gP9koapi5a5Lcwtv35bRIHb-Ex0wKwjQ7GF7m0x5OHFevy66hgu92EehomCEFy_QSvW8Yd_C_PKQ9eTCnIvd0pY/s1600/moe-logo-45x45.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSeYP1ArQ29QcTNIWCWPwXdiiv_Sg0AhQL9UN6uK4x3SUltBh9-UK0gP9koapi5a5Lcwtv35bRIHb-Ex0wKwjQ7GF7m0x5OHFevy66hgu92EehomCEFy_QSvW8Yd_C_PKQ9eTCnIvd0pY/s1600/moe-logo-45x45.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
  1763. &lt;br /&gt;
  1764. &lt;br /&gt;&lt;/div&gt;
  1765. </description><link>http://blog.riand.com/2016/12/the-king-is-dead-long-live-to-new-ios.html</link><author>noreply@blogger.com (Philippe Riand)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSeYP1ArQ29QcTNIWCWPwXdiiv_Sg0AhQL9UN6uK4x3SUltBh9-UK0gP9koapi5a5Lcwtv35bRIHb-Ex0wKwjQ7GF7m0x5OHFevy66hgu92EehomCEFy_QSvW8Yd_C_PKQ9eTCnIvd0pY/s72-c/moe-logo-45x45.png" height="72" width="72"/><thr:total>2</thr:total></item></channel></rss>

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid RSS" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

http://www.feedvalidator.org/check.cgi?url=http%3A//blog.riand.com/feeds/posts/default%3Falt%3Drss

Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda