mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
Merge pull request #6220 from ErisDS/issue-6205-limit
Add `limit` attribute to tags & foreach helpers
This commit is contained in:
commit
432f8610c8
4 changed files with 102 additions and 91 deletions
|
@ -3,6 +3,7 @@
|
|||
//
|
||||
// Block helper designed for looping through posts
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
errors = require('../errors'),
|
||||
|
||||
hbsUtils = hbs.handlebars.Utils,
|
||||
|
@ -15,9 +16,10 @@ foreach = function (context, options) {
|
|||
|
||||
var fn = options.fn,
|
||||
inverse = options.inverse,
|
||||
i = 0,
|
||||
columns = options.hash.columns,
|
||||
ret = '',
|
||||
length = _.size(context),
|
||||
limit = options.hash.limit || length,
|
||||
output = '',
|
||||
data,
|
||||
contextPath;
|
||||
|
||||
|
@ -50,53 +52,32 @@ foreach = function (context, options) {
|
|||
}
|
||||
}
|
||||
|
||||
ret = ret + fn(context[field], {
|
||||
output = output + fn(context[field], {
|
||||
data: data,
|
||||
blockParams: hbsUtils.blockParams([context[field], field], [contextPath + field, null])
|
||||
});
|
||||
}
|
||||
|
||||
function iterateArray(context) {
|
||||
var j;
|
||||
for (j = context.length; i < j; i += 1) {
|
||||
execIteration(i, i, i === context.length - 1);
|
||||
}
|
||||
}
|
||||
function iterateCollection(context) {
|
||||
var count = 1;
|
||||
|
||||
function iterateObject(context) {
|
||||
var priorKey,
|
||||
key;
|
||||
|
||||
for (key in context) {
|
||||
if (context.hasOwnProperty(key)) {
|
||||
// We're running the iterations one step out of sync so we can detect
|
||||
// the last iteration without have to scan the object twice and create
|
||||
// an itermediate keys array.
|
||||
if (priorKey) {
|
||||
execIteration(priorKey, i - 1);
|
||||
}
|
||||
priorKey = key;
|
||||
i += 1;
|
||||
_.each(context, function (item, key) {
|
||||
if (count <= limit) {
|
||||
execIteration(key, count - 1, count === length);
|
||||
}
|
||||
}
|
||||
if (priorKey) {
|
||||
execIteration(priorKey, i - 1, true);
|
||||
}
|
||||
count += 1;
|
||||
});
|
||||
}
|
||||
|
||||
if (context && typeof context === 'object') {
|
||||
if (hbsUtils.isArray(context)) {
|
||||
iterateArray(context);
|
||||
} else {
|
||||
iterateObject(context);
|
||||
}
|
||||
iterateCollection(context);
|
||||
}
|
||||
|
||||
if (i === 0) {
|
||||
ret = inverse(this);
|
||||
if (length === 0) {
|
||||
output = inverse(this);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return output;
|
||||
};
|
||||
|
||||
module.exports = foreach;
|
||||
|
|
|
@ -16,28 +16,33 @@ tags = function (options) {
|
|||
options = options || {};
|
||||
options.hash = options.hash || {};
|
||||
|
||||
var autolink = options.hash && _.isString(options.hash.autolink) && options.hash.autolink === 'false' ? false : true,
|
||||
separator = options.hash && _.isString(options.hash.separator) ? options.hash.separator : ', ',
|
||||
prefix = options.hash && _.isString(options.hash.prefix) ? options.hash.prefix : '',
|
||||
suffix = options.hash && _.isString(options.hash.suffix) ? options.hash.suffix : '',
|
||||
var autolink = !(_.isString(options.hash.autolink) && options.hash.autolink === 'false'),
|
||||
separator = _.isString(options.hash.separator) ? options.hash.separator : ', ',
|
||||
prefix = _.isString(options.hash.prefix) ? options.hash.prefix : '',
|
||||
suffix = _.isString(options.hash.suffix) ? options.hash.suffix : '',
|
||||
limit = options.hash.limit ? parseInt(options.hash.limit, 10) : undefined,
|
||||
output = '';
|
||||
|
||||
function createTagList(tags) {
|
||||
var tagNames = _.pluck(tags, 'name');
|
||||
|
||||
if (autolink) {
|
||||
return _.map(tags, function (tag) {
|
||||
return utils.linkTemplate({
|
||||
url: config.urlFor('tag', {tag: tag}),
|
||||
text: _.escape(tag.name)
|
||||
});
|
||||
}).join(separator);
|
||||
});
|
||||
}
|
||||
return _.escape(tagNames.join(separator));
|
||||
return _(tags).pluck('name').each(_.escape);
|
||||
}
|
||||
|
||||
if (this.tags && this.tags.length) {
|
||||
output = prefix + createTagList(this.tags) + suffix;
|
||||
output = createTagList(this.tags);
|
||||
|
||||
if (limit) {
|
||||
output = output.slice(0, limit);
|
||||
}
|
||||
|
||||
output = prefix + output.join(separator) + suffix;
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(output);
|
||||
|
|
|
@ -261,6 +261,15 @@ describe('{{#foreach}} helper', function () {
|
|||
});
|
||||
|
||||
describe('(compile)', function () {
|
||||
var objectHash = {posts: {
|
||||
first: {title: 'first'}, second: {title: 'second'}, third: {title: 'third'}, fourth: {title: 'fourth'}, fifth: {title: 'fifth'}
|
||||
}},
|
||||
arrayHash = {posts: [
|
||||
{title: 'first'}, {title: 'second'}, {title: 'third'}, {title: 'fourth'}, {title: 'fifth'}
|
||||
]},
|
||||
arrayHash2 = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'},
|
||||
objectHash2 = {goodbyes: {foo: {text: 'goodbye'}, bar: {text: 'Goodbye'}, baz: {text: 'GOODBYE'}}, world: 'world'};
|
||||
|
||||
function shouldCompileToExpected(templateString, hash, expected) {
|
||||
var template = handlebars.compile(templateString),
|
||||
result = template(hash);
|
||||
|
@ -269,92 +278,97 @@ describe('{{#foreach}} helper', function () {
|
|||
}
|
||||
|
||||
/** Many of these are copied direct from the handlebars spec */
|
||||
it('foreach with object and @key', function () {
|
||||
it('object and @key', function () {
|
||||
var templateString = '<ul>{{#foreach posts}}<li>{{@key}} {{title}}</li>{{/foreach}}</ul>',
|
||||
hash = {posts: {first: {title: 'first'}, second: {title: 'second'}}},
|
||||
expected = '<ul><li>first first</li><li>second second</li></ul>';
|
||||
expected = '<ul><li>first first</li><li>second second</li><li>third third</li><li>fourth fourth</li><li>fifth fifth</li></ul>';
|
||||
|
||||
shouldCompileToExpected(templateString, hash, expected);
|
||||
shouldCompileToExpected(templateString, objectHash, expected);
|
||||
});
|
||||
|
||||
it('foreach with @index', function () {
|
||||
it('@index', function () {
|
||||
var templateString = '<ul>{{#foreach posts}}<li>{{@index}} {{title}}</li>{{/foreach}}</ul>',
|
||||
hash = {posts: [{title: 'first'}, {title: 'second'}]},
|
||||
expected = '<ul><li>0 first</li><li>1 second</li></ul>';
|
||||
expected = '<ul><li>0 first</li><li>1 second</li><li>2 third</li><li>3 fourth</li><li>4 fifth</li></ul>';
|
||||
|
||||
shouldCompileToExpected(templateString, hash, expected);
|
||||
shouldCompileToExpected(templateString, arrayHash, expected);
|
||||
shouldCompileToExpected(templateString, objectHash, expected);
|
||||
});
|
||||
|
||||
it('foreach with @number', function () {
|
||||
it('@number', function () {
|
||||
var templateString = '<ul>{{#foreach posts}}<li>{{@number}} {{title}}</li>{{/foreach}}</ul>',
|
||||
hash = {posts: [{title: 'first'}, {title: 'second'}]},
|
||||
expected = '<ul><li>1 first</li><li>2 second</li></ul>';
|
||||
expected = '<ul><li>1 first</li><li>2 second</li><li>3 third</li><li>4 fourth</li><li>5 fifth</li></ul>';
|
||||
|
||||
shouldCompileToExpected(templateString, hash, expected);
|
||||
shouldCompileToExpected(templateString, arrayHash, expected);
|
||||
shouldCompileToExpected(templateString, objectHash, expected);
|
||||
});
|
||||
|
||||
it('foreach with nested @index', function () {
|
||||
it('nested @index', function () {
|
||||
var templateString = '{{#foreach goodbyes}}{{@index}}. {{text}}! {{#foreach ../goodbyes}}{{@index}} {{/foreach}}After {{@index}} {{/foreach}}{{@index}}cruel {{world}}!',
|
||||
hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'},
|
||||
expected = '0. goodbye! 0 1 2 After 0 1. Goodbye! 0 1 2 After 1 2. GOODBYE! 0 1 2 After 2 cruel world!';
|
||||
|
||||
shouldCompileToExpected(templateString, hash, expected);
|
||||
shouldCompileToExpected(templateString, arrayHash2, expected);
|
||||
shouldCompileToExpected(templateString, objectHash2, expected);
|
||||
});
|
||||
|
||||
it('foreach with block params', function () {
|
||||
it('array block params', function () {
|
||||
var templateString = '{{#foreach goodbyes as |value index|}}{{index}}. {{value.text}}! {{#foreach ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/foreach}} After {{index}} {{/foreach}}{{index}}cruel {{world}}!',
|
||||
hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}], world: 'world'},
|
||||
expected = '0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!';
|
||||
expected = '0. goodbye! 0 0 0 1 0 2 After 0 1. Goodbye! 1 0 1 1 1 2 After 1 2. GOODBYE! 2 0 2 1 2 2 After 2 cruel world!';
|
||||
|
||||
shouldCompileToExpected(templateString, hash, expected);
|
||||
shouldCompileToExpected(templateString, arrayHash2, expected);
|
||||
});
|
||||
|
||||
it('foreach with @first', function () {
|
||||
it('object block params', function () {
|
||||
var templateString = '{{#foreach goodbyes as |value index|}}{{index}}. {{value.text}}! {{#foreach ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/foreach}} After {{index}} {{/foreach}}{{index}}cruel {{world}}!',
|
||||
expected = 'foo. goodbye! foo foo foo bar foo baz After foo bar. Goodbye! bar foo bar bar bar baz After bar baz. GOODBYE! baz foo baz bar baz baz After baz cruel world!';
|
||||
|
||||
shouldCompileToExpected(templateString, objectHash2, expected);
|
||||
});
|
||||
|
||||
it('@first', function () {
|
||||
var templateString = '{{#foreach goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/foreach}}cruel {{world}}!',
|
||||
hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'},
|
||||
expected = 'goodbye! cruel world!';
|
||||
|
||||
shouldCompileToExpected(templateString, hash, expected);
|
||||
shouldCompileToExpected(templateString, arrayHash2, expected);
|
||||
shouldCompileToExpected(templateString, objectHash2, expected);
|
||||
});
|
||||
|
||||
it('foreach with nested @first', function () {
|
||||
it('nested @first', function () {
|
||||
var templateString = '{{#foreach goodbyes}}({{#if @first}}{{text}}! {{/if}}{{#foreach ../goodbyes}}{{#if @first}}{{text}}!{{/if}}{{/foreach}}{{#if @first}} {{text}}!{{/if}}) {{/foreach}}cruel {{world}}!',
|
||||
hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'},
|
||||
expected = '(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!';
|
||||
|
||||
shouldCompileToExpected(templateString, hash, expected);
|
||||
shouldCompileToExpected(templateString, arrayHash2, expected);
|
||||
shouldCompileToExpected(templateString, objectHash2, expected);
|
||||
});
|
||||
|
||||
it('foreach object with @first', function () {
|
||||
var templateString = '{{#foreach goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/foreach}}cruel {{world}}!',
|
||||
hash = {goodbyes: {foo: {text: 'goodbye'}, bar: {text: 'Goodbye'}}, world: 'world'},
|
||||
expected = 'goodbye! cruel world!';
|
||||
|
||||
shouldCompileToExpected(templateString, hash, expected);
|
||||
});
|
||||
|
||||
it('foreach with @last', function () {
|
||||
it('@last', function () {
|
||||
var templateString = '{{#foreach goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/foreach}}cruel {{world}}!',
|
||||
hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'},
|
||||
expected = 'GOODBYE! cruel world!';
|
||||
|
||||
shouldCompileToExpected(templateString, hash, expected);
|
||||
shouldCompileToExpected(templateString, arrayHash2, expected);
|
||||
shouldCompileToExpected(templateString, objectHash2, expected);
|
||||
});
|
||||
|
||||
it('foreach object with @last', function () {
|
||||
var templateString = '{{#foreach goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/foreach}}cruel {{world}}!',
|
||||
hash = {goodbyes: {foo: {text: 'goodbye'}, bar: {text: 'Goodbye'}}, world: 'world'},
|
||||
expected = 'Goodbye! cruel world!';
|
||||
|
||||
shouldCompileToExpected(templateString, hash, expected);
|
||||
});
|
||||
|
||||
it('foreach with nested @last', function () {
|
||||
it('nested @last', function () {
|
||||
var templateString = '{{#foreach goodbyes}}({{#if @last}}{{text}}! {{/if}}{{#foreach ../goodbyes}}{{#if @last}}{{text}}!{{/if}}{{/foreach}}{{#if @last}} {{text}}!{{/if}}) {{/foreach}}cruel {{world}}!',
|
||||
hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'},
|
||||
expected = '(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!';
|
||||
|
||||
shouldCompileToExpected(templateString, hash, expected);
|
||||
shouldCompileToExpected(templateString, arrayHash2, expected);
|
||||
shouldCompileToExpected(templateString, objectHash2, expected);
|
||||
});
|
||||
|
||||
it('foreach with limit 1', function () {
|
||||
var templateString = '<ul>{{#foreach posts limit="1"}}<li>{{title}}</li>{{else}}not this{{/foreach}}</ul>',
|
||||
expected = '<ul><li>first</li></ul>';
|
||||
|
||||
shouldCompileToExpected(templateString, arrayHash, expected);
|
||||
shouldCompileToExpected(templateString, objectHash, expected);
|
||||
});
|
||||
|
||||
it('foreach with limit 3', function () {
|
||||
var templateString = '<ul>{{#foreach posts limit="3"}}<li>{{title}}</li>{{else}}not this{{/foreach}}</ul>',
|
||||
expected = '<ul><li>first</li><li>second</li><li>third</li></ul>';
|
||||
|
||||
shouldCompileToExpected(templateString, arrayHash, expected);
|
||||
shouldCompileToExpected(templateString, objectHash, expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -109,4 +109,15 @@ describe('{{tags}} helper', function () {
|
|||
|
||||
String(rendered).should.equal('<a href="/tag/foo-bar/">foo</a>, <a href="/tag/bar/">bar</a>');
|
||||
});
|
||||
|
||||
it('can limit no. tags output to 1', function () {
|
||||
var tags = [{name: 'foo', slug: 'foo-bar'}, {name: 'bar', slug: 'bar'}],
|
||||
rendered = helpers.tags.call(
|
||||
{tags: tags},
|
||||
{hash: {limit: '1'}}
|
||||
);
|
||||
should.exist(rendered);
|
||||
|
||||
String(rendered).should.equal('<a href="/tag/foo-bar/">foo</a>');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue