I have a plugin I am working on that extends the topic-list-item with the topic-list-tags plugin-outlet with a file in …/connectors/topic-list-tags/file-name.raw.hbs. When my page loads (I am running in development mode with Vagrant) I am greeted with Uncaught Error: Assertion Failed: undefined must be a subclass or an instance of Ember.View, not
in the console, as a result of plugin-outlet.js.es6:164 using undefined as an Ember View object.
This happens because when the connectors are loaded, _connectorCache[‘topic-list-tags’] is set to an empty array, and the raw template is put into _rawCache[‘topic-list-tags’]. Later, because the empty array in _connectorCache[‘topic-list-tags’] is a truthy value, it attempts to load a view from this empty array, which of course is undefined.
The relevant code sections from plugin-outlet.js.es6 include…
findOutlets(Ember.TEMPLATES, function(outletName, resource, uniqueName) {
_connectorCache[outletName] = _connectorCache[outletName] || [];
const mixin = {templateName: resource.replace('javascripts/', '')};
let viewClass = uniqueViews[uniqueName];
if (viewClass) {
// We are going to add it back with the proper template
_connectorCache[outletName].removeObject(viewClass);
} else {
if (!/\.raw$/.test(uniqueName)) {
viewClass = Ember.View.extend({ classNames: [outletName + '-outlet', uniqueName] });
}
}
if (viewClass) {
_connectorCache[outletName].pushObject(viewClass.extend(mixin));
} else {
// we have a raw template
if (!_rawCache[outletName]) {
_rawCache[outletName] = [];
}
_rawCache[outletName].push(Ember.TEMPLATES[resource]);
}
});
}
and
registerHelper('plugin-outlet', function(params, hash, options, env) {
const connectionName = params[0];
if (!_connectorCache) { buildConnectorCache(); }
if (_connectorCache[connectionName]) {
const childViews = _connectorCache[connectionName];
// If there is more than one view, create a container. Otherwise
// just shove it in.
const viewClass = (childViews.length > 1) ? Ember.ContainerView : childViews[0];
const newHash = $.extend({}, viewInjections(env.data.view.container));
if (hash.tagName) { newHash.tagName = hash.tagName; }
delete options.fn; // we don't need the default template since we have a connector
// ---The error occurs on the line below, where viewClass is undefined---
env.helpers.view.helperFunction.call(this, [viewClass], newHash, options, env);
[...]
}
});
A very easy workaround is to simply add an additional .hbs (not .raw.hbs) file to the …/connectors/topic-list-tags/ directory. This makes _connectorCache[‘topic-list-tags’] into an array with one element, preventing the error.
Alternatively, it would be very simple to either not create the _connectorCache entry in the first place for a raw template, or to ensure the array is not empty before indexing it.
What I don’t understand in all of this is why this does not seem to be a problem for the majority of Discourse users, since popular plugins like the Topic List Preview and other customizations also depend on the topic-list-tags outlet.
Is this is not a general bug, then I would want to know what keeps this from impacting other systems. Is the error harmlessly ignored when Discourse is run in production mode?
I have made a pull request to fix this: https://github.com/discourse/discourse/pull/4296