How do I display instant messages, when insecure is removed, with Meteor.methods and Meteor.call? - methods

I am taking coding courses online, so I can build my app sometime next year...
Can you help me with this instant message code please?
a. I am supposed to display an alert message when the user is not logged in.
b. Display the usename in the header.
c. Display the username with his instant message.
Since insecure is removed, I have to use Meteor.methods and meteor.call. I cannot use Sessions. I keep getting weird errors...
Here is the javascript code I have tried based on the course module, but I get errors that don't make sense to me...
Messages = new Mongo.Collection("messages");
if (Meteor.isClient) {
// this will configure the sign up field so it
// they only need a username
Accounts.ui.config({
passwordSignupFields: 'USERNAME_ONLY',
});
Template.messageForm.events({
// this event listener is triggered when they click on
// the post! button on the message form template
'click .js-save-message': function (event) {
var messageText = $('#message-text-input').val();
// notice how tihs has changed since the lsat time
// now we read the username from the Meteor.user()
var messageNickname = "Anon";
if (Meteor.user()) {
messageNickname = Meteor.user().username;
}
var message = {
messageText: messageText,
nickname: messageNickname,
createdOn: new Date()
};
// HERE is where you come in ....
// call a meteor method
// on the server here instead of
if (Meteor.isServer) {
Meteor.methods({ // defines a method, adds extra security layer to app
insertMessage: function () {
var doc, user, euser;
doc = Message.findOne();
if (!doc) {
return;
} // no logged in user, give up
// now I have a doc and possibly a user
user = Meteor.user().profile;
eusers = insertMessage.findOne({ docid: doc._id });
if (!eusers) {
eusers = {
docid: doc._id,
users: {},
};
}
user.lastEdit = new Date();
eusers.users[this.userId] = user;
insertMessage.upsert({ _id: eusers._id }, eusers);
}
}
)
}
// comment out this code, which won't work as we removed insecure...
//Messages.insert(message); // the insecure way of doing it
// ... put code here that calls the
Meteor.call('insertMesage', message, function (err, res) {
if (!res) {
alert('You need to log in!');
}
});
Template.header.helpers({
// HERE is another one for you - can you
// complete the template helper for the 'header' template
// called 'nickname' that
// returns the nickname from the Session variable?, if they have set it
nickname: function () {
if (Meteor.user()) {
return Meteor.user().username;
}
},
});
Template.messageList.helpers({
// this helper provides the list of messages for the
// messageList template
messages: function () {
return Messages.find({}, { sort: { createdOn: -1 } })
}
});
},
});
}
Here is the html file
<body>
{{>header}}
{{>nicknameForm}}
{{>messageList}}
{{>messageForm}}
</body>
<template name="header">
<h1>Welcome to M-Instant {{nickname}}</h1>
</template>
<template name="messageList">
{{#each messages}}
{{>messageItem}}
{{/each}}
</template>
<template name="messageItem">
<h3>{{nickname}} - {{messageText}}</h3>
</template>
<template name="nicknameForm">
<div class="form-group">
<label for="nickname-input">Nickname:</label>
<input type="text" class="form-control" id="nickname-input"
placeholder="Type message here...">
<button type="submit" class="btn btn-default js-set-nickname">Set my
nickname!</button>
</div>
</template>
<template name="messageForm">
<div class="form-group">
<label for="message-text-input">Message:</label>
<input type="text" class="form-control" id="message-text-input"
placeholder="Type message here...">
<button type="submit" class="btn btn-primary js-save-message">Post!
</button>
</div>
</template>
Here is the Methods file
Meteor.methods({
'insertMessage':function(message){
console.log("If you manage to call the method, you'll see this
message in the server console");
if (!Meteor.user()){
return;
}
else {
return Messages.insert(message);
}
}
})

Related

How to create private channel name dynamically

I woluld like to create real-time order tracking system using laravel, vue and pusher. In admin panel I have list of all orders. The idea is when admin change order status the user (owner of this order) will get notification without refreshing page. To make it I want to use private channels. My problem is to create private channel name dynamically.
Admin panel looks like below.
<h1>Admin</h1>
<table>
#foreach ($orders as $o)
<tr>
<td>{{$o->name}}</td>
<td>{{$o->status}}</td>
<td>{{$o->getUser->name}}</td>
<td>
<update-order-status :id="{{$o->id}}"></update-order-status>
</td>
</tr>
#endforeach
</table>
There is update-order-status component which is a form used to change status of order. Id of order is passed to component via props. Content of component look like below:
///update-order-status component
<template>
<div>
<form method="GET" action="change_status">
<input type="hidden" name="id" :value=id />
<select name="status">
<option value="placed">Placed</option>
<option value="progres">Progres</option>
<option value="delivered">Delivered</option>
</select>
<input type="submit" value="update status" v-on:click="upd(id)" />
</form>
</div>
</template>
<script>
export default {
created() {
},
props: ["id"],
data: function() {
return {
};
},
methods: {
upd: function(id) {
this.$root.$emit("eve", id);
}
}
};
</script>
When admin submit form order status is updated in database and also event eve is emitted. This event is used to pass id of order to status-changed compoennt. This component is placed in user panel and contains notification that order status has been changed.
///status changed component
<template>
<div v-if="sn" class="notification is-primary">
<button class="delete" v-on:click="del()"></button>
Staus of order nr {{nr}} has been changed
</div>
</template>
<script>
export default {
created() {
this.$root.$on("eve", data => {
this.nr = data;
});
Echo.private("order" + this.nr).listen("status_changed", e => {
this.sn = true;
});
},
props: [],
data: function() {
return {
sn: false,
nr: 0
};
},
methods: {
del: function() {
this.sn = false;
}
}
};
</script>
Here is the problem. Variable nr is 0 all time. It'not changing dynamically. When nr is static or channel is public this code works fine. Please help.
Put the Echo inside the $on. Otherwise the asynchronous $on happens too late to change nr in the Echo.
created() {
this.$root.$on("eve", data => {
this.nr = data;
Echo.private("order" + this.nr).listen("status_changed", e => {
this.sn = true;
});
});
}

Spring MVC and bootstrap modal form : how to create server-side validation for it?

I'm newbie at spring and front-end at all.
I have (not mine) an old front-end code to show modal form over some page :
modal form is:
<div id="myModalForm" class="modal inmodal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog ">
<div class="modal-content animated fadeIn">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span
aria-hidden="true">×</span><span class="sr-only"></span></button>
<h4 class="modal-title"><spring:message code="entity.modal.title"/></h4>
</div>
<div id="modalContent" class="modal-body">
<form id="createForm" name="createForm" class="" action="/entity/create"
method="post" enctype="multipart/form-data">
...
</form>
</div>
</div>
</div>
</div>
form is shown from java scripts. Button that invokes form is:
<script>
$(document).ready(function () {
$('#tbl').DataTable({
responsive: true,
dom: 'l<"toolbar">frtip',
initComplete: function () {
$("div.toolbar")
.html('<button id="new_entity_btn" type="button" class = "btn btn-white" onclick="openCreateWindow()" ><spring:message
code="entity.table.create.btn"/> </button>');
}
});
/*activate tooltips*/
$(function () {
$('[data-toggle="popover"]').popover();
});
});
</script>
and onclick for button is:
<script>
function openCreateWindow() {
$('#myModalForm').modal('show');
}
</script>
Now as you can see #myModalForm is not a Spring view form as it doesn't generate even get-request to be shown. It just appears over existing page in modal mode. Now the question is how to create server-side validation for it? I've tried next:
1) refactor it to spring mvc form i.e. via
<form:form ... />
tag with attribute
ModelAttribute='MyFormAttribute'. But i have no idea how to create back-end object for MyFormAttribute as appearance of form doesn't generate get-request. And idea was to have on post method of controller something like:
#RequestMapping(value = {"/entity/create"}, method = RequestMethod.POST)
String createNewEntity(
#ModelAttribute("MyFormAttribute") MyEntity entity,
BindingResult bindingResult,
Model model)
2) Trying to change post method of controller so that it might return error. But there is no way to create bindingResult parameter in case if form is not mvc one
3) Trying to validate via jQuery.validate i.e. smth. like :
<script>
/*form validation*/
$().ready(function () {
$("#createForm").validate({
rules: {
Field1: {
remote: function () {
var r = {
url: '/validateEntityField1',
type: "POST"
};
return r;
}
},
Field2: {
remote: function () {
var r = {
url: '/validateEntityField2',
type: "POST"
};
return r;
}
}
...
},
errorElement: "em",
highlight: function (element, errorClass, validClass) {
$(element).fadeOut(function () {
$(element).fadeIn();
});
$(element).addClass(errorClass).removeClass(validClass)
},
unhighlight: function (element, errorClass, validClass) {
$(element).removeClass(errorClass).addClass(validClass)
}
})
;
});
</script>
But it looks rather ugly as i should validate every field separately on its own end point and. Besides form has file input so i should send it twice or smth.
So, how could i achieve next issue:
leave modal form over existing page
validate it on server side. It would be nice to make validation against whole entity.
Answering on my commented question. Not the exact code you are looking but will give you an idea. Though it depends on individual choice, in controller you can use ValidationUtils as shown below. Here you can map your error messages and json response which later on you want on your front-end:
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public #ResponseBody JsonResponse addUser(#ModelAttribute("user") User user, BindingResult result, Errors errors, Model model) {
JsonResponse response = new JsonResponse();
ValidationUtils.rejectIfEmptyOrWhitespace(result, "firstname", "Firstname is required. It can not be empty.");
if (user.getFirstname().length() < 4 || user.getFirstname().length() > 32) {
errors.rejectValue("firstname", "Firstname must be between 4 and 32 characters.");
}
ValidationUtils.rejectIfEmptyOrWhitespace(result, "lastname", "Lastname is required. It can not be empty.");
if (user.getLastname().length() < 4 || user.getLastname().length() > 32) {
errors.rejectValue("lastname", "Lastname must be between 4 and 32 characters.");
}
if (!result.hasErrors()) {
userList.add(user);
response.setStatus("SUCCESS");
response.setResult(userList);
} else {
response.setStatus("FAIL");
response.setResult(result.getAllErrors());
}
return response;
}
And I picked the sample for Bootstrap Modal from here:
https://www.w3schools.com/bootstrap4/bootstrap_modal.asp
Somewhere in your html:
<!-- Modal body -->
<div class="modal-body">
<div class="form-group">
<label class="col-form-label" for="username">Enter your First Name</label>
<i class="fas fa-users"></i>
<input type="text" id="firstname" class="form-control">
</div>
....
....
</div>
Put up (similar to this) in your ajax:
if (response.status == "SUCCESS") {
userInfo = "<ol>";
for (i = 0; i < response.result.length; i++) {
userInfo += "<br><li><b>Firstname</b> : "
+ response.result[i].firstname
+ response.result[i].lastname;
}
And
errorInfo = "";
for (i = 0; i < response.result.length; i++) {
errorInfo += "<br>" + (i + 1) + ". "
+ response.result[i].code;
}
$('#error').html(
"<b>Errors found during validation : </b>"
+ errorInfo);
I created a small piece of code for this. You can take a look here on GitHub.

Vue Component with Stripe JS v3

I am using Vue component for my checkout form.
The stripe js (v3) file was included in the header section.
The form was in Component
This component has two section. One is to get payment details from the user and another is to submit card details.
<template>
<div class="payment_form">
<div id="payment_details" v-if="showPaymentDetails">
<!-- User input goes here. Like username phone email -->
</div>
<div id="stripe-form" v-if="showStripeForm">
<form action="/charge" method="post" id="payment-form" #submit.prevent="createStripeToken()">
<div class="form-row">
<label for="card-element">
Credit or debit card
</label>
<div id="card-element">
<!-- a Stripe Element will be inserted here. -->
</div>
<!-- Used to display Element errors -->
<div id="card-errors" role="alert"></div>
</div>
<button>Submit Payment</button>
</form>
</div>
</div>
</template>
<script>
import { Validator } from 'vee-validate';
export default {
data() {
return {
stripeToken: '',
showPaymentDetails: true,
showStripeForm: true,
}
},
created() {
},
methods: {
validateForm() {
self = this;
this.$validator.validateAll().then(result => {
if (result) {
// eslint-disable-next-line
alert('From Submitted!');
console.log(this.$data);
axios.post('/data',{
name:this.name,
})
.then(function (response) {
self.showStripeForm = true;
console.log(response);
})
.catch(function (error) {
console.log(error);
});
return;
}
});
},
createStripeToken(){
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
window.stripe.createToken(card).then(function(result) {
if (result.error) {
// Inform the user if there was an error
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server
console.log(result.token);
}
});
});
},
initStripe(){
window.stripe = Stripe('stripe_test_key_here');
var elements = stripe.elements();
var style = {
base: {
// Add your base input styles here. For example:
fontSize: '16px',
lineHeight: '24px'
}
};
// Create an instance of the card Element
window.card = elements.create('card', {style: style});
// Add an instance of the card Element into the `card-element` <div>
window.card.mount('#card-element');
}
},
mounted() {
this.initStripe();
setTimeout(function () {
this.showStripeForm = false;
},2000);
}
}
</script>
I try to load the stripe form on page load and try to disable the element via showStripeForm.
But vue unset the loaded stripe card form from the stripe server and saved the dom to its original state.
So i can't trigger the stripe form on the axios callback.
I don't want to user stripe checkout and stripe js v1(getting input on your own form is deprecated after this version).
In mounted. Change the setTimeout callback to an arrow function, otherwise, this will point to Window instead of Vue.
mounted() {
setTimeout(() => {
this.showStripeForm = false
}, 2000)
}
Also, the way you access the DOM is not so Vue-ish. You could use ref on the DOM element you want to use in your code. For example:
<form action="/charge" method="post" ref="payment-form" #submit.prevent="createStripeToken()">
Then access it from $refs like this:
var form = this.$refs['payment-form']
/*
Same result as document.getElementById('payment-form')
but without using an id attribute.
*/

