Merge remote-tracking branch 'github-open/open227' into open-master

This commit is contained in:
Pete Richards
2015-11-03 15:43:08 -08:00
3 changed files with 77 additions and 28 deletions

View File

@ -31,7 +31,6 @@ define(
function () { function () {
"use strict"; "use strict";
/** /**
* Defines the mct-representation directive. This may be used to * Defines the mct-representation directive. This may be used to
* present domain objects as HTML (with event wiring), with the * present domain objects as HTML (with event wiring), with the
@ -96,6 +95,9 @@ define(
}), }),
toClear = [], // Properties to clear out of scope on change toClear = [], // Properties to clear out of scope on change
counter = 0, counter = 0,
couldRepresent = false,
lastId,
lastKey,
changeTemplate = templateLinker.link($scope, element); changeTemplate = templateLinker.link($scope, element);
// Populate scope with any capabilities indicated by the // Populate scope with any capabilities indicated by the
@ -141,6 +143,13 @@ define(
}); });
} }
function unchanged(canRepresent, id, key) {
return canRepresent &&
couldRepresent &&
id === lastId &&
key === lastKey;
}
// General-purpose refresh mechanism; should set up the scope // General-purpose refresh mechanism; should set up the scope
// as appropriate for current representation key and // as appropriate for current representation key and
// domain object. // domain object.
@ -149,7 +158,13 @@ define(
representation = lookup($scope.key, domainObject), representation = lookup($scope.key, domainObject),
path = representation && getPath(representation), path = representation && getPath(representation),
uses = ((representation || {}).uses || []), uses = ((representation || {}).uses || []),
canRepresent = !!(path && domainObject); canRepresent = !!(path && domainObject),
id = domainObject && domainObject.getId(),
key = $scope.key;
if (unchanged(canRepresent, id, key)) {
return;
}
// Create an empty object named "representation", for this // Create an empty object named "representation", for this
// representation to store local variables into. // representation to store local variables into.
@ -173,6 +188,11 @@ define(
delete $scope[property]; delete $scope[property];
}); });
// To allow simplified change detection next time around
couldRepresent = canRepresent;
lastId = id;
lastKey = key;
// Populate scope with fields associated with the current // Populate scope with fields associated with the current
// domain object (if one has been passed in) // domain object (if one has been passed in)
if (canRepresent) { if (canRepresent) {

View File

@ -92,10 +92,19 @@ define(
var activeElement = element, var activeElement = element,
activeTemplateUrl, activeTemplateUrl,
comment = this.$compile('<!-- hidden mct element -->')(scope), comment = this.$compile('<!-- hidden mct element -->')(scope),
activeScope,
self = this; self = this;
function destroyScope() {
if (activeScope) {
activeScope.$destroy();
activeScope = undefined;
}
}
function removeElement() { function removeElement() {
if (activeElement !== comment) { if (activeElement !== comment) {
destroyScope();
activeElement.replaceWith(comment); activeElement.replaceWith(comment);
activeElement = comment; activeElement = comment;
} }
@ -110,8 +119,10 @@ define(
} }
function populateElement(template) { function populateElement(template) {
element.empty(); destroyScope();
element.append(self.$compile(template)(scope)); activeScope = scope.$new(false);
element.html(template);
self.$compile(element.contents())(activeScope);
} }
function badTemplate(templateUrl) { function badTemplate(templateUrl) {
@ -120,8 +131,8 @@ define(
} }
function changeTemplate(templateUrl) { function changeTemplate(templateUrl) {
if (templateUrl !== activeTemplateUrl) {
if (templateUrl) { if (templateUrl) {
destroyScope();
addElement(); addElement();
self.load(templateUrl).then(function (template) { self.load(templateUrl).then(function (template) {
// Avoid race conditions // Avoid race conditions
@ -136,7 +147,6 @@ define(
} }
activeTemplateUrl = templateUrl; activeTemplateUrl = templateUrl;
} }
}
if (templateUrl) { if (templateUrl) {
changeTemplate(templateUrl); changeTemplate(templateUrl);

View File

@ -27,7 +27,8 @@ define(
function (TemplateLinker) { function (TemplateLinker) {
'use strict'; 'use strict';
var JQLITE_METHODS = [ 'replaceWith', 'empty', 'append' ]; var JQLITE_METHODS = [ 'replaceWith', 'empty', 'html', 'contents' ],
SCOPE_METHODS = [ '$on', '$new', '$destroy' ];
describe("TemplateLinker", function () { describe("TemplateLinker", function () {
var mockTemplateRequest, var mockTemplateRequest,
@ -38,6 +39,8 @@ define(
mockElement, mockElement,
mockTemplates, mockTemplates,
mockElements, mockElements,
mockContents,
mockNewScope,
mockPromise, mockPromise,
linker; linker;
@ -46,14 +49,18 @@ define(
mockSce = jasmine.createSpyObj('$sce', ['trustAsResourceUrl']); mockSce = jasmine.createSpyObj('$sce', ['trustAsResourceUrl']);
mockCompile = jasmine.createSpy('$compile'); mockCompile = jasmine.createSpy('$compile');
mockLog = jasmine.createSpyObj('$log', ['error', 'warn']); mockLog = jasmine.createSpyObj('$log', ['error', 'warn']);
mockScope = jasmine.createSpyObj('$scope', ['$on']); mockScope = jasmine.createSpyObj('$scope', SCOPE_METHODS);
mockNewScope = jasmine.createSpyObj('$scope', SCOPE_METHODS);
mockElement = jasmine.createSpyObj('element', JQLITE_METHODS); mockElement = jasmine.createSpyObj('element', JQLITE_METHODS);
mockPromise = jasmine.createSpyObj('promise', ['then']); mockPromise = jasmine.createSpyObj('promise', ['then']);
mockTemplates = {}; mockTemplates = {};
mockElements = {}; mockElements = {};
mockContents = {};
mockTemplateRequest.andReturn(mockPromise); mockTemplateRequest.andReturn(mockPromise);
mockCompile.andCallFake(function (html) { mockCompile.andCallFake(function (toCompile) {
var html = typeof toCompile === 'string' ?
toCompile : toCompile.testHtml;
mockTemplates[html] = jasmine.createSpy('template'); mockTemplates[html] = jasmine.createSpy('template');
mockElements[html] = mockElements[html] =
jasmine.createSpyObj('templateEl', JQLITE_METHODS); jasmine.createSpyObj('templateEl', JQLITE_METHODS);
@ -63,6 +70,17 @@ define(
mockSce.trustAsResourceUrl.andCallFake(function (url) { mockSce.trustAsResourceUrl.andCallFake(function (url) {
return { trusted: url }; return { trusted: url };
}); });
mockScope.$new.andReturn(mockNewScope);
mockElement.html.andCallFake(function (html) {
mockContents[html] =
jasmine.createSpyObj('contentsEl', JQLITE_METHODS);
mockContents[html].testHtml = html;
});
mockElement.contents.andCallFake(function () {
return mockContents[
mockElement.html.mostRecentCall.args[0]
];
});
linker = new TemplateLinker( linker = new TemplateLinker(
mockTemplateRequest, mockTemplateRequest,
@ -131,10 +149,11 @@ define(
}, false); }, false);
}); });
it("compiles loaded templates with linked scope", function () { it("compiles element contents with a new scope", function () {
expect(mockCompile).toHaveBeenCalledWith(testTemplate); expect(mockCompile)
.toHaveBeenCalledWith(mockContents[testTemplate]);
expect(mockTemplates[testTemplate]) expect(mockTemplates[testTemplate])
.toHaveBeenCalledWith(mockScope); .toHaveBeenCalledWith(mockNewScope);
}); });
it("replaces comments with specified element", function () { it("replaces comments with specified element", function () {
@ -142,9 +161,9 @@ define(
.toHaveBeenCalledWith(mockElement); .toHaveBeenCalledWith(mockElement);
}); });
it("appends rendered content to the specified element", function () { it("inserts HTML content into the specified element", function () {
expect(mockElement.append) expect(mockElement.html)
.toHaveBeenCalledWith(mockElements[testTemplate]); .toHaveBeenCalledWith(testTemplate);
}); });
it("clears templates when called with undefined", function () { it("clears templates when called with undefined", function () {