I have problems with creating routes with user's usernames. So idea is something like this: Click on path and go to that users profile. His link should be something like : http://www.something.com/usersUsername
I was reading and trying everything I found on internet about this but lot of stuff changed so I couldn't manage this.
Only thing I found usefull is that when page loads client ,,watch" paths first and then subscribes to a collection so I got ,,null" for path. Any help? My idea is to create something to waitOn for subscribe...
Packages: iron:router , accounts-ui , accounts-password
Here is code:
Start page, template:
<template name="početna">
<h1>Dobrodošli!</h1>
<h3>Registrujte se:</h3>
{{> register}}
<h3>Prijavite se:</h3>
{{> login}}
{{#if currentUser}}
<h2>Logovan si!</h2>
{{> logout}}
Profil
{{/if}}
Router JS file:
Router.configure({
layoutTemplate: 'okvir'
});
// * * * * * * //
Router.route('/', {
name: 'početna',
template: 'početna',
});
Router.route('/:username', {
waitOn: function(){
return Meteor.subscribe('userData'), Meteor.user().username
},
name: 'profil',
template: 'profil',
});
Simple HTML template file only to se if it works:
<template name="profil">
<h1>RADI</h1>
</template>
Thanks!
Here you go:
Router.route('/:username',{
name: 'profil',
waitOn: function () {
return Meteor.subscribe('userData', this.params.username)
},
action: function () {
this.render('profil', {
data: function () {
return Meteor.users.findOne({username: this.params.username});
}
});
}
});
EDIT:
With this.params.username will let anybody visit that profile, user or not. If you want to prevent that, you can use onBeforeAction()
onBeforeAction: function() {
if(Meteor.user() && this.params.username == Meteor.user().username){
this.next();
} else {
Router.go('/not-authorized') //or any other route
}
},
Luna, thanks for help! Luna's answer helped but I also needed:
1.) Helper to set value of username=username
Template["početna"].helpers({ username: function() { return Meteor.user().username } })
2.) Publish
Meteor.publish("userData", (username) => {
return Meteor.users.find({
username: username
})
});
Related
I am relatively new to Vue, so forgive me if this is obvious (or obviously impossible).
I have a set of JSON data (fetched from a RESTful API via vue-resource):
{content: "This is content. <a href='/blog'> Link to blog </a>"}
Right now, the link triggers a page reload. If it were a vue-router v-link, that would not be an issue. However, this doesn't work (quotes are escaped in the data, of course):
{content: "This is content. <a v-link="{ path: '/blog' }"> Link to blog </a>"}
At this point, the template is already parsed, and Vue won't create a v-link anymore (it will just show up as a v-link in the rendered html).
My final result would ideally mean that I could include links in my CMS, either in HTML or Vue format, and have Vue route them correctly as v-links.
Is there something I can do to make Vue interpret the link in the JSON data?
I've answered the question on Vue Chat, and writing it here in case any other people facing similar problem
Simplified example on Codepen
HTML
<div id="app">
<div>
<a v-link= "{path:'/home'}">Go to home</a>
</div>
<router-view></router-view>
</div>
<template id="home">
<div>
<div>
Fetched Content:
</div>
<div>
{{{ fetchedContent }}}
</div>
</div>
</template>
<template id="route1">
<div>
Route1 view
</div>
</template>
<template id="route2">
<div>
Route2 view, this is different from Route1
</div>
</template>
javascript
function getContent (callback) {
var content = 'Click this: Go to route1 and Go to route2'
setTimeout(function () { callback(content) }, 1000)
}
var Home = Vue.component('home',{
template:'#home',
data: function () {
return {
fetchedContent: 'Loading...'
};
},
ready: function () {
var self = this
var router = this.$router
getContent( function (result) {
self.fetchedContent = result;
Vue.nextTick(function () {
var hyperLinks = self.$el.getElementsByTagName('a')
Array.prototype.forEach.call(hyperLinks, function (a) {
a.onclick = function (e) {
e.preventDefault()
router.go({ path: a.getAttribute("href") })
}
})
})
})
}
});
var Route1 = Vue.component('route1', {
template: '#route1'
});
var Route2 = Vue.component('route2', {
template: "#route2"
});
var router = new VueRouter({
hashbang:false,
history:true
});
router.map({
'/home':{
component:Home
},
'/route1':{
component:Route1
},
'/route2':{
component:Route2
}
});
router.start({
}, '#app');
I had a similar solution here: question using a custom dataset in my JSON code and a click listener to process it:
mounted() {
window.addEventListener('click', event => {
let target = event.target
if (target && target.href && target.dataset.url) {
event.preventDefault();
console.log(target.dataset.url);
const url = JSON.parse(target.dataset.url);
console.log(url.name)
this.$router.push(url.name);
}
});
},
I'm using CollectionFS to allow image uploads. The image uploads need to belong to specific posts. I followed the steps from the documentation - Storing FS.File references in your objects - however, I'm having a hard time displaying the image of the associated post.
The post currently saves with a postImage that references an image._id - this part is working fine. However, I am unsure how to display the actual photo, as it will need to grab the photo from the images collection (the post collection just saves an ID to reference).
post-list.html
<template name="postList">
<tr data-id="{{ _id }}" class="{{ postType }}">
...
<td><textarea name="postContent" value="{{ postContent }}"></textarea> </td>
<td>{{ postImage._id }} </td> // This currently displays the correct image._id, but I would like to display the image,
<td><button class="delete tiny alert">Delete</button></td>
</tr>
</template>
post-list.js
Template.postList.helpers({
posts: function() {
var currentCalendar = this._id;
return Posts.find({calendarId: currentCalendar}, {sort: [["postDate","asc"]] });
}
});
post-form.js - This form creates a new Post and Image. The Image._id is saved to the Post.postImage.
Template.postForm.events({
// handle the form submission
'submit form': function(event) {
// stop the form from submitting
event.preventDefault();
// get the data we need from the form
var file = $('.myFileInput').get(0).files[0];
var fileObj = Images.insert(file);
var currentCalendar = this._id;
var newPost = {
...
calendarId: currentCalendar,
owner: Meteor.userId(),
postImage: fileObj,
};
// create the new poll
Posts.insert(newPost);
}
});
use reywood:publish-composite and dburles:collection-helpers so;
Collections || collections.js
Posts = new Mongo.Collection('posts');
Images = new FS.Collection("files", {
stores: [
// Store gridfs or fs
]
});
Posts.helpers({
images: function() {
return Images.find({ postId: this._id });
}
});
Template || template.html
<template name="postList">
{{# each posts }}
{{ title }}
{{# each images }}
<img src="{{ url }}">
{{/each}}
{{/each}}
</template>
Client || client.js
Template.postList.helpers({
posts: function() {
return Posts.find();
}
});
Template.postList.events({
// handle the form submission
'submit form': function(event, template) {
// stop the form from submitting
event.preventDefault();
// get the data we need from the form
var file = template.find('.myFileInput').files[0];
Posts.insert({
calendarId: this._id,
owner: Meteor.userId()
}, function(err, _id) {
var image = new FS.File(file);
file.postId = _id;
if (!err) {
Images.insert(image);
}
});
}
});
Router || router.js
Router.route('/', {
name: 'Index',
waitOn: function() {
return Meteor.subscribe('posts');
}
});
Server || publications.js
Meteor.publishComposite('posts', function() {
return {
find: function() {
return Posts.find({ });
},
children: [
{
find: function(post) {
return Images.find({ postId: post._id });
}
}
]
}
});
When using CollectionFS, on the client side you need to ensure that your subscriptions are correctly defined. This is the biggest stumbling block i've encountered with my developers in using CFS - understanding and mapping the subscription correctly.
First things first - you need to have a subscription that is going to hit Images collection. I'm not familiar with the latest CFS updates for their internal mapping but the following approach has usually worked for me.
Meteor.publish('post', function(_id) {
var post = Posts.findOne({_id: _id});
if(!post) return this.ready();
return [
Posts.find({_id: _id}),
Images.find({_id: post.postImage})
]
});
For displaying, there is a handy CFSUI package( https://github.com/CollectionFS/Meteor-cfs-ui ) that provides some nice front end handlers.
With the above mapping your subscription, you can then do something like
{{#with FS.GetFile "images" post.postImage}}
<img src="{{this.url store='thumbnails'}}" alt="">
{{/with}}
I am attempting to make a element in a meteor template editable via a update function. The data changes when it is inserted from a server side code in the fixture.js code. However I have no luck updating it via a editable form with some Template.name.events({}); code and, creating a collection, publishing and subscribing to it. The very last piece of code is the fixture.js file. So in some regard I can insert into the collection and update it, but I have no luck with the edit financialsEdit template. The router.js file I included only contains parts regarding the financials template. If needed I will post more.
Basically I can't update a collection value with a update function using $set and passing a key value pair.
UPDATE: I added the permissions.js file in the lib directory to show what ownsDocument returns.
Here is my code.
client Directory
client/editable/edit_financial.js
Template.financialsEdit.events({
'submit .financialsEdit': function(e) {
e.preventDefault();
var currentFinanceId = this._id;
var financialsProperties = {
issuedOutstanding: $('#issuedOutstanding').val()
}
Financials.update(currentFinanceId, {$set: financialsProperties}, function(error) {
if (error) {
alert(error.reason);
} else {
console.log(financialsProperties);
// Router.go('financials');
Router.go('financials');
}
});
}
});
client/editable/financials_helpers.js
Template.financials.helpers({
financials: function() {
return Financials.find();
},
ownFinancial: function() {
return this.userId === Meteor.userId();
}
});
client/editable/financials
<template name="financials">
<div id="finance">
{{#each financials}}
<h2>Issued Outstand : {{issuedOutstanding}}</h2>
{{/each}}
</div>
</template>
client/editable/financials_edit.html
<template name="financialsEdit">
<form class="main form financialsEdit">
<input id="issuedOutstanding" type="number" value="{{issuedOutstanding}}" placeholder="{{issuedOutstanding}}" class="form-control">
<input type="submit" value="Submit" class="submit"/>
</form>
</template>
lib Directory
lib/router.js
Router.route('/financials', function () {
this.render('financials');
});
Router.route('/financialsedit', {name: 'financialsEdit'});
lib/collections/financials.js
Financials = new Mongo.Collection('financials');
Financials.allow({
update: function(userId, financial) { return ownsDocument(userId, financial); },
remove: function(userId, financial) { return ownsDocument(userId, financial); },
});
Financials.deny({
update: function(userId, financial, fieldNames) {
// may only edit the following two fields:
return (_.without(fieldNames, 'issuedOutstanding').length > 0);
}
});
lib/permissions.js
// check that the userId specified owns the documents
ownsDocument = function(userId, doc) {
return doc && doc.userId === userId;
}
server/publications.js
Meteor.publish('financials', function() {
return Financials.find();
});
server/fixture.js
if (Financials.find().count() === 0) {
Financials.insert({
issuedOutstanding: '43253242'
});
}
Apologies if this has already been asked and I couldn't figure that out.
I am attempting to link an Ember dynamic Route with a Template. It's not working. The error for the below code is Error while processing route: favorite undefined is not a function
The idea is that the main page should show a list of favorites that are returned via Ajax. Each favorite should have a link. The user clicks a link and the favorite is injected into the relevant template on the same page.
The main page is working correctly. With the code below, the links are currently showing index.html/#/12345ab where 12345ab is the product_id.
HTML Template:
<script type="text/x-handlebars" id="favorites">
{{#each favorite in arrangedContent}}
<div class="productBox">
{{#link-to 'favorite' favorite.product_id}}
<img {{bind-attr src=favorite.product_image}} />
{{/link-to}}
</div>
{{/each}}
{{outlet}}
</script>
<script type="text/x-handlebars" id="favorite">
<h2>{{product_name}}</h2>
<img {{bind-attr src=product_image}} />
</script>
Router code:
App.Router.map(function() {
this.resource('favorites', { path: '/'});
this.resource('favorite', { path: ':product_id' });
});
App.FavoritesRoute = Ember.Route.extend({
model: function() {
return Ember.$.ajax({
//this returns correctly
}).then(function(data) {
return data.favorites;
});
}
});
App.FavoriteRoute = Ember.Route.extend({
model: function(params) {
return App.Favorites.findBy('product_id', params.product_id);
}
});
Update:
The answer below gives the following code, but if the user goes directly to the page via the URL or a straight refresh, it fails due to the fact that the favorites model is not resolved yet. Exact error is: Cannot read property 'findBy' of undefined
App.FavoriteRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('favorites').findBy('product_id', params.product_id);
}
});
Update 2:
Entire Router code:
App.Router.map(function() {
this.resource('favorites', { path: '/'});
this.resource('favorite', { path: ':product_id' });
});
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return new Promise(function(resolve, reject) {
Ember.$.ajax({
url: 'MY_URL',
dataType: 'jsonp',
jsonp: 'callback'
}).then(function(data) {
resolve(data.favorites);
});
});
},
setupController: function(controller, model) {
return this.controllerFor('favorites').set('model', model);
}
});
App.FavoriteRoute = Ember.Route.extend({
model: function(
return this.controllerFor('favorites').get('model').findBy('product_id', params.product_id);
}
});
By the looks of it, you want to find the model from the parent route. You can do it likes so:
App.FavoriteRoute = Ember.Route.extend({
model: function(params) {
this.modelFor('favorites').arrangedContent.findBy('product_id', params.product_id);
}
});
UPDATE:
The problem is that your promise from the parent route isn't getting resolved correctly. You're returning a promise but the result of that promise isn't getting resolved i.e. (return data.favorites) is not resolving the promise.
Update it to:
App.FavoritesRoute = Ember.Route.extend({
model: function() {
return new Promise(function(resolve, reject) {
Ember.$.ajax('yourURL').then(
function(data){
resolve(data.favorites);
});
});
}
});
Also incorporate the initial feedback from this answer. I have a working JSBin going against an actual endpoint to show it works: http://jsbin.com/boloya/3/edit
P.S. Look out for params.product_id, that comes in as a string. You made need to cast it to the required type in order for the findBy to work correctly.
P.S.S. I should also add, Ember.$.ajax returns a promise, but you want the model to be data.favorites which is the need for the outer promise. If you just returned Ember.$.ajax and accessed everything via model.favorites you wouldn't need it.
Your routes need to be nested for a child resource to have access to a parent via #modelFor. However, if your UI isn't nested, your routes probably shouldn't be, since nesting routes also wires up a corresponding view hierarchy.
You could always define the model for the favorite route in terms of a subset of the data returned by your ajax call:
//routes/favorite.js
model: function() {
return Ember.$.getJSON('/favorites').then(function(favorites) {
return favorites.findBy('id', params.product_id');
});
}
but then, the top-level .getJSON('/favorites)call would be made multiple times, every time the user enters/favorites, and every time he enters/favorites/:id`.
Instead, you can have the application set up the FavoritesController once upon entry. That way you can share data, but favorite doesn't have to be a child route of favorites. It might look something this:
//routes/application.js
model: function() {
return Ember.$.getJSON('/favorites');
},
setupController: function(controller, model) {
this.controllerFor('favorites').set('model', model);
}
and
//routes/favorite.js
model: function(params) {
return this.controllerFor('favorites').find('id', params.id);
}
That way, the JSON is only loaded once, ApplicationRouter is wiring up your FavoritesController for you, and the data is shared with the favorite resource.
With some help from the Ember IRC channel, this is the working code. In essence, it creates an Index template for both the favorites and the favorite template to render into. Then the favorite route can access it's parent route favorites and yet still render into the same template area.
HTML
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" id="favorites">
{{outlet}}
</script>
<script type="text/x-handlebars" id="favorites/index">
{{#each favorite in arrangedContent}}
<div class="productBox">
{{#link-to 'favorite' favorite.product_id}}
<img {{bind-attr src=favorite.product_image}} />
{{/link-to}}
</div>
{{/each}}
{{outlet}}
</script>
<script type="text/x-handlebars" id="favorite">
<h2>{{product_name}}</h2>
<img {{bind-attr src=product_image}} />
</script>
Router.js
App.Router.map(function() {
this.resource('favorites', { path: '/'}, function () {
this.resource('favorite', { path: ':product_id' });
});
});
App.IndexRoute = Ember.Route.extend({
redirect: function () {
this.replace('favorites');
}
});
App.FavoritesRoute = Ember.Route.extend({
model: function() {
return new Promise(function(resolve) {
Ember.$.ajax({
url: 'MY_URL',
dataType: 'jsonp',
jsonp: 'callback'
}).then(function(data) {
resolve(data.favorites);
});
});
}
});
App.FavoriteRoute = Ember.Route.extend({
model: function (params) {
return this.modelFor('favorites').findBy('product_id', params.product_id);
}
});
I have the following code which calls an transitionToRoute('search') when a search-query is entered and the enter button is pressed or submit button is clicked.
However, my Router still won't show the searchQuery in the template where it says:
<p>You searched for: "{{searchQuery}}"</p>
and the URL looks like http://www.example.com/#/search/[object Object] when searching for something (which doesn't seem right to me).
(full code can be viewed over at: http://jsfiddle.net/Mn2yy/1/)
This is the relevant code:
Templates:
<script type="text/x-handlebars" data-template-name="container">
<button {{action "doSearch"}} rel="tooltip-bottom" title="search" class="icon"><i class="icofont-search"></i></button>
{{view Ember.TextField valueBinding="search" action="doSearch"}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="searchpage">
<h1>Search</h1>
{{#linkTo "home"}}Homepage{{/linkTo}}
<p>You searched for: "{{searchQuery}}"</p>
</script>
Application controller:
MyApp.ApplicationController = Ember.Controller.extend({
// the initial value of the `search` property
search: '',
doSearch: function() {
// the current value of the text field
var query = this.get('search');
this.transitionToRoute('search');
}
});
and the Searchpage route:
MyApp.SearchRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('searchQuery', this.get('query'));
},
renderTemplate: function() {
this.render('searchpage', { into: 'container' });
}
});
First, you need to define the dynamic segment in the router for the search route:
MyApp.Router.map(function() {
this.route("home", { path: "/" });
this.route("search", { path: "/search/:query" })
});
Then you set the searchQuery property on the application in the doSearch action. You also pass the query variable to the transitionToRoute method, since it'll fill in the dynamic segment.
MyApp.ApplicationController = Ember.Controller.extend({
// the initial value of the `search` property
search: '',
doSearch: function() {
// the current value of the text field
var query = this.get('search');
this.set('searchQuery', query);
this.transitionToRoute('search', query);
}
});
Since you need to access this property from the App.SearchController instance, you need to wire the 2 controllers together by using the needs API:
MyApp.SearchController = Ember.Controller.extend({
needs: ['application'],
application: Ember.computed.alias('controllers.application')
});
Aliased the controllers.application property to just application, to avoid too much typing eg. in the template.
Then you bind to this property in the search template:
<script type="text/x-handlebars" data-template-name="searchpage">
<h1>Search</h1>
{{#linkTo "home"}}Homepage{{/linkTo}}
<p>You searched for: "{{application.searchQuery}}"</p>
</script>
Last step: if you refresh the page at this point, searchQuery won't be automatically populated from the URL. Let's just fix that with the deserialize hook:
MyApp.SearchRoute = Ember.Route.extend({
deserialize: function(params) {
this.controllerFor('application').setProperties({
searchQuery: params.query,
search: params.query
});
},
renderTemplate: function() {
this.render('searchpage', { into: 'container' });
}
});
This will get the params from the URL and set up the application controller with the value of the query key.
That's pretty much it, hope I didn't miss anything!