Client-Side form validation with Vue 2

I am developing an application with with Laravel 5.3 and Vue 2 I implemented a form-validation via Vue 2 but the it doesn't check the request for any errors on client-side it firstly lets every single request to be sent to the server and then displays the errors which are sent back from the server, in the following you can find my code
class Form {
constructor(data) {
this.originalData = data;
for (let field in data) {
this[field] = data[field];
}
this.errors = new Errors();
}
data() {
let data = {};
for (let property in this.originalData) {
data[property] = this[property];
}
return data;
}
reset() {
for (let field in this.originalData) {
this[field] = '';
}
this.errors.clear();
}
post(url) {
return this.submit('post', url);
}
submit(requestType, url) {
return new Promise((resolve, reject) => {
axios[requestType](url, this.data())
.then(response => {
this.onSuccess(response.data);
resolve(response.data);
})
.catch(error => {
this.onFail(error.response.data);
reject(error.response.data);
});
});
}
onSuccess(data) {
alert(data.message);
this.reset();
}
onFail(errors) {
this.errors.record(errors);
}
}
and this is the `Vue class
` `new Vue({
el: "#form",
data: {
form: new Form({
name: '',
description: ''
})
},
methods: {
onSubmit() {
this.form.post('/admin/category');
//.then(response => alert('Item created successfully.'));
//.catch(errors => console.log(errors));
},
onSuccess(response) {
alert(response.data.message);
form.reset();
}
}
});`
and this is my HTML form
<form method="post" id="form" action="{{ url('admin/category') }}" '
#submit.prevent="onSubmit" #keydown="form.errors.clear($event.target.name)">
{{ csrf_field() }}
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" name="name"
v-model="form.name" placeholder="Name">
<span class="help is-danger"
v-if="form.errors.has('name')" v-text="form.errors.get('name')"></span>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control" id="description" name="description"
v-model="form.description" placeholder="Description"></textarea>
<span class="help is-danger"
v-if="form.errors.has('description')"
v-text="form.errors.get('description')"></span>
</div>
<button type="submit" class="btn btn-default" :disabled="form.errors.any()">Create New Category</button>
</form>
Question:
I need to modify the code in a way that firstly it checks on client side and if it finds any errors prevent the request from being sent to the server not letting every single request to be sent to server and then displays the errors sent back from the server
You can use any type of client side validation with your setup, just before you submit the form you can take the data from the Form class and validate it with vanilla javascript or any other validation library.
Let's use validate.js as an example:
in your onSubmit method you can do:
onSubmit() {
//get all the fields
let formData = this.form.data();
//setup the constraints for validate.js
var constraints = {
name: {
presence: true
},
description: {
presence: true
}
};
//validate the fields
validate(formData, constraints); //you can use this promise to catch errors
//then submit the form to the server
this.form.post('/admin/category');
//.then(response => alert('Item created successfully.'));
//.catch(errors => console.log(errors));
},

Meteor: Error: {{#each}} currently only accepts arrays, cursors or falsey values

I keep getting this error message when I click on the send button. Im trying to create a Instant Messenger app where online users can chat one on one. I am a beginner and I would really appreciate any help. Here is my error message, again it appears in the console once I click the Send button.
Exception from Tracker recompute function: meteor.js:862 Error:
{{#each}} currently only accepts arrays, cursors or falsey values.
at badSequenceError (observe-sequence.js:148)
at observe-sequence.js:113
at Object.Tracker.nonreactive (tracker.js:597)
at observe-sequence.js:90
at Tracker.Computation._compute (tracker.js:331)
at Tracker.Computation._recompute (tracker.js:350)
at Object.Tracker._runFlush (tracker.js:489)
at onGlobalMessage (meteor.js:347)
Here is my HTML
<template name="chat_page">
<h2>Type in the box below to send a message!</h2>
<div class="row">
<div class="col-md-12">
<div class="well well-lg">
{{#each messages}}
{{> chat_message}}
{{/each}}
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<form class="js-send-chat">
<input class="input" type="text" name="chat" placeholder="type a message here...">
<input type="submit" value="Send">
</form>
</div>
</div>
</template>
<!-- simple template that displays a message -->
<template name="chat_message">
<div class = "container">
<div class = "row">
<img src="/{{profile.avatar}}" class="avatar_img">
{{username}} said: {{text}}
</div>
</div>
<br>
</template>
Client Side
Template.chat_page.helpers({
messages: function () {
var chat = Chats.findOne({ _id: Session.get("chatId") });
return chat.messages;
},
other_user: function () {
return "";
}
});
Template.chat_page.events({
'submit .js-send-chat': function (event) {
console.log(event);
event.preventDefault();
var chat = Chats.findOne({ _id: Session.get("chatId") });
if (chat) {
var msgs = chat.messages;
if (! msgs) {
msgs = [];
}
msgs.push({ text: event.target.chat.value });
event.target.chat.value = "";
chat.messages = msgs;
Chats.update({ _id: chat._id }, { $set : { messages: chat } });
Meteor.call("sendMessage", chat);
}
}
});
Parts of the server side
Meteor.publish("chats", function () {
return Chats.find();
});
Meteor.publish("userStatus", function () {
return Meteor.users.find({ "status.online": true });
});
Meteor.publish("userData", function () {
if (this.userId) {
return Meteor.users.find({ _id: this.userId },{ fields: { 'other': 1, 'things': 1 } });
} else {
this.ready();
}
return Meteor.users.find({ "status.online": true });
});
Meteor.publish("users", function () {
return Meteor.users.find({ "status.online": true });
});
Chats.allow({
insert: function () { return true; },
update: function () { return true; },
remove: function () { return true; }
});
Meteor.methods({
sendMessage: function (chat) {
Chats.insert({
chat: chat,
createdAt: new Date(),
username: Meteor.user().profile.username,
avatar: Meteor.user().profile.avatar,
});
}
});
Chances are your subscriptions aren't ready. This means that Chats.findOne() will return nothing, meaning that Chats.findOne().messages will be undefined.
Try the following:
{{ #if Template.subscriptionsReady }}
{{#each messages}}
{{/each}}
{{/else}}
Alternatively, use a find() on chats, then {{#each}} on the messages within that chat. For example:
Template['Chat'].helpers({
chats: function () {
return Chats.find(Session.get('chatId')); // _id is unique, so this should only ever have one result.
}
});
Then in template:
{{#each chats}}
{{#each messages}}
{{>chat_message}}
{{/each}}
{{/each}}
I think there might be a logical error in this line
Chats.update({ _id : chat._id }, { $set : { messages : chat } });
You are setting the value of the field messages to chat. But chat is an object. So in your helper when you are returning Chats.findOne().messages to the {{#each}} block, you are actually returning an object which is not a valid value to be sent to an {{#each}} block and hence the error.
I think what you mean to do is
Chats.update({ _id : chat._id }, { $set : { messages : msgs } });

Resources