I am making a chat web app.
people can login with facebook and talk (insert message into mongoDB).
showing the text is simple:
javascript:
messages: function () {
return Messages.find({}, {sort: {createdAt: 1}});
}
each message has these attributes:
text: text,
createdAt: new Date(), // current time
owner: Meteor.userId(), // _id of logged in user
username: Meteor.user().profile.name
It works fine, but I want to "style the message differently" depending on
whether the message's owner is equal to currentUser (i.e. whether it's my message or others message)
For example, I want my message to be float:right and others message to be float:left
I am thinking the code probably looks something like this:
{{#if mymsg}}
<div class="msgdiv_my">
<span class="message">{{text}}</span>
</div>
{{else}}
<div class="msgdiv">
<span class="message">{{text}}</span>
</div>
{{/if}}
Where and how to write the mymsg function (which should return True if the message.owner == currentUser, and false otherwise)
You would usually write those checks in a template helper, like this:
Template.myTemplate.helpers({
ownDocument: function (doc) {
return doc.owner === Meteor.userId();
}
});
Then in myTemplate, call your helper like this:
<template name="myTemplate">
{{#if ownDocument text}}
<div class="msgdiv_my">
<span class="message">{{text}}</span>
</div>
{{else}}
<div class="msgdiv">
<span class="message">{{text}}</span>
</div>
{{/if}}
</template>
Although you may want to implement a global "equals" helper to your meteor app's client, as it sadly is not built in Meteor Spacebars yet:
Template.registerHelper('equals',
function(v1, v2) {
return (v1 === v2);
}
);
This way, you would call:
{{#if equals text.owner currentUser._id}}
And get the same result.
KyleMit wrote a lengthy answer for all your equality check needs in Meteor.
Related
Context: I receive from Elasticsearch the result of a search (example below) which I put into a Vue.js data object. I then list the data via <div v-for="result in results">{{result.name}}</div>.
var vm = new Vue({
el: "#root",
data: {
results: [{
'name': 'john',
'big': true
},
{
'name': 'jim',
'tall': true
},
{
'name': 'david'
}
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<div id="root">
<div v-for="result in results">{{result.name}}</div>
</div>
I now would like to filter the results. To do so, I will have switches which will be bound via v-model.
Question: what is the correct way to handle filtering in Vue.js?
I would like to render (via a v-if, I guess) only elements from results which match a filter (say, big is checked - so only johnshould be visible), or a concatenation of filters (logical AND).
The part I have a hard time turning into Vue.js philosophy is "display the element if all active switches are present (value true) in that element).
Since I am sure that having a chain of v-ifs is not the right approach, I prefer to ask before jumping into that (and I would probably rather rerun a search with parameters than go this way - but I would prefer to avoid the search way).
Create a computed property which returns only the filtered results:
computed: {
filteredResults() {
return this.results.filter((result) => {
// your filter logic, something like this:
// return result.big || result.tall
});
}
}
And use it in the v-for instead:
<div v-for="result in filteredResults">{{result.name}}</div>
I am trying to implement a custom validation function which can return either true (if the field is valid) or some custom error message. Here's my current attempt:
global.Messages = Models.Messages = new Mongo.Collection 'messages'
MessagesSchema = new SimpleSchema({
content: {
type: String,
label: "Message",
max: 200,
custom: ->
if #obj.content.includes("a")
true
else
"not contain a"
}, {tracker: Tracker})
Messages.attachSchema MessagesSchema
This is a contrived example but still, it's not working. The conditional in the custom function is run, and when true gets returned then the record does save. However, if "not contain a" gets returned, it does not become the validation message displayed on the client. It just says content is invalid, and I'm not sure how to customize this message. Here's the template code:
{{#autoForm collection="Messages" id="insertMessageForm" type="insert"}}
<fieldset>
<legend>Add message</legend>
{{> afFieldInput type='text' name='content'}}
{{#if afFieldIsInvalid name='content'}}
<span class="help-block">{{afFieldMessage name='content'}}</span>
{{/if}}
</fieldset>
<button type='submit' class='btn btn-primary'>Insert</button>
{{/autoForm}}
There were a few problems with my original code.
First of all, I didn't specify how I was requiring SimpleSchema but it should be done this way; this uses the new node-simpl-schema package which is what meteor-simple-schema migrated to:
SimpleSchema = require('simpl-schema').default
SimpleSchema.extendOptions(['autoform']);
Validation messages are mapped to keys:
SimpleSchema.setDefaultMessages
messages:
en:
"notA": "doesnt contain a"
The messages and en hashes are necessary for it to be the correct structure.
important point: the return value of custom is not the message that gets displayed on the client. It is a key, pointing to the entry in the default messages object.
For example:
custom: ->
if #obj.content.includes("a")
true
else
"notA"
This will end up showing the message "doesnt contain a"
So I have two collections, appliers & profiles,
appliers = {_id,idAppliersProfile}
&
profile = {_id,profilename}
So my question is if I do an #each for appliers, how do I access profile collection to get the profile instead of just the id?
Thanks.
Assuming both sets of docs are published to the client, one solution looks something like this:
html
<template name="myTemplate">
{{#each appliers}}
{{#with getProfile idAppliersProfile}}
<div>{{profilename}}</div>
{{/with}}
{{/each}}
</template>
js
Template.myTemplate.helpers({
appliers: function () {
return appliers.find();
},
getProfile: function (id) {
return profile.findOne(id);
}
});
I'm actually struggling with something simple; I want to my helper to be executed on the template "fullArticle".
I've tried to warp the tempate into a div, but it's not correct to use name HTML attribute on div. So I tried with a class, still nothing on the console.
I would be able to write something like this on my helper:
'.article': function(){...}
or better, use a data HTML attribute
'[data-name="article"]': function(){...}
or even directly target the template name (what i'm trying to do in the code below:
fullArticle: function(){...}
fullArticle.html
<template name="fullArticle">
<div class="article">
{{_id}}
<h1>Full article {{title}}</h1>
<h3><small>{{date}}</small></h3>
<p>{{content}}</p>
</div>
</template>
fullArticle.js
if (Meteor.isClient){
Template.fullArticle.helpers({
fullArticle: function(){
console.log("id is : " + this._id);
//return Articles.findOne({_id: id});
}
});
}
routes.js
Router.route(':categoryName/article/:_id', function(){
this.render('fullArticle', {
data: function(){
return {_id: this.params._id};
}
});
});
Do you know how to make it work, or propose a better solution?
Below you can find typical schema how to get data from database and display on client side. Please use it as inspiration.
On the server side create publish function:
Meteor.publish('article',function(articleId){
check(articleId, String);
return Articles.find({_id:articleId})
})
routes.js
Router.route(':categoryName/article/:_id',{
name:'full.article',
template: 'fullArticle',
waitOn: function () {
return Meteor.subscribe( 'article', this.params._id ),
},
data : function(){
return Articles.findOne( { _id: this.params._id } )
}
})
fullArticle.html
<template name="fullArticle">
<div class="article">
{{_id}}
<h1>Full article {{title}}</h1>
<h3><small>{{date}}</small></h3>
<p>{{content}}</p>
</div>
</template>
I can't get a the 'show' page of an instance of a model to display its data.
Here's the template that won't show its data:
<template name="priority">
<h1>Priority: {{title}}</h1>
</template>
It's very simple in and of itself, yet I can't get title to display. Iron:router does the job of directing us to this page with the following code:
Router.route('/priority/:_id', function(){
var priority = this.params._id;
this.render('priority', {
data: function(priority){
Meteor.call('showPriority', priority, function(error){
if (error) {
console.log("An error has occured: " + error);
}
})
}
})
}, {
name: 'priority.show'
});
The Meteor.method is very simple, it just queries for the variable priority:
'showPriority': function(priority) {
return Priorities.findOne({_id: priority});
}
The view which carries the href is here:
<template name="priorityList">
<ul class="table-view">
{{#each this}}
<li class="table-view-cell">
{{title}}
<span class="pull-right icon icon-edit"></span>
</li>
{{/each}}
</ul>
</template>
Note that this view shows a list of all priorities. When I inspect the href, all the paths are being dynamically generated with an _id:
<a href="/priority/yYihyZmZ2xkAso7i5">...
Oh, and I should mention, that I also tried to use the waitOn method, since I thought I might be loading the template before the data, but that didn't help either...
Router.configure({
...
waitOn: function(){
return Meteor.subscribe('priorities');
}
});
So much code, just to show what's going on!
What's the deal here? Why won't my "show" template give me any data?
Change you route to this.
Router.map(function () {
this.route('priority', {
path: '/priority/:_id',
waitOn: function(){
return Meteor.subscribe('priorities',this.params._id);
},
data: function(){
if(this.ready()){
return Priorities.findOne({_id: this.params._id});
}else{
this.render('loading') //if data not ready we render some loading template
}
}
});
});
You don't need to make a Meteor.call, for the find(); instead to everything on the data:function()
The above is just an example so you can get the idea, but it should work since you are expecting _id:priority and _id:this.params._id its the same.
Just be sure you have the autopublish package removed.
and have the subscriptions/publish in order.
Meteor.publish('Menu', function(){
return Priorities.find();
});