Skip to content

Commit a3ff2fa

Browse files
committed
v2.12.1
- Update FR to v2.12.1 - Fix issue when template was rendered twice within waitOn - Add build-in `this.render(layout, template, data)` method - Add `yield` feature (*see templating section in docs*) - Within `yield` feature now this package has 100% templating compatibility with Iron-Router
1 parent 3202a11 commit a3ff2fa

File tree

9 files changed

+261
-50
lines changed

9 files changed

+261
-50
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2016, dr.dimitru (Veliov Group LLC.)
1+
Copyright (c) 2016, dr.dimitru (Veliov Group, LLC)
22
All rights reserved.
33

44
Redistribution and use in source and binary forms,

README.md

Lines changed: 112 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ FlowRouter Extra:
1717
* [data hook](https://github.com/VeliovGroup/flow-router#data-hook) - Fetch data from collection before render router's template
1818
* [onNoData hook](https://github.com/VeliovGroup/flow-router#onnodata-hook) - Do something if "*data hook*" returns falsy value
1919
* [Data in other hooks](https://github.com/VeliovGroup/flow-router#data-in-other-hooks) - Use fetched data in other hooks
20+
* [Render Template](https://github.com/VeliovGroup/flow-router#render-template) - Render template into layout
21+
* [Templating](https://github.com/VeliovGroup/flow-router#templating) - Construct your layout and templates
2022
* [Suggested usage](https://github.com/VeliovGroup/flow-router#suggested-usage) - Bootstrap router's configuration
2123
* [Other packages compatibility](https://github.com/VeliovGroup/flow-router#other-packages-compatibility) - Best packages to be used with flow-router-extra
2224

@@ -59,7 +61,7 @@ FlowRouter.route('/post/:_id', {
5961
return [Meteor.subscribe('post', params._id)];
6062
},
6163
whileWaiting: function (params, queryParams) {
62-
BlazeLayout.render('_layout', {content: '_loading'});
64+
this.render('_layout', '_loading');
6365
}
6466
});
6567
```
@@ -73,7 +75,7 @@ FlowRouter.route('/post/:_id', {
7375
return [Meteor.subscribe('post', params._id)];
7476
},
7577
whileWaiting: function () {
76-
BlazeLayout.render('_layout', {content: '_loading'});
78+
this.render('_layout', '_loading');
7779
},
7880
data: function (params, queryParams) {
7981
return PostsCollection.findOne({_id: params._id});
@@ -85,8 +87,8 @@ When you having `data` hook in a route, - returned data will be passed to `actio
8587
```javascript
8688
FlowRouter.route('/post/:_id', {
8789
name: 'post',
88-
action: function (params, queryParams, data) {
89-
BlazeLayout.render('_layout', {content: 'post', post: data});
90+
action: function (params, queryParams, post) {
91+
this.render('_layout', 'post', {post: post});
9092
},
9193
waitOn: function (params) {
9294
return [Meteor.subscribe('post', params._id)];
@@ -116,7 +118,7 @@ FlowRouter.route('/post/:_id', {
116118
return PostsCollection.findOne({_id: params._id});
117119
},
118120
onNoData: function (params, queryParams){
119-
BlazeLayout.render('_layout', {content: '_404'});
121+
this.render('_layout', '_404');
120122
}
121123
});
122124
```
@@ -138,25 +140,127 @@ FlowRouter.route('/post/:_id', {
138140
});
139141
```
140142

143+
### Render Template
144+
*Instead of BlazeLayout, you can use build-in * `this.render()` *method*. Use it in context of `action`, `onNoData`, `whileWaiting`, `data`, `waitOn` or any other hook.
145+
146+
`this.render(layout, template [, data])`
147+
- `layout` {*String*} - Name of layout template (*which has * `yield`)
148+
- `template` {*String*} - Name of template (*which will be rendered into yield*)
149+
- `data` {*Object*} - [Optional] Object of data context to use in template. *This object supports reactive data sources, but only when handled by "yielded" template, not nested templates, otherwise use template helpers*
150+
151+
### Templating
152+
*In order to use build-in* `this.render()` *method, layout template must contain* `yield` *placeholder*
153+
```html
154+
<!-- layout.html: -->
155+
<head>
156+
<meta charset="UTF-8" />
157+
<meta name="fragment" content="!" />
158+
<!-- ... -->
159+
<title>My Title</title>
160+
</head>
161+
162+
<template name="_layout">
163+
<header>
164+
{{> header}}
165+
</header>
166+
167+
<section>
168+
{{> yield}}
169+
</section>
170+
171+
<footer>
172+
{{> footer}}
173+
</footer>
174+
</template>
175+
176+
<template name="header">
177+
<h1>My Page</h1>
178+
</template>
179+
180+
<template name="footer">
181+
<p><!-- ... --></p>
182+
</template>
183+
```
184+
185+
```html
186+
<!-- posts.html: -->
187+
<template name="posts">
188+
<ul>
189+
{{#each post in posts}}
190+
<li>
191+
<a href="/post/{{post._id}}">{{post.title}}</a>
192+
</li>
193+
{{/each}}
194+
</ul>
195+
</template>
196+
```
197+
198+
```html
199+
<!-- post.html: -->
200+
<template name="post">
201+
{{#with post}}
202+
<h3>{{title}}</h3>
203+
<article>
204+
{{{text}}}
205+
</article>
206+
{{/with}}
207+
</template>
208+
```
209+
210+
```javascript
211+
// routes.js
212+
FlowRouter.route('/posts', {
213+
name: 'posts',
214+
action: function (params, queryParams, posts) {
215+
this.render('_layout', 'posts', posts);
216+
},
217+
waitOn: function () {
218+
return [Meteor.subscribe('posts')];
219+
},
220+
data: function () {
221+
return PostsCollection.find({});
222+
}
223+
});
224+
225+
FlowRouter.route('/post/:_id', {
226+
name: 'posts',
227+
action: function (params, queryParams, post) {
228+
this.render('_layout', 'post', post);
229+
},
230+
waitOn: function (params) {
231+
return [Meteor.subscribe('post', params._id)];
232+
},
233+
data: function (params) {
234+
return PostsCollection.findOne({_id: params._id});
235+
}
236+
});
237+
```
238+
141239
### Suggested usage
142240
As example we took simple post route:
143241
```javascript
242+
// meteorhacks:subs-manager package
243+
var subsManager = new SubsManager();
244+
144245
FlowRouter.route('/post/:_id', {
145246
name: 'post',
146247
action: function (params, queryParams, data) {
147248
// Pass data to template's context
148249
// No need to create helpers
149-
BlazeLayout.render('_layout', {content: 'post', post: data});
250+
this.render('_layout', 'post', {post: data});
150251
},
151252
waitOn: function (params) {
152253
// meteorhacks:subs-manager package
153254
return [subsManager.subscribe('post', params._id)];
154255
},
256+
whileWaiting: function () {
257+
this.render('_layout', '_loading');
258+
},
155259
data: function (params) {
156260
return PostsCollection.findOne({_id: params._id});
157261
},
158262
onNoData: function (){
159-
BlazeLayout.render('_layout', {content: '_404'});
263+
this.render('_layout', '_404');
160264
},
161265
// ostrio:flow-router-title package
162266
title: function (params, queryParams, post) {
@@ -167,7 +271,7 @@ FlowRouter.route('/post/:_id', {
167271
FlowRouter.notFound = {
168272
title: '404: Page not found',
169273
action: function () {
170-
BlazeLayout.render('_layout', {content: '_404'});
274+
this.render('_layout', '_404');
171275
}
172276
};
173277

File renamed without changes.

client/renderer.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
BlazeRenderer = (function() {
2+
function BlazeRenderer(rootEl) {
3+
this.rootEl = rootEl;
4+
this.old = null;
5+
this.reactTemplate = new ReactiveVar(null);
6+
this.current = {
7+
layout: null,
8+
template: null
9+
};
10+
11+
if (!rootEl || !_.isFunction(rootEl)) {
12+
throw new Meteor.Error(400, 'You must pass function into BlazeRenderer constructor, which returns DOM element');
13+
}
14+
}
15+
16+
BlazeRenderer.prototype.render = function(layout, template, data) {
17+
var _data, _template, _layout;
18+
19+
_template = typeof Template !== "undefined" && Template !== null ? Template[template] : void 0;
20+
_layout = typeof Template !== "undefined" && Template !== null ? Template[layout] : void 0;
21+
22+
if (!_template) {
23+
throw new Meteor.Error(404, 'No such template: ' + template);
24+
}
25+
26+
if (!_layout) {
27+
throw new Meteor.Error(404, 'No such layout: ' + layout);
28+
}
29+
30+
if (_layout) {
31+
_data = {
32+
___content: this.reactTemplate
33+
};
34+
35+
if (data) {
36+
_data = _.extend(_data, data);
37+
}
38+
39+
if (this.current.template === template) {
40+
var self = this;
41+
this.reactTemplate.set(null);
42+
Meteor.setTimeout(function () {
43+
self.reactTemplate.set(template);
44+
}, 1);
45+
} else {
46+
this.reactTemplate.set(template);
47+
}
48+
49+
if (this.current.layout !== layout) {
50+
if (this.old) {
51+
Blaze.remove(this.old);
52+
}
53+
54+
var getData = function getData () {
55+
return _data;
56+
};
57+
this.old = Blaze.renderWithData(_layout, getData, this.rootEl());
58+
} else {
59+
this.old.dataVar.set(_data);
60+
}
61+
62+
this.current.layout = layout;
63+
this.current.template = template;
64+
}
65+
};
66+
67+
return BlazeRenderer;
68+
})();

client/route.js

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ Route = function(router, pathDef, options, group) {
22
options = options || {};
33

44
this.options = options;
5+
6+
this.render = router.Renderer.render.bind(router.Renderer);
57
this.pathDef = pathDef;
68

79
// Route.path is deprecated and will be removed in 3.0
@@ -64,39 +66,46 @@ Route.prototype.checkSubscriptions = function(subscriptions) {
6466
Route.prototype.waitOn = function(current, next) {
6567
var self = this, subscriptions, timer, _data;
6668
if (self._waitOn) {
67-
if (!current) { current = {}; }
68-
self._whileWaiting && self._whileWaiting(current.params, current.queryParams);
69-
subscriptions = self._waitOn(current.params, current.queryParams);
70-
timer = Meteor.setInterval(function(){
71-
if (self.checkSubscriptions(subscriptions)) {
72-
Meteor.clearInterval(timer);
73-
if (self._data) {
74-
_data = self._data(current.params, current.queryParams);
75-
if (_data) {
76-
self._currentData = _.clone(_data);
69+
var wait = function () {
70+
timer = Meteor.setTimeout(function(){
71+
if (self.checkSubscriptions(subscriptions)) {
72+
Meteor.clearTimeout(timer);
73+
if (self._data) {
74+
_data = self._currentData = self._data(current.params, current.queryParams);
7775
}
76+
next(current, _data);
77+
} else {
78+
wait();
7879
}
79-
next(current, _data);
80-
}
81-
}, 25);
80+
}, 25);
81+
};
82+
83+
if (!current) { current = {}; }
84+
subscriptions = self._waitOn(current.params, current.queryParams);
85+
if (!self.checkSubscriptions(subscriptions)) {
86+
self._whileWaiting && self._whileWaiting(current.params, current.queryParams);
87+
}
88+
wait();
8289
} else {
8390
next(current);
8491
}
8592
};
8693

8794
Route.prototype.callAction = function(current) {
8895
var self = this;
89-
if (self._waitOn) {
90-
self.waitOn(current, function(current, data){
91-
if (self._onNoData && !data) {
92-
self._onNoData(current.params, current.queryParams);
93-
} else {
94-
self._action(current.params, current.queryParams, data);
95-
}
96-
});
9796

97+
if (!self._waitOn && self._data) {
98+
self._currentData = self._data(current.params, current.queryParams);
99+
}
100+
101+
if (self._data) {
102+
if (self._onNoData && !self._currentData) {
103+
self._onNoData(current.params, current.queryParams);
104+
} else {
105+
self._action(current.params, current.queryParams, self._currentData);
106+
}
98107
} else {
99-
self._action(current.params, current.queryParams);
108+
self._action(current.params, current.queryParams, self._currentData);
100109
}
101110
};
102111

0 commit comments

Comments
 (0)