According to the Iron-Router Documentation, this is how you can handle routing when trying to route to a page that has no data associated with it.
if Meteor.isClient
Router.onBeforeAction('dataNotFound')
Router.map ->
#route 'chat',
path: '/chat/:room_name'
notFoundTemplate: 'home'
data: ->
Rooms.findOne({room_name: #params.room_name})
My code specifically is designed to redirect to the home page when a specific chat room is not defined. It works as expected with one very annoying issue. There is a lot of page flickering going on. The hooks seems to be rendering the home page first than following through with the logic to land you on the right page.
So when visiting a chat room that does exists, it quickly renders the home page for a split second, than loads the room. When visiting a chat room that doesn't exist, it is rendering the home page, than quickly re-rendering it. In both cases there is a flicker happening that makes these pages very annoying to use.
Am I just doing this in the wrong way? Or is there a better way to avoid the flickering?
EDIT: This only happens on a hard page reload
Here is my updated code based on the first answer, but I am having an issue where every room_name is being rendered and the ones that dont exist aren't being redirected to home
Routes: Which exists in the root level outside of client/server folders
if Meteor.isClient
Router.onBeforeAction('dataNotFound')
Router.map ->
roomExists = undefined
#route 'chat',
path: '/chat/:room_name'
notFoundTemplate: 'home'
onBeforeAction: ->
#subscribe('rooms').wait()
data: ->
Rooms.findOne({room_name: #params.room_name})
Server Code
Meteor.publish 'rooms', () ->
Room.find({})
You could also subscribe in the helper using the following pattern:
Template.someTemplate.helper = function() {
var ready = Meteor.subscribe('somePublish').ready();
var data = collection.find({_id: this.id});
return {
ready: ready,
data: data
};
};
In the template html you would then do this:
{{#with helper}}
{{#if ready}}
{{Dostuff with your data}}
{{else}}
{{>spinner}} Loading...
{{/if}}
{{/with}}
Using the subscribes in the router stops the whole page from loading, while in most cases only a few sections of that page actually depend on that subscription being ready.
Possible reason for that behavior is the subscription not being ready when the page is rendered the first time. The fix is to move your subscriptions to the router and wait on them:
Router.map ->
#route 'chat',
path: '/chat/:room_name'
notFoundTemplate: 'home'
onBeforeAction: ->
#subscribe('chatRooms').wait()
data: ->
Rooms.findOne({room_name: #params.room_name})
Related
Currently I have a form called a "form A", I've created a number of views for this form, an edit, create and list. I want to be able to create a form called "form B", that will pull through "form A's ID" as a parent ID. To enable me to have a list of all form B under a "form A" (essential a one to many relationship). So at the moment under my list for "form A" I have this link for each item:
{{#link-to 'form-a.form-b.listing' form-a.id}} <!--Link to a controller -->
<span class="btn btn-primary"> form b </span>
{{/link-to}}
Here is also my router
this.route('form-a', function() {
this.route('new');
this.route('listing');
this.route('edit', {path: '/edit/:form-a_id' });
this.route('form-b', function() {
this.route('listing', {path: '/listing/:form-a_id'});
this.route('new');
});
});
So I'm trying to pull through the Id of form-a for the listing. However I'm kind of stuck here as I'm unsure how what to do next and how to correctly pull through "form a" Id in the listing controller and the use it as a parent ID for each "form B". Is there a better way to have nested forms with one too many relationships or am I going about it in the best way?
I hope someone can help as this issue as I have hit the coding wall. If you need any clarifications please ask as I know I'm usually terrible at describing my issues.
This post applies to Ember 2.x.x and was written as of 2.15.
I think what will help you out a lot is paramsFor().
It's hard to say what the "right" routing structure because your UI will dictate things somewhat, and I'm not sure how much exact URLs matter.
Here's how I would set up the routes, assuming there will be multiple form a's in time.
this.route('form-as', function() {
this.route('form-a', {path: '/:form-a_id'}, function() {
this.route('new');
this.route('listing');
this.route('edit');
this.route('form-b', function() {
this.route('listing', {path: '/listing'});
this.route('new');
});
});
});
In the code above, new, listing, and edit under form a will have access to the form a id via the params in the model hook of the routes:
model(params) {
console.log(params['form-a_id'])
// using the [] accessor since dashes don't work for normal dictionary nav
}
In the form b segment, listing and new can get access to the form-a parameters like this:
model() {
console.log(this.paramsFor('form-as.form-a'))
}
Watch out for those dasherized ids and model names. They are a likely source of bugs and I'm not 100% sure I got them right here. I avoid them.
For more about paramsFor(), see Reusing Route Context in the Guides and Dynamic Segments
Say I wrote a blog app in Sails.js.
On every page in this app, there is a sidebar widget called "Recent Posts", where it lists the titles of the 5 most recent posts and clicking on them takes you to the post in question.
Because this sidebar widget is present on every page, it should be in layout.ejs. But, here we have a conflict - dynamic content is only supposed to be pulled from the database in the controller action for rendering a specific view.
This dynamic content isn't for a specific view, it's for the whole site (via layout.ejs).
By the conventions that I understand, I'd have to get that dynamic content data for the sidebar widget in every controller action that renders a view (otherwise I would get an undefined error when I attempt to call that local in my layout.ejs file).
Things I've tried / considered:
Load that dynamic content in every controller action that renders a view (this solution is very bad) and calling that dynamic content in layout.ejs as if it were a local for the specific view. This works fine, but goes against D.R.Y. principles and quite frankly is a pain in the ass to have to run the same query to the database in every controller action.
As per another similar stackoverflow question, create a new config (E.G. config/globals.js), load my dynamic content from my database into that config file as a variable, and then calling sails.config.globals.[variable_name] in my layout.ejs file. This also worked, since apparently config variables are available everywhere in the application -- but it 's a hacky solution that I'm not a fan of (the content I'm loading is simply the titles and slugs of 5 recent posts, not a "global config option", as the solution implies).
Run the query to get the dynamic content inside the .EJS file directly between some <% %> tags. I'm not sure if this would work, but even if it did, it goes against the separation of concerns MVC principle and I'd like to avoid doing this if at all possible (if it even works).
As per a lengthy IRC discussion # http://webchat.freenode.net/?channels=sailsjs, it was suggested to create a policy and map that policy to all my controllers. In that policy, query the database for the 5 most recent posts, and set them to the req.recentposts. The problem with this solution is that, while the recent posts data will be passed to every controller, I still have to pass that req.recentposts data to my view -- making it so I still have to modify every single res.view({}) in every action. I don't have to have the database query in every action, which is good, but I still have to add a line of code to every action that renders a view... this isn't D.R.Y. and I'm looking for a better solution.
So, what is the proper solution, without needing to load that dynamic content in every controller action (a solution that adheres to D.R.Y. is what I'm lookng for), to get some dynamic content available to my layout.ejs file?
In folder /config you should create a file express.js and add something like that:
module.exports.express = {
customMiddleware: function(app){
app.use(function(req, res, next){
// or whatever query you need
Posts.find().limit(5).exec(function(err, posts){
res.locals.recentPosts = posts;
// remember about next()
next();
});
});
}
}
Then just make some simple loop in your view:
<% for(var i=0; i<recentPosts.length; i++) { %>
<% recentPosts[i].title %>
<% } %>
Here are some links to proper places in documentation:
https://github.com/balderdashy/sails-docs/blob/0.9/reference/Configuration.md#express
and
https://github.com/balderdashy/sails-docs/blob/0.9/reference/Response.md#reslocals
I found out another way to do this. What I did was to create a service that could render .ejs files to plain html by simply taking advantage of the ejs library already in sails. This service could either be invoked by the controller, or even passed as a function in the locals, and executed from within the .ejs. The service called TopNavBarService would look like:
var ejs = require('ejs');
exports.render = function() {
/* database finds goes here */
var userInfo = {
'username' : 'Kallehopp',
'real_name' : 'Kalle Hoppson'
};
var html = null;
ejs.renderFile('./views/topNavBar.ejs', {'locals':userInfo}, function(err, result) { html = result; });
return html;
}
In the constroller it could look like:
module.exports = {
testAction: function (req, res) {
return res.view('testView', {
renderNavbar: TopNavBarService.render // service function as a local!
});
}
};
This way you can create your customized ejs-helper that could even take arguments (although not shown here). When invoked, the helper could access the database and render a part of the html.
<div>
<%- renderNavbar() %>
</div>
I am trying to be as lazy loading as possible,
But, I am puzzle on how to start, here is my "sequenced comprehension":
Objective: Create a contacts page with contacts existing on the server
Step1.0: To use the router: the <div id="contacts"> must exists to trigger a rule, so I stepped back to (Step0.9),
Step0.9: Created this div in the body. Fine, the router find the #contacts, Oh, but this is a view, ok, stepped back to (Step0.8).
Step0.8: Erase the div created in the body and replace it by a view instead:
contactsView = Backbone.View.extend
tagName: 'div',
id: 'contacts'
To be lazy loading, this view should only be created when the #contact is trigger in my router table, but I just removed it from by body, it does exist anymore, I am back to Step1.0 ???
Some tutorials found, shows global variable settings... Please, how the general scenario using a router, a view, their models, and collection should proceed (no code is necessary for an answer, just one line for each steps) ?
I know there can be multiples ways, but what is the most common backbone step strategy to create elements.
Thanks in advance.
I'm not 100% sure I understood you correctly. If I didn't please let me know in the comments.
There seems to be some confusion in your question regarding the usage of Backbone.Router in general. When the router maps a route to URL fragment #contacts, that has nothing to do with a DOM element with the id #contacts. The hash sign simply happens to be the identifier for an URL fragment and id CSS selector, but that's where the similarity ends.
Typically my router looks something like this:
var AppRouter = Backbone.Router.extend({
routes: {
contacts: "contactList"
},
contactList: function() {
var contacts = new ContactCollection();
var view = new ContactListView({collection:contacts});
view.render().$el.appendTo("#contacts");
}
});
Notice that the #contacts element doesn't need to be called that. You can call it #pony, or you can render the view directly to the document body if you want.
So in these terms the workflow is:
Router gets hit
Collection is initialized
View is rendered
Usual way i do is
Have the div#contacts loaded within body
Router maps the #contacts to the method showContacts in the router
showContacts creates the view, attaches it to the desired div
var view = new contactsView();
$('#contacts').empty().append(view.el);
view.render();
You need not define the id in the definition of contactsView
I have some input fields inside a modal dialog like so (that's jade):
.modal-body
.control-group
label.required(for="event-planner-email")= Email
.input
input#event-planner-email(name="email", type='text')
.control-group
label.required(for="event-planner-name")= Name
.input
input#event-planner-name(name="name", type='text')
And I would like to be able to fill them out in a test. I tried pressing the link that opens the modal and filling the fields using zombie.js browser object, code in coffee follows:
browser.clickLink 'a#open-planner', () ->
browser
.fill('event-planner-email', someEmail)
.fill('event-planner-name', someName)
.pressButton 'Create', () ->
do done
But I get an error saying: "No INPUT matching 'event-planner-email'". If anyone knows how to do this, help would be much appreciated. Thanks!
Maybe the modal has not come into view yet by the time your call back is firing. If there is a transition happening to show the modal, you'll have to wait for that transition to complete.
Maybe try adding a setTimeout in your call back of the clickLink:
browser.clickLink 'a#open-planner', ->
fillForm = ->
browser
.fill('event-planner-email', someEmail)
.fill('event-planner-name', someName)
.pressButton 'Create', ->
do done
setTimeout fillForm, 1000
From the Bootstrap docs
You need to run your zombie code after the modal loads, so you should use the "shown" event
$('#myModal').on('shown', function () {
// zombie
})
I'm not sure how to represent that in coffee
Thanks everyone, turns out I just didn't use the proper css selector for the input elements, so instead of
.fill('event-planner-email', someEmail)
should be
.fill('input#event-planner-email', someEmail)
Because in jade snippet above, 'event-planner-email' is the id of the input element. Wanted to delete my question, so that I don't expose my stupidity any further, but apparently it's too late.
I'm using jquery-ui tab example to add extra tabs. I changed that code to be able to add tabs that load a form via Ajax. I was able to create that just changing these:
var $tabs = $( "#tabs").tabs({
cache: true,
tabTemplate: "<li><a href='formularioAgricola.php' id='#{label}'>#{label}</a> <span class='ui-icon ui-icon-close'>Remove Tab</span></li>"
//ajaxOptions: a
});
So I changed the tabTemplate to load the same Form always.
My problem is that I'm not sure how to retrieve, either to tell that every tag from that form use jquery-ui stuff, like buttons, datepickers, etc.
In a regular form I would do something like:
$("#btnRevisar").button()
But when we talk about form load via Ajax it is different.
and also, how can I try to differ one form from other one, if they are all named with the same name, is it possible?
Thanks guys
Carlos.
Within the tabs docs page, tab titled "Events" there is a "load" event. The "ui" argument gives you access to an object that includes the current panel that is loaded. If you are using same ID on forms, beware that ID's must be unique in a page.
var $tabs = $( "#tabs").tabs({
cache: true,
tabTemplate: "<li><a href='formularioAgricola.php' id='#{label}'>#{label}</a> <span class='ui-icon ui-icon-close'>Remove Tab</span></li>",
/* add new option for load event*/
load: function( event, ui){
var $currTabContentPanel=$(ui.panel);
/* only look in currently loaded content for formClass*/
$currTabContentPanel.find('.formClass').doSomething()
}
});