{"action":"created","issue":{"url":"https://api.github.com/repos/cmv/cmv-app/issues/452","labels_url":"https://api.github.com/repos/cmv/cmv-app/issues/452/labels{/name}","comments_url":"https://api.github.com/repos/cmv/cmv-app/issues/452/comments","events_url":"https://api.github.com/repos/cmv/cmv-app/issues/452/events","html_url":"https://github.com/cmv/cmv-app/issues/452","id":103257458,"number":452,"title":"Singleton cache object","user":{"login":"carrbrpoa","id":10212618,"avatar_url":"https://avatars.githubusercontent.com/u/10212618?v=3","gravatar_id":"","url":"https://api.github.com/users/carrbrpoa","html_url":"https://github.com/carrbrpoa","followers_url":"https://api.github.com/users/carrbrpoa/followers","following_url":"https://api.github.com/users/carrbrpoa/following{/other_user}","gists_url":"https://api.github.com/users/carrbrpoa/gists{/gist_id}","starred_url":"https://api.github.com/users/carrbrpoa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/carrbrpoa/subscriptions","organizations_url":"https://api.github.com/users/carrbrpoa/orgs","repos_url":"https://api.github.com/users/carrbrpoa/repos","events_url":"https://api.github.com/users/carrbrpoa/events{/privacy}","received_events_url":"https://api.github.com/users/carrbrpoa/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":8,"created_at":"2015-08-26T12:38:51Z","updated_at":"2015-08-28T19:19:39Z","closed_at":null,"body":"Hello,\r\n\r\nFirst of all, I opened [another issue](https://github.com/tmcgee/cmv-widgets/issues/26), related to another widget. Adding to the info provided in the issue, there's an aditional behavior that seems to redraw the table and call ```renderCell``` in every table resize.\r\n\r\nBased on this and in the fact that I'm not a Dojo expert, I decided to create some kind of cache, [Singleton](http://www.anujgakhar.com/2013/08/29/singletons-in-dojo/) and injectable anywhere in the application. Ended with a class called ```ObjectCacheManager```:\r\n\r\n```javascript\r\ndefine([\r\n\t\t\"dojo/_base/declare\",\r\n\t\t\"dojo/_base/lang\",\r\n\t\t\"dojo/json\"\r\n\t], function (\r\n\t\tdeclare,\r\n\t\tlang,\r\n\t\tJSON) {\r\n\tvar ObjectCacheManager = declare(\"gis.dijit.ObjectCacheManager\", [], {\r\n\t\t\tcachedData : {},\r\n\r\n\t\t\taddCachedData : function (identifier, data) {\r\n\t\t\t\tthis.addCachedDataInternal(this.cachedData, identifier, data);\r\n\t\t\t},\r\n\r\n\t\t\taddCachedDataInternal : function (target, identifiers, data) {\r\n\t\t\t\tvar currentIdentifier = identifiers.shift();\r\n\t\t\t\tif (!currentIdentifier) {\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tif (!target[currentIdentifier]) {\r\n\t\t\t\t\tvar stringCacheData = '{\"' + currentIdentifier + '\": {}}';\r\n\t\t\t\t\tvar jsonCacheData = JSON.parse(stringCacheData);\r\n\t\t\t\t\tlang.mixin(target, jsonCacheData);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (identifiers.length === 0) {\r\n\t\t\t\t\ttarget[currentIdentifier] = data;\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tthis.addCachedDataInternal(target[currentIdentifier], identifiers, data);\r\n\t\t\t},\r\n\r\n\t\t\tremoveCachedData : function (identifier) {\r\n\t\t\t\tif (!identifier || identifier.length === 0) {\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tvar previousIdentifier = identifier.length === 1\r\n\t\t\t\t\t ? identifier[0]\r\n\t\t\t\t\t : identifier[identifier.length - 2];\r\n\t\t\t\tvar cacheItem = this.searchCacheIdentifier(this.cachedData, identifier, true);\r\n\t\t\t\tdelete cacheItem[previousIdentifier];\r\n\t\t\t},\r\n\r\n\t\t\tgetCachedData : function (identifier) {\r\n\t\t\t\tvar cacheItem = this.searchCacheIdentifier(this.cachedData, identifier);\r\n\t\t\t\treturn cacheItem;\r\n\t\t\t},\r\n\r\n\t\t\tsearchCacheIdentifier : function (searchTarget, identifiers, returnPrevious) {\r\n\t\t\t\tif (!identifiers || !searchTarget) {\r\n\t\t\t\t\treturn undefined;\r\n\t\t\t\t}\r\n\t\t\t\tif (identifiers.length === 0 || (returnPrevious && identifiers.length === 1)) {\r\n\t\t\t\t\treturn searchTarget;\r\n\t\t\t\t}\r\n\t\t\t\tvar currentIdentifier = identifiers.shift();\r\n\t\t\t\treturn this.searchCacheIdentifier(searchTarget[currentIdentifier], identifiers);\r\n\t\t\t}\r\n\t\t});\r\n\tif (!_instance) {\r\n\t\tvar _instance = new ObjectCacheManager();\r\n\t}\r\n\treturn _instance;\r\n});\r\n```\r\n\r\nExample of use with Search widget's config:\r\n```javascript\r\ndefine([ 'dojo/on', 'dojo/date/locale', 'dojo/topic', 'dojo/request', 'dojo/_base/array',\r\n'gis/dijit/ObjectCacheManager' ], function(on, locale, topic, request, array, objectCache) {\r\n(...)\r\ngridOptions : {\r\n\tcolumns : [{\r\n\t\t\tid : 'Documents',\r\n\t\t\tfield : 'EU',\r\n\t\t\tlabel : 'Exp. Uni.',\r\n\t\t\twidth : 100,\r\n\t\t\tsortable : true,\r\n\t\t\texportable : true,\r\n\t\t\trenderCell : function (object, value, node) {\r\n\r\n\t\t\t\tvar cachedData = objectCache.getCachedData(['attributesTable', 'pesquisaEU', 'EU', value]);\r\n\t\t\t\tif (cachedData) {\r\n\t\t\t\t\t(...)\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\r\n\t\t\t\trequest.get(\"http://.../webservices/documents/123456789\", {\r\n\t\t\t\t\thandleAs : \"json\"\r\n\t\t\t\t}).then(function (data) {\r\n\t\t\t\t\tif (data.count > 0) {\r\n\t\t\t\t\t\tvar nodeContents = '';\r\n\t\t\t\t\t\tarray.forEach(data.documents, function (documento, i) {\r\n\t\t\t\t\t\t\tnodeContents += '<a href=\"' + documento.trim() + '\" target=\"_blank\">' + 'Doc.' + (i + 1) + '</a> ';\r\n\t\t\t\t\t\t});\r\n\r\n\t\t\t\t\t\tobjectCache.addCachedData(['attributesTable', 'pesquisaEU', 'EU', value], data.documents);\r\n\t\t\t\t\t\t(...)\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t},\r\n\t\t(...)\r\n```\r\n\r\nThe previous call to ```objectCache.addCachedData``` results in a \"cached\" json like this:\r\n```javascript\r\n{\r\n\tattributesTable : {\r\n\t\tpesquisaEU : {\r\n\t\t\tEU : {\r\n\t\t\t\t\"202238.9\" : [...]\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n```\r\nSubsequent calls with different identifiers aggregates to this:\r\n```javascript\r\n{\r\n\tattributesTable : {\r\n\t\tpesquisaEU : {\r\n\t\t\tEU : {\r\n\t\t\t\t\"202238.9\" : [...],\r\n\t\t\t\t\"303255.1\" : [...],\r\n\t\t\t\t\"400012.5\" : [...]\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n```\r\n\r\nThis avoids that my ```request.get``` inside ```renderCell``` be called several times with every resize, because now it's cached. In another part of the application (clicking *Search* button, for example), I can clear that specific cache that I added with the \"starter level\" called *attributesTable*.\r\n\r\nDo you guys thinks it's contributable or is it too specific for this? Better ideas, other ways? :)"},"comment":{"url":"https://api.github.com/repos/cmv/cmv-app/issues/comments/135864893","html_url":"https://github.com/cmv/cmv-app/issues/452#issuecomment-135864893","issue_url":"https://api.github.com/repos/cmv/cmv-app/issues/452","id":135864893,"user":{"login":"carrbrpoa","id":10212618,"avatar_url":"https://avatars.githubusercontent.com/u/10212618?v=3","gravatar_id":"","url":"https://api.github.com/users/carrbrpoa","html_url":"https://github.com/carrbrpoa","followers_url":"https://api.github.com/users/carrbrpoa/followers","following_url":"https://api.github.com/users/carrbrpoa/following{/other_user}","gists_url":"https://api.github.com/users/carrbrpoa/gists{/gist_id}","starred_url":"https://api.github.com/users/carrbrpoa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/carrbrpoa/subscriptions","organizations_url":"https://api.github.com/users/carrbrpoa/orgs","repos_url":"https://api.github.com/users/carrbrpoa/repos","events_url":"https://api.github.com/users/carrbrpoa/events{/privacy}","received_events_url":"https://api.github.com/users/carrbrpoa/received_events","type":"User","site_admin":false},"created_at":"2015-08-28T19:19:39Z","updated_at":"2015-08-28T19:19:39Z","body":"Perfect, @tmcgee!\r\nIt worked as you said. See the beginning of my config/search.js:\r\n\r\n```javascript\r\ndefine([ 'dojo/on', 'dojo/date/locale', 'dojo/topic', 'dojo/request', 'dojo/_base/array', 'dojo/promise/all' ],\r\n\t\tfunction(on, locale, topic, request, array, all) {\r\n\r\n\t\t\tfunction formatDateTime(value) {\r\n\t\t\t\tif (!value)\r\n\t\t\t\t\treturn '';\r\n\t\t\t\tvar date = new Date(value);\r\n\t\t\t\treturn locale.format(date, {\r\n\t\t\t\t\tformatLength : 'short'\r\n\t\t\t\t});\r\n\t\t\t}\r\n\r\n\t\t\ttopic.subscribe('pesquisaEU' + '/queryResults', function(queryResults) {\r\n\t\t\t\tvar calls = [];\r\n\r\n\t\t\t\t// loop through the results\r\n\t\t\t\tarray.forEach(queryResults.features, function(feature) {\r\n\t\t\t\t\t// add your call for each feature to the calls array\r\n\t\t\t\t\t// the result of each call would be used to update the\r\n\t\t\t\t\t// queryResults\r\n\t\t\t\t\tvar findDocuments = \r\n\t\t\t\t\t\trequest.get(\"http://.../webservices/documentos/123456789\", {\r\n\t\t\t\t\t\t\thandleAs : \"json\"\r\n\t\t\t\t\t\t}).then(function(data) {\r\n\t\t\t\t\t\t\tvar dataToCache = [];\r\n\t\t\t\t\t\t\tif (data.count > 0) {\r\n\t\t\t\t\t\t\t\tfeature.attributes.imagedDocuments = data.documentos[0]; //first one just to test\r\n\t\t\t\t\t\t\t\treturn true;\r\n\t\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}, function(err) {\r\n\t\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\t\r\n\t\t\t\t\tcalls.push(findDocuments);\r\n\t\t\t\t});\r\n\r\n\t\t\t\t// all done, now populate the Grid\r\n\t\t\t\t\r\n\t\t\t\tall(calls).then(function () { \r\n\t\t\t\t\ttopic.publish('pesquisaEU' + '/populateGrid', queryResults); \r\n\t\t\t\t});\r\n\t\t\t});\r\n\r\n\t\t\treturn {\r\n\t\t\t\tmap : true,\r\n\t\t\t\tmapClickMode : true,\r\n(...)\r\n```\r\nKind of *hardcoded* stuff but I don't see another way.\r\n\r\nThen, I'll handle my ```feature.attributes.imagedDocuments``` in a ```column```'s ```formatter``` or something, since it's an array and I want to present each item as an icon:\r\n\r\n```javascript\r\ncolumns : [{\r\n\t\tid : 'Documents',\r\n\t\tfield : 'imagedDocuments',\r\n\t\tlabel : 'Exp. Uni.',\r\n\t\twidth : 100,\r\n\t\tsortable : true,\r\n\t\texportable : true,\r\n\t\tformatter : function (value) {\r\n\t\t\t(...)\r\n\t\t}\r\n\t}\r\n\t(...)\r\n],\r\n```\r\n\r\nOf course, I checked that the anonymous function is **not** called a thousand times, just what I want.\r\nThank you very much for this tip!\r\n\r\nNow, what about the ObjectCacheManager stuff? :)"}}