Skip to content

Commit f783ad9

Browse files
author
Volodymyr Kublytskyi
committed
Merge branch magento/magento2/develop into magento-partners/magento2ce/prs-develop-2017-11-17
2 parents ca4df43 + 2bdf70c commit f783ad9

File tree

3 files changed

+156
-11
lines changed

3 files changed

+156
-11
lines changed

app/code/Magento/Ui/view/base/web/js/lib/knockout/template/engine.js

Lines changed: 135 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
* See COPYING.txt for license details.
44
*/
55
define([
6+
'jquery',
67
'ko',
78
'underscore',
89
'./observable_source',
910
'./renderer',
1011
'../../logger/console-logger'
11-
], function (ko, _, Source, renderer, consoleLogger) {
12+
], function ($, ko, _, Source, renderer, consoleLogger) {
1213
'use strict';
1314

1415
var RemoteTemplateEngine,
@@ -18,7 +19,76 @@ define([
1819
/**
1920
* Remote template engine class. Is used to be able to load remote templates via knockout template binding.
2021
*/
21-
RemoteTemplateEngine = function () {};
22+
RemoteTemplateEngine = function () {
23+
// Instance reference for closure.
24+
var engine = this,
25+
// Decorate the builtin Knockout "template" binding to track synchronous template renders.
26+
origUpdate = ko.bindingHandlers.template.update;
27+
28+
/**
29+
* Counter to track the number of currently running render tasks (both synchronous and asynchronous).
30+
* @type {Number}
31+
* @private
32+
*/
33+
this._rendersOutstanding = 0;
34+
35+
/**
36+
* Use a jQuery object as an event bus (but any event emitter with on/off/emit methods could work)
37+
* @type {jQuery}
38+
* @private
39+
*/
40+
this._events = $(this);
41+
42+
/**
43+
* Rendered templates
44+
* @type {Object}
45+
* @private
46+
*/
47+
this._templatesRendered = {};
48+
49+
/*eslint-disable no-unused-vars*/
50+
/**
51+
* Decorate update method
52+
*
53+
* @param {HTMLElement} element
54+
* @param {Function} valueAccessor
55+
* @param {Object} allBindings
56+
* @param {Object} viewModel
57+
* @param {ko.bindingContext} bindingContext
58+
* @returns {*}
59+
*/
60+
ko.bindingHandlers.template.update = function (element, valueAccessor, allBindings, viewModel, bindingContext) {
61+
/*eslint-enable no-unused-vars*/
62+
var options = ko.utils.peekObservable(valueAccessor()),
63+
templateName,
64+
isSync,
65+
updated;
66+
67+
if (typeof options === 'object') {
68+
if (options.templateEngine && options.templateEngine !== engine) {
69+
return origUpdate.apply(this, arguments);
70+
}
71+
72+
if (!options.name) {
73+
consoleLogger.error('Could not find template name', options);
74+
}
75+
templateName = options.name;
76+
} else if (typeof options === 'string') {
77+
templateName = options;
78+
} else {
79+
consoleLogger.error('Could not build a template binding', options);
80+
}
81+
engine._trackRender(templateName);
82+
isSync = engine._hasTemplateLoaded(templateName);
83+
updated = origUpdate.apply(this, arguments);
84+
85+
if (isSync) {
86+
engine._releaseRender(templateName, 'sync');
87+
}
88+
89+
return updated;
90+
};
91+
};
2292

2393
/**
2494
* Creates unique template identifier based on template name and it's extenders (optional)
@@ -32,6 +102,64 @@ define([
32102
RemoteTemplateEngine.prototype = new NativeTemplateEngine;
33103
RemoteTemplateEngine.prototype.constructor = RemoteTemplateEngine;
34104

105+
/**
106+
* When an asynchronous render task begins, increment the internal counter for tracking when renders are complete.
107+
* @private
108+
*/
109+
RemoteTemplateEngine.prototype._trackRender = function (templateName) {
110+
var rendersForTemplate = this._templatesRendered[templateName] !== undefined ?
111+
this._templatesRendered[templateName] : 0;
112+
113+
this._rendersOutstanding++;
114+
this._templatesRendered[templateName] = rendersForTemplate + 1;
115+
this._resolveRenderWaits();
116+
};
117+
118+
/**
119+
* When an asynchronous render task ends, decrement the internal counter for tracking when renders are complete.
120+
* @private
121+
*/
122+
RemoteTemplateEngine.prototype._releaseRender = function (templateName) {
123+
var rendersForTemplate = this._templatesRendered[templateName];
124+
125+
this._rendersOutstanding--;
126+
this._templatesRendered[templateName] = rendersForTemplate - 1;
127+
this._resolveRenderWaits();
128+
};
129+
130+
/**
131+
* Check to see if renders are complete and trigger events for listeners.
132+
* @private
133+
*/
134+
RemoteTemplateEngine.prototype._resolveRenderWaits = function () {
135+
if (this._rendersOutstanding === 0) {
136+
this._events.triggerHandler('finishrender');
137+
}
138+
};
139+
140+
/**
141+
* Get a promise for the end of the current run of renders, both sync and async.
142+
* @return {jQueryPromise} - promise that resolves when render completes
143+
*/
144+
RemoteTemplateEngine.prototype.waitForFinishRender = function () {
145+
var defer = $.Deferred();
146+
147+
this._events.one('finishrender', defer.resolve);
148+
149+
return defer.promise();
150+
};
151+
152+
/**
153+
* Returns true if this template has already been asynchronously loaded and will be synchronously rendered.
154+
* @param {String} templateName
155+
* @returns {Boolean}
156+
* @private
157+
*/
158+
RemoteTemplateEngine.prototype._hasTemplateLoaded = function (templateName) {
159+
// Sources object will have cached template once makeTemplateSource has run
160+
return sources.hasOwnProperty(templateName);
161+
};
162+
35163
/**
36164
* Overrided method of native knockout template engine.
37165
* Caches template after it's unique name and renders in once.
@@ -43,7 +171,8 @@ define([
43171
* @returns {TemplateSource} Object with methods 'nodes' and 'data'.
44172
*/
45173
RemoteTemplateEngine.prototype.makeTemplateSource = function (template, templateDocument, options, bindingContext) {
46-
var source,
174+
var engine = this,
175+
source,
47176
templateId;
48177

49178
if (typeof template === 'string') {
@@ -60,12 +189,13 @@ define([
60189
component: bindingContext.$data.name
61190
});
62191

63-
renderer.render(template).done(function (rendered) {
192+
renderer.render(template).then(function (rendered) {
64193
consoleLogger.info('templateLoadedFromServer', {
65194
template: templateId,
66195
component: bindingContext.$data.name
67196
});
68197
source.nodes(rendered);
198+
engine._releaseRender(templateId, 'async');
69199
}).fail(function () {
70200
consoleLogger.error('templateLoadingFail', {
71201
template: templateId,
@@ -115,7 +245,7 @@ define([
115245
RemoteTemplateEngine.prototype.renderTemplate = function (template, bindingContext, options, templateDocument) {
116246
var templateSource = this.makeTemplateSource(template, templateDocument, options, bindingContext);
117247

118-
return this.renderTemplateSource(templateSource, bindingContext, options);
248+
return this.renderTemplateSource(templateSource);
119249
};
120250

121251
return new RemoteTemplateEngine;

app/code/Magento/Ui/view/base/web/js/lib/knockout/template/renderer.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ define([
99
], function ($, _, loader) {
1010
'use strict';
1111

12-
var colonReg = /\\:/g,
13-
attributes = {},
14-
elements = {},
15-
globals = [],
12+
var colonReg = /\\:/g,
13+
renderedTemplatePromises = {},
14+
attributes = {},
15+
elements = {},
16+
globals = [],
1617
renderer,
1718
preset;
1819

@@ -24,11 +25,25 @@ define([
2425
*
2526
* @param {String} tmplPath - Path to the template.
2627
* @returns {jQueryPromise}
28+
* @alias getRendered
2729
*/
2830
render: function (tmplPath) {
29-
var loadPromise = loader.loadTemplate(tmplPath);
31+
var cachedPromise = renderedTemplatePromises[tmplPath];
3032

31-
return loadPromise.then(renderer.parseTemplate);
33+
if (!cachedPromise) {
34+
cachedPromise = renderedTemplatePromises[tmplPath] = loader
35+
.loadTemplate(tmplPath)
36+
.then(renderer.parseTemplate);
37+
}
38+
39+
return cachedPromise;
40+
},
41+
42+
/**
43+
* @ignore
44+
*/
45+
getRendered: function (tmplPath) {
46+
return renderer.render(tmplPath);
3247
},
3348

3449
/**
187 KB
Loading

0 commit comments

Comments
 (0)