");
diff --git a/app/assets/javascripts/discourse/components/global-notice.js.es6 b/app/assets/javascripts/discourse/components/global-notice.js.es6
index 7dca11a0c..0f6f60f67 100644
--- a/app/assets/javascripts/discourse/components/global-notice.js.es6
+++ b/app/assets/javascripts/discourse/components/global-notice.js.es6
@@ -1,8 +1,9 @@
-export default Ember.Component.extend({
+import StringBuffer from 'discourse/mixins/string-buffer';
- shouldRerender: Discourse.View.renderIfChanged("site.isReadOnly"),
+export default Ember.Component.extend(StringBuffer, {
+ rerenderTriggers: ['site.isReadOnly'],
- render: function(buffer) {
+ renderString: function(buffer) {
var notices = [];
if (this.site.get("isReadOnly")) {
diff --git a/app/assets/javascripts/discourse/components/navigation-item.js.es6 b/app/assets/javascripts/discourse/components/navigation-item.js.es6
index ff5b01acd..e66fdd276 100644
--- a/app/assets/javascripts/discourse/components/navigation-item.js.es6
+++ b/app/assets/javascripts/discourse/components/navigation-item.js.es6
@@ -1,17 +1,11 @@
-/**
- This view handles rendering of a navigation item
+import StringBuffer from 'discourse/mixins/string-buffer';
- @class NavigationItemComponent
- @extends Ember.Component
- @namespace Discourse
- @module Discourse
-**/
-export default Ember.Component.extend({
+export default Ember.Component.extend(StringBuffer, {
tagName: 'li',
classNameBindings: ['active', 'content.hasIcon:has-icon'],
attributeBindings: ['title'],
hidden: Em.computed.not('content.visible'),
- shouldRerender: Discourse.View.renderIfChanged('content.count'),
+ rerenderTriggers: ['content.count'],
title: function() {
var categoryName = this.get('content.categoryName'),
@@ -42,7 +36,7 @@ export default Ember.Component.extend({
return I18n.t("filters." + name + ".title", extra);
}.property('content.count'),
- render: function(buffer) {
+ renderString: function(buffer) {
var content = this.get('content');
buffer.push("
\n");
}
-export default Ember.Component.extend({
+export default Ember.Component.extend(StringBuffer, {
tagName: 'span',
classNameBindings: [':topic-post-badges'],
- _shouldRerender: Discourse.View.renderIfChanged('url', 'unread', 'newPosts', 'unseen'),
+ rerenderTriggers: ['url', 'unread', 'newPosts', 'unseen'],
- render: function(buffer) {
+ renderString: function(buffer) {
var url = this.get('url');
link(buffer, this.get('unread'), url, 'unread', 'unread_posts');
diff --git a/app/assets/javascripts/discourse/components/topic-status.js.es6 b/app/assets/javascripts/discourse/components/topic-status.js.es6
index 1d0883f2e..dd8b0f8c0 100644
--- a/app/assets/javascripts/discourse/components/topic-status.js.es6
+++ b/app/assets/javascripts/discourse/components/topic-status.js.es6
@@ -1,18 +1,12 @@
-/**
- This view is for rendering an icon representing the status of a topic
+import StringBuffer from 'discourse/mixins/string-buffer';
- @class TopicStatusComponent
- @extends Ember.Component
- @namespace Discourse
- @module Discourse
-**/
-export default Ember.Component.extend({
+export default Ember.Component.extend(StringBuffer, {
classNames: ['topic-statuses'],
hasDisplayableStatus: Em.computed.or('topic.archived','topic.closed', 'topic.pinned', 'topic.unpinned', 'topic.invisible', 'topic.archetypeObject.notDefault', 'topic.is_warning'),
- shouldRerender: Discourse.View.renderIfChanged('topic.archived', 'topic.closed', 'topic.pinned', 'topic.visible', 'topic.unpinned', 'topic.is_warning'),
+ rerenderTriggers: ['topic.archived', 'topic.closed', 'topic.pinned', 'topic.visible', 'topic.unpinned', 'topic.is_warning'],
- didInsertElement: function(){
+ watchClick: function(){
var self = this;
this.$('a').click(function(){
@@ -27,13 +21,13 @@ export default Ember.Component.extend({
return false;
});
- },
+ }.on('didInsertElement'),
canAct: function() {
return Discourse.User.current() && !this.get('disableActions');
}.property('disableActions'),
- render: function(buffer) {
+ renderString: function(buffer) {
if (!this.get('hasDisplayableStatus')) { return; }
var self = this;
@@ -41,7 +35,7 @@ export default Ember.Component.extend({
var renderIconIf = function(conditionProp, name, key, actionable) {
if (!self.get(conditionProp)) { return; }
var title = Handlebars.Utils.escapeExpression(I18n.t("topic_statuses." + key + ".help"));
- var startTag = actionable ? "a href='#'" : "span";
+ var startTag = actionable ? "a href" : "span";
var endTag = actionable ? "a" : "span";
buffer.push("<" + startTag + " title='" + title + "' class='topic-status'>
" + endTag + ">");
diff --git a/app/assets/javascripts/discourse/helpers/i18n_helpers.js b/app/assets/javascripts/discourse/helpers/i18n_helpers.js
index 324229c97..fb9e8c7c8 100644
--- a/app/assets/javascripts/discourse/helpers/i18n_helpers.js
+++ b/app/assets/javascripts/discourse/helpers/i18n_helpers.js
@@ -49,14 +49,13 @@ Ember.Handlebars.registerBoundHelper("boundI18n", function(property, options) {
@for Handlebars
**/
Ember.Handlebars.registerHelper('countI18n', function(key, options) {
- var view = Discourse.View.extend({
+ var view = Discourse.View.extend(Discourse.StringBuffer, {
tagName: 'span',
- shouldRerender: Discourse.View.renderIfChanged('count', 'suffix'),
+ rerenderTriggers: ['count', 'suffix'],
- render: function(buffer) {
+ renderString: function(buffer) {
buffer.push(I18n.t(key + (this.get('suffix') || ''), { count: this.get('count') }));
}
-
});
return Ember.Handlebars.helpers.view.call(this, view, options);
});
diff --git a/app/assets/javascripts/discourse/views/activity-filter.js.es6 b/app/assets/javascripts/discourse/views/activity-filter.js.es6
index b29821fd5..24445239d 100644
--- a/app/assets/javascripts/discourse/views/activity-filter.js.es6
+++ b/app/assets/javascripts/discourse/views/activity-filter.js.es6
@@ -1,8 +1,10 @@
-export default Ember.Component.extend({
+import StringBuffer from 'discourse/mixins/string-buffer';
+
+export default Ember.Component.extend(StringBuffer, {
tagName: 'li',
classNameBindings: ['active', 'noGlyph'],
- shouldRerender: Discourse.View.renderIfChanged('content.count', 'count'),
+ rerenderTriggers: ['content.count', 'count'],
noGlyph: Em.computed.empty('icon'),
active: function() {
@@ -37,7 +39,7 @@ export default Ember.Component.extend({
return this.get('content.description') || I18n.t("user.filters.all");
}.property('content.description'),
- render: function(buffer) {
+ renderString: function(buffer) {
buffer.push("
");
var icon = this.get('icon');
if (icon) {
diff --git a/app/assets/javascripts/discourse/views/button.js.es6 b/app/assets/javascripts/discourse/views/button.js.es6
index b06713cb0..0f62c7204 100644
--- a/app/assets/javascripts/discourse/views/button.js.es6
+++ b/app/assets/javascripts/discourse/views/button.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.View.extend({
+import StringBuffer from 'discourse/mixins/string-buffer';
+
+export default Discourse.View.extend(StringBuffer, {
tagName: 'button',
classNameBindings: [':btn', ':standard', 'dropDownToggle'],
attributeBindings: ['title', 'data-toggle', 'data-share-url'],
@@ -12,7 +14,7 @@ export default Discourse.View.extend({
return I18n.t(this.get('textKey'));
}.property('textKey'),
- render: function(buffer) {
+ renderString: function(buffer) {
if (this.renderIcon) {
this.renderIcon(buffer);
}
diff --git a/app/assets/javascripts/discourse/views/dropdown-button.js.es6 b/app/assets/javascripts/discourse/views/dropdown-button.js.es6
index 307183ea7..b10d8ad7a 100644
--- a/app/assets/javascripts/discourse/views/dropdown-button.js.es6
+++ b/app/assets/javascripts/discourse/views/dropdown-button.js.es6
@@ -1,41 +1,45 @@
-export default Discourse.View.extend({
- classNameBindings: [':btn-group', 'hidden'],
- shouldRerender: Discourse.View.renderIfChanged('text', 'longDescription'),
+import StringBuffer from 'discourse/mixins/string-buffer';
- didInsertElement: function() {
- var self = this;
+export default Discourse.View.extend(StringBuffer, {
+ classNameBindings: [':btn-group', 'hidden'],
+ rerenderTriggers: ['text', 'longDescription'],
+
+ _bindClick: function() {
// If there's a click handler, call it
- if (self.clicked) {
- self.$('ul li').on('click.dropdown-button', function(e) {
+ if (this.clicked) {
+ var self = this;
+ this.$().on('click.dropdown-button', 'ul li', function(e) {
e.preventDefault();
- if ($(e.currentTarget).data('id') !== self.get('activeItem'))
+ if ($(e.currentTarget).data('id') !== self.get('activeItem')) {
self.clicked($(e.currentTarget).data('id'));
+ }
+ self.$('.dropdown-toggle').dropdown('toggle');
return false;
});
}
- },
+ }.on('didInsertElement'),
- willDestroyElement: function() {
- this.$('ul li').off('click.dropdown-button');
- },
+ _unbindClick: function() {
+ this.$().off('click.dropdown-button', 'ul li');
+ }.on('willDestroyElement'),
- render: function(buffer) {
- var self = this;
+ renderString: function(buffer) {
- buffer.push("" + self.get('title') + " ");
+ buffer.push("" + this.get('title') + " ");
buffer.push("");
- buffer.push(self.get('text'));
+ buffer.push(this.get('text'));
buffer.push(" ");
buffer.push("");
- var desc = self.get('longDescription');
+ var desc = this.get('longDescription');
if (desc) {
buffer.push("");
buffer.push(desc);
diff --git a/app/assets/javascripts/discourse/views/input-tip.js.es6 b/app/assets/javascripts/discourse/views/input-tip.js.es6
index b2d0709c9..6caf2dad6 100644
--- a/app/assets/javascripts/discourse/views/input-tip.js.es6
+++ b/app/assets/javascripts/discourse/views/input-tip.js.es6
@@ -1,19 +1,13 @@
-/**
- This view handles rendering a tip when a field on a form is invalid
+import StringBuffer from 'discourse/mixins/string-buffer';
- @class InputTipView
- @extends Discourse.View
- @namespace Discourse
- @module Discourse
-**/
-export default Discourse.View.extend({
+export default Discourse.View.extend(StringBuffer, {
classNameBindings: [':tip', 'good', 'bad'],
+ rerenderTriggers: ['validation'],
- shouldRerender: Discourse.View.renderIfChanged('validation'),
bad: Em.computed.alias('validation.failed'),
good: Em.computed.not('bad'),
- render: function(buffer) {
+ renderString: function(buffer) {
var reason = this.get('validation.reason');
if (reason) {
var icon = this.get('good') ? 'fa-check' : 'fa-times';
diff --git a/app/assets/javascripts/discourse/views/post-menu.js.es6 b/app/assets/javascripts/discourse/views/post-menu.js.es6
index 4925a38c0..1c44a6ffb 100644
--- a/app/assets/javascripts/discourse/views/post-menu.js.es6
+++ b/app/assets/javascripts/discourse/views/post-menu.js.es6
@@ -1,3 +1,5 @@
+import StringBuffer from 'discourse/mixins/string-buffer';
+
// Helper class for rendering a button
export var Button = function(action, label, icon, opts) {
this.action = action;
@@ -28,11 +30,11 @@ Button.prototype.render = function(buffer) {
var hiddenButtons;
-export default Discourse.View.extend({
+export default Discourse.View.extend(StringBuffer, {
tagName: 'section',
classNames: ['post-menu-area', 'clearfix'],
- shouldRerender: Discourse.View.renderIfChanged(
+ rerenderTriggers: [
'post.deleted_at',
'post.flagsAvailable.@each',
'post.reply_count',
@@ -43,13 +45,13 @@ export default Discourse.View.extend({
'post.topic.deleted_at',
'post.replies.length',
'post.wiki',
- 'collapsed'),
+ 'collapsed'],
_collapsedByDefault: function() {
this.set('collapsed', true);
}.on('init'),
- render: function(buffer) {
+ renderString: function(buffer) {
var post = this.get('post');
buffer.push("");
diff --git a/app/assets/javascripts/discourse/views/star-button.js.es6 b/app/assets/javascripts/discourse/views/star-button.js.es6
index e08bd14bc..c87aea9f2 100644
--- a/app/assets/javascripts/discourse/views/star-button.js.es6
+++ b/app/assets/javascripts/discourse/views/star-button.js.es6
@@ -6,7 +6,7 @@ export default ButtonView.extend({
helpKeyBinding: 'controller.starTooltipKey',
attributeBindings: ['disabled'],
- shouldRerender: Discourse.View.renderIfChanged('controller.starred'),
+ rerenderTriggers: ['controller.starred'],
click: function() {
this.get('controller').send('toggleStar');
diff --git a/app/assets/javascripts/discourse/views/topic-closing.js.es6 b/app/assets/javascripts/discourse/views/topic-closing.js.es6
index bfbd211a6..c5f22d503 100644
--- a/app/assets/javascripts/discourse/views/topic-closing.js.es6
+++ b/app/assets/javascripts/discourse/views/topic-closing.js.es6
@@ -1,19 +1,15 @@
-/**
- This view is used for rendering the notification that a topic will
- automatically close.
+import StringBuffer from 'discourse/mixins/string-buffer';
- @class TopicClosingView
- @extends Discourse.View
- @namespace Discourse
- @module Discourse
-**/
-export default Discourse.View.extend({
+export default Discourse.View.extend(StringBuffer, {
elementId: 'topic-closing-info',
delayedRerender: null,
- shouldRerender: Discourse.View.renderIfChanged('topic.closed', 'topic.details.{auto_close_at,auto_close_based_on_last_post,auto_close_hours}'),
+ rerenderTriggers: ['topic.closed',
+ 'topic.details.auto_close_at',
+ 'topic.details.auto_close_based_on_last_post',
+ 'topic.details.auto_close_hours'],
- render: function(buffer) {
+ renderString: function(buffer) {
if (!this.present('topic.details.auto_close_at')) return;
if (this.get("topic.closed")) return;
@@ -36,8 +32,7 @@ export default Discourse.View.extend({
}
var basedOnLastPost = this.get("topic.details.auto_close_based_on_last_post");
- var key = basedOnLastPost ? 'topic.auto_close_notice_based_on_last_post' : 'topic.auto_close_notice'
-
+ var key = basedOnLastPost ? 'topic.auto_close_notice_based_on_last_post' : 'topic.auto_close_notice';
var autoCloseHours = this.get("topic.details.auto_close_hours") || 0;
buffer.push(' ');
diff --git a/app/assets/javascripts/discourse/views/topic-map-container.js.es6 b/app/assets/javascripts/discourse/views/topic-map-container.js.es6
index b6b85d3cc..5dee27706 100644
--- a/app/assets/javascripts/discourse/views/topic-map-container.js.es6
+++ b/app/assets/javascripts/discourse/views/topic-map-container.js.es6
@@ -6,7 +6,10 @@ import DiscourseContainerView from 'discourse/views/container';
export default DiscourseContainerView.extend({
classNameBindings: ['hidden', ':topic-map'],
- shouldRerender: Discourse.View.renderIfChanged('topic.posts_count'),
+
+ _postsChanged: function() {
+ Ember.run.once(this, 'rerender');
+ }.observes('topic.posts_count'),
hidden: function() {
if (!this.get('post.firstPost')) return true;
diff --git a/app/assets/javascripts/discourse/views/view.js b/app/assets/javascripts/discourse/views/view.js
index e73dee4f2..c62af2582 100644
--- a/app/assets/javascripts/discourse/views/view.js
+++ b/app/assets/javascripts/discourse/views/view.js
@@ -40,16 +40,8 @@ Discourse.View.reopenClass({
});
},
- /**
- Returns an observer that will re-render if properties change. This is useful for
- views where rendering is done to a buffer manually and need to know when to trigger
- a new render call.
-
- @method renderIfChanged
- @params {String} propertyNames*
- @return {Function} observer
- **/
renderIfChanged: function() {
+ Em.warn("`rerenderIfChanged` is deprecated. Use the `StringBuffer` mixin with `rerenderTriggers` instead.");
var args = Array.prototype.slice.call(arguments, 0);
args.unshift(function () {
Ember.run.once(this, 'rerender');
diff --git a/test/javascripts/views/view-test.js.es6 b/test/javascripts/views/view-test.js.es6
index 1595c9bc7..f9ea42dfe 100644
--- a/test/javascripts/views/view-test.js.es6
+++ b/test/javascripts/views/view-test.js.es6
@@ -30,47 +30,3 @@ test("registerHelper: enables embedding a child view in a parent view via dedica
equal(parentView.$("#child").length, 1, "child view registered as helper is appended to the parent view");
equal(parentView.$("#child").text(), "foo", "child view registered as helper gets parameters provided during helper invocation in parent's template");
});
-
-test("renderIfChanged: rerenders the whole view template when one of registered view fields changes", function() {
- var view, rerenderSpy;
-
- var viewRerendersOnceWhen = function(message, changeCallback) {
- rerenderSpy.reset();
- Ember.run(function() { changeCallback(); });
- ok(rerenderSpy.calledOnce, "view rerenders when " + message);
- };
-
- var viewDoesNotRerenderWhen = function(message, changeCallback) {
- rerenderSpy.reset();
- Ember.run(function() { changeCallback(); });
- ok(!rerenderSpy.called, "view does not rerender when " + message);
- };
-
-
- view = Ember.View.extend({
- shouldRerender: Discourse.View.renderIfChanged("simple", "complex.@each.nested")
- }).create({
- simple: "initial value",
- complex: [Ember.Object.create({nested: "initial value"})],
- unregistered: "initial value"
- });
-
- rerenderSpy = sinon.spy(view, "rerender");
-
- Ember.run(function() {
- view.appendTo("#qunit-fixture");
- });
-
-
- viewRerendersOnceWhen("a simple field (holding a string) changes", function() {
- view.set("simple", "updated value");
- });
-
- viewRerendersOnceWhen("a nested sub-field of a complex field (holding an array of objects) changes", function() {
- view.get("complex").objectAt(0).set("nested", "updated value");
- });
-
- viewDoesNotRerenderWhen("unregistered field changes", function() {
- view.set("unregistered", "updated value");
- });
-});