I want to have a url like this /restaurants/:pageNumber and I want /restaurants to assume the pageNumber parameter is 1.
Here is my Router.js :
Router.map(function() {
this.route('restaurants', function() {});
this.route('restaurants', { path: '/restaurants/:pageNumber' }, function() {});
});
If it remove the function() {} for it, I just get a blank page with no errors in the console for /restaurants/1
My routes/restaurants/index.js :
export default Ember.Route.extend({
ajax: Ember.inject.service(),
model(params) {
return Ember.RSVP.hash({
response: this.get('ajax').request('/getAllRestaurants', {method: 'GET'}),
currentPage: params.pageNumber | 1
});
}
});
On the templates/restaurants/index.hbs I check {{model.currentPage}} and it's always 1.
Because logical OR is ||, not |. page = params.pageNumber || 1. But more reliable is ternary operator, page = (params.pageNumber !== undefined ? params.pageNumber : 1)
Did you try { path: '/restaurants/:page_number' } and
currentPage: params.page_number || 1 ?
Related
I was wondering if I can do the following with a ternary operator instead of if-else.
if (myPath === '/') {
next({ path: '/articles', query: { page: 1 } });
} else {
next();
}
What I want to do is to replace all of that with just one line:-
next(myPath === '/' ? { path: '/articles', query: { page: 1 } } : nothing);
If I'm going in the right direction then it just mainly boils down to passing 'nothing' to the next function. This is a code sample from the VueJS router navigation guard by the way if it's any help.
Is something like this possible?
I need to match a route like this: /route*
I mean, one that matches /route1, /route2, /route999, /routewhatever
I don't want to match /route/whatever. I want that if the user puts anything after the main name it's ignored.
/route* didn't work. Is this possible ?
the only way I see is to use a UrlMatcher
export function wildCardMatcher(url: UrlSegment[]) {
return url.length === 1 && url[0].path.startsWith('route') ? ({consumed: url}) : null;
}
and then
{ matcher: wildCardMatcher, component: HomeViewComponent,}
here's a demo
UrlMatcher in Angular lets you define your own function to match a url to a route.
You could do something like
function wildCardMatcher(url: UrlSegment[]) {
return url[0].path.slice(0,5) === 'route' ? ({consumed: url}) : null;
}
routes = [{
matcher: wildCardMatcher,
component: Whatever
}]
https://angular.io/api/router/UrlMatcher
I keep hitting this error and am pretty stumped as to why. I can't trace the error in dev tools back to anything in my primary JS file. My Ember-related code is as follow:
var App = Ember.Application.create();
App.ApplicationRoute = Ember.Route.extend({
model: function() {
var url = 'http://www.json-generator.com/api/json/get/bMRERKrXfS';
return Ember.$.getJSON(url).then(function(data) {
model = {
list1: data
};
return model;
});
}
});
Ember.Handlebars.helper('autocomplete', Ember.View.extend({
templateName: 'controls/autocomplete',
filteredList: function() {
...
}
return list.filter(function(item) {
...
});
}.property('list.#each', 'filter')
}));
I have removed app-specific code from the structure there. Any time I try to run this I get the following error:
Ember Cannot read property 'path' of undefined which looks to be coming from my ember.min.js file. Any help tracking this down would be great.
Codepen Demo Link
EDIT
When running this code locally and not in CodePen I end up with the following errors:
Uncaught TypeError: Cannot read property 'extend' of undefined which looks to be thrown by Ember.View.extend
and
Uncaught TypeError: Cannot read property 'isHelperFactory' of undefined which is coming from the ember.min file.
EDIT 2
I have tried updating the helper to an Ember component:
App.AutoCompleteComponent = Ember.Component.extend({
filteredList: function() {
var list = this.get('list'),
filter = this.get('filter'),
filterArr = [],
filterArrLength;
if (filter && filter.indexOf(', ') !== -1) {
filterArr = filter.split(', ');
filterArrLength = filterArr.length;
filter = filterArr[filterArrLength - 1]
}
if (!filter || (filterArrLength > 0 && filterArr[filterArrLength - 1].length == 0)) {
$("#filter-results").hide();
INPUT.unbindKeys();
return;
} else {
if (!$("#filter-results").is(":visible")) {
INPUT.bindKeys();
$("#filter-results").show();
}
}
return list.filter(function(item) {
if (!filterArrLength) {
return item.title.toLowerCase().startsWith(filter);
} else {
return item.title.toLowerCase().startsWith(filterArr[filterArrLength - 1]);
}
});
}.property('list.#each', 'filter')
});
And I have updated my Handlebars template to be "components/auto-complete"
Still, with this I am receiving the same errors. My CodePen link has also been updated for review.
Ember.View is deprecated. See the new docs about writing helpers.
However based on the functionality described and because you are providing a template I suspect that what you want is a component:
// app/components/auto-complete.js
import Ember from 'ember';
export default Ember.Component.extend({
filteredList: ...
});
The template is automatically found by Ember, in this case at templates/components/auto-complete.hbs.
Edit to your Edit 2: Note that defining objects on App is now deprecated.
I am try to publish posts created by the usernames in the following array each users has. I am adding this to the microscope practice app The error is I20140826-20:31:53.452(-4)? at Meteor.publish.Comments.find.postId [as _handler] (app/server/publications.js:2:13). Thanks in advanced here is the code.
publications.js
The loop is supposed to publish posts made by each username in the following array.
Meteor.publish('posts', function(options) {
for (u=0;u<this.user.profile.following.length;u++) {
f=profile.following[u].text();
return Posts.find({username:f}, options);
}
});
The Routes it will affect
Controllers
PostsListController = RouteController.extend({
template: 'postsList',
increment: 5,
limit: function() {
return parseInt(this.params.postsLimit) || this.increment;
},
findOptions: function() {
return {sort: this.sort, limit: this.limit()};
},
waitOn: function() {
return Meteor.subscribe('posts', this.findOptions());
},
posts: function() {
return Posts.find({}, this.findOptions());
},
data: function() {
var hasMore = this.posts().count() === this.limit();
return {
posts: this.posts(),
nextPath: hasMore ? this.nextPath() : null
};
}
});
NewPostsListController = PostsListController.extend({
sort: {submitted: -1, _id: -1},
nextPath: function() {
return Router.routes.newPosts.path({postsLimit: this.limit() + this.increment})
}
});
BestPostsListController = PostsListController.extend({
sort: {votes: -1, submitted: -1, _id: -1},
nextPath: function() {
return Router.routes.bestPosts.path({postsLimit: this.limit() + this.increment})
}
});
Router map
this.route('newPosts', {
path: '/new/:postsLimit?',
controller: NewPostsListController
});
this.route('bestPosts', {
path: '/best/:postsLimit?',
controller: BestPostsListController
});
Your publish function is wrong, are you aware that your loop is useless because it is exiting upon encountering the first return ?
Even if you were aggregating the cursors accumulated in the loop this wouldn't work because at the moment a publish function can only return multiple cursors from DIFFERENT collections.
You need to use the appropriate mongo selector here, which is probably $in.
Also, profile.following is not even defined in the publish function, and iterating over an array is done by checking the iterator variable against the array length (profile.following.length), or better yet using Array.prototype.forEach or _.each.
I think this is what you're trying to do :
Meteor.publish("posts",function(options){
if(!this.userId){
this.ready();
return;
}
var currentUser=Meteor.users.findOne(this.userId);
return Posts.find({
username:{
$in:currentUser.profile.following
}
},options);
});
You should definitely read resources about JavaScript itself before digging any further in Meteor, if you're following the Discover Meteor book I think they provide some good JS tutorials for beginners.
Ref to the question Trying to Migrate to Iron-Router from Router. I still dont understand how to migrate meteor router to iron-router.
I am using router in my meteor project. The router file is like followings:
Meteor.Router.add({
"/settings": function() {
if (!Roles.userIsInRole(Meteor.user(), ['admin'])) {
return false;
}
return 'site_settings';
},
"/new_page": function() {
if (!Roles.userIsInRole(Meteor.user(), ['admin'])) {
return false;
}
return 'new_page';
},
"/navigation": function() {
if (!Roles.userIsInRole(Meteor.user(), ['admin'])) {
return false;
}
return 'navigation';
},
"/login": function() {
return 'loginButtonsFullPage';
},
"/users": function() {
if (!Roles.userIsInRole(Meteor.user(), ['admin'])) {
return false;
}
return 'admin_users';
}
});
If someone knows how to use an iron-router to replace the return template in the right way. Much appreciate.
I meet a little bit complicated router function, and I have no idea how to solve it. the code is like:
"/": function() {
// Don't render until we have our data
if (!GroundDB.ready()) {
//if (!Offline.subscriptionLoaded('pages') || !Offline.subscriptionLoaded('settings')) {
return 'loadingpage';
} else {
var page_slug = utils.getSetting('indexPage');
var page = Pages.findOne({slug: page_slug});
if(!page) {
page = Pages.findOne();
// if pages dont have any public pages
if (!page) {
var isIndexPageInNav=Navigation.findOne({"location":"header_active","pages.slug":page_slug});
// if index page slug in navigation that means the user dont have right to view this slides or the index page not exist
if(isIndexPageInNav)
return 'loginButtonsFullPage';
else
return '404';
}
else {
page_slug = page.slug;
}
}
Session.set("page-slug", page_slug);
return page.template;
}
}
As you know the iron-router need give a template at the begining. but with router I can return dynamic templates. How does iron-router implement this idea.
Router.map(function() {
//site_settings being the name of the template
this.route('site_settings', {
path: '/settings',
action: function() {
if (!Roles.userIsInRole(Meteor.user(), ['admin'])) {
//if the conditional fails render a access_denied template
this.render('access_denied');
} else {
//else continue normally rendering, in this case the 'site_settings'
//template
this.render();
}
}
});
this.route('loginButtonsFullPage', {
path: '/login'
});
});
Note since you will be doing that if user is admin conditional a lot you can wrap that logic inside a controller and link it to all the relevant routes such as:
Router.map(function() {
this.route('site_settings', {
path: '/settings',
controller: 'AdminController'
});
this.route('new_page', {
path: '/new_page',
controller: 'AdminController'
});
this.route('navigation', {
path: '/navigation',
controller: 'AdminController'
});
//etc...
//don't need to add the controller for this one
//since all users have access
this.route('loginHuttonsFullPage', {
path: '/login'
});
});
AdminController = RouteController.extend({
action: function() {
if (!Roles.userIsInRole(Meteor.user(), ['admin'])) {
this.render('access_denied');
} else {
this.render();
}
}
});
A couple of other things you will want to check out in iron-router are layouts with {{> yield}} and waitOn which is indispensable.
The docs at https://github.com/EventedMind/iron-router will do a better job of explaining those concepts then I can here.
Here is my attempt at your more complicated route. It may not work right away because I may be misunderstanding what you are doing but the key things are to substitute the returns with this.render(template_name); waitOn instead of checking if something is ready(), adding all the required subscriptions to the waitOn and then finally adding all your logic to an action
//note: index is the name of the route, you do not actually need a template called index.
//in the previous examples where no template to render was returned then iron-router will
//look for a template with the same name as the route but in this route we will be providing
//it with a specific route name in all cases
this.route('index', {
path: '/',
//assuming GroundDB is a subscription e.g. GroundDB = Meteor.subscribe('groundDB');
//I don't know what your page and nav subscriptions are called but you should wait on them too.
//if you haven't assigned them to a variable do something like
//pageSubscription = Meteor.subscribe('pages');
waitOn: [GroundDB, pageSubscription, navigationSub],
//the template to load while the subscriptions in waitOn aren't ready.
//note: this can be defined globally if your loading template will be the same
//for all pages
loadingTemplate: 'loadingpage',
//here we evaluate the logic on which page to load assuming everything has loaded
action: function() {
var page_slug = utils.getSetting('indexPage');
var page = Pages.findOne({slug: page_slug});
if (!page) {
var isIndexPageInNav = Navigation.findOne({"location":"header_active","pages.slug":page_slug});
if(isIndexPageInNav)
this.render('loginButtonsFullPage');
else
this.render('404');
} else {
page_slug = page.slug;
}
Session.set("page-slug", page_slug);
this.render(page.template);
}
});