Why is `this` undefined in a Blaze event handler? - javascript

I'm stuck in a reply function to intern messages: the email reply-sending function works fine (if I choose manually in the code the to field), but I'm failing, when I choose the message to reply, to select automatically the email in the contact-messages collection (field email) with my Meteor.methods.
In few words :
var to = "bob#bob.com" => ok
var to = this.email => no value catched
Here below my event on the reply form submit and the method
Event (can't catch var to = this.email)
Template.ContactReplyModal.events({
'click .send-message':function(e) {
e.preventDefault();
Meteor.call('replyMessage', this._id, function(error) {
if(error) {
Bert.alert({
title: 'Error',
message: error.reason,
type: 'danger'
});
} else {
var to = this.email;
var from = "my#mail.com";
var subject = $('#reply-subject').val();
var message = $('#reply-message').val();
if(message != '' && subject != '') {
Meteor.call('sendEmailContact', to, from, subject, message, function (error) {
if(error) {
Bert.alert({
title: 'Error',
message: error.reason,
type: 'danger'
});
} else {
$('#reply-message').val('');
$('#reply-subject').val('');
Bert.alert({
title: 'Success',
message: 'Message sended.',
type: 'success'
});
}
});
} else {
Bert.alert({
title: 'Error',
message: 'Message error.',
type: 'danger'
});
}
}
});
},
//Close events for ContactReplyModal
'click .close-login': ()=> {
Session.set('nav-toggle-contactreply', '');
},
'click .modal-overlay-contactreply': ()=> {
Session.set('nav-toggle-contactreply', '');
}
});
Method (using here the replyMessage function)
//Contact Method
Meteor.methods({
insertMessage: function(message) {
ContactMessages.insert(message);
},
openMessage: function(messageId) {
ContactMessages.update({_id: messageId}, {$set: {new: false}});
},
replyMessage: function(messageId) {
ContactMessages.findOne({_id: messageId});
},
deleteMessage: function(messageId) {
ContactMessages.remove({_id: messageId});
}
});
EDIT
The bind of the variable email with an arrow function doesn't work.
So maybe it is an issue of capturing the variable?
I cant' read console.log (this); and console.log (this.email); says undefined.
Here below is my message collection.
"_id": "6c3478WugEajr6zaw",
"name": "bob",
"email": "bob#bob.com",
"message": "This is a try.",
"submitted": "2017-01-05T15:19:04.642Z",
"new": true
I really don't understand, cause this below event works on the openMessage method (so the right message is correctly identified from the others)
//CLIENTSIDE
'click .open-message':function() {
Meteor.call('openMessage', this._id, function(error) {
if(error) {
Bert.alert({
title: 'Error',
message: error.reason,
type: 'danger'
});
}
});
}
//SERVERSIDE
Meteor.methods({
insertMessage: function(message) {
ContactMessages.insert(message);
},
openMessage: function(messageId) {
ContactMessages.update({_id: messageId}, {$set: {new: false}});
},
replyMessage: function(message) {
ContactMessages.findOne({_id: message});
},
deleteMessage: function(messageId) {
ContactMessages.remove({_id: messageId});
}
});
EDIT 2
As asked, below the template & the js linked to. The method is already showed and an example of the data in collection too.
template (contact-reply.html)
<template name="ContactReply">
<h3>Reply</h3>
<h3>To: {{email}}</h3>
<input class="form-control" type="text" name="reply-subject" id="reply-subject" placeholder="Subject"/>
<br>
<textarea class="form-control" name="reply-message" id="reply-message" rows="6"></textarea>
<br>
<button class="btn btn-success send-message">Send</button>
</template>
<template name="ContactReplyModal">
<div class="contactreply-modal {{$.Session.get 'nav-toggle-contactreply'}}">
<i class="fa fa-close close-login"></i>
<h3>Send a reply</h3>
{{> ContactReply}}
</div>
<div class="modal-overlay-contactreply"></div>
</template>
js of the template (contact-reply.js)
import './contact-reply.html';
Template.ContactReplyModal.events({
'click .send-message':function(e) {
e.preventDefault();
console.log(this);
Meteor.call('replyMessage', this._id, (error) => {
if(error) {
Bert.alert({
title: 'Error',
message: error.reason,
type: 'danger'
});
} else {
console.log (this.email);
const to = this.email;
var from = "my#mail.com";
var subject = $('#reply-subject').val();
var message = $('#reply-message').val();
if(message != '' && subject != '') {
Meteor.call('sendEmailContact', to, from, subject, message, (error) => {
if(error) {
Bert.alert({
title: 'Error',
message: error.reason,
type: 'danger'
});
} else {
$('#reply-message').val('');
$('#reply-subject').val('');
Bert.alert({
title: 'Success',
message: 'Message sended.',
type: 'success'
});
}
});
} else {
Bert.alert({
title: 'Error',
message: 'Message error.',
type: 'danger'
});
}
}
});
},
//Close events for ContactReplyModal
'click .close-login': ()=> {
Session.set('nav-toggle-contactreply', '');
},
'click .modal-overlay-contactreply': ()=> {
Session.set('nav-toggle-contactreply', '');
}
});

First, it is important to be sure that the data context is correct.
Each element within the template is rendered with a certain data context. If you target them in a template event handler, the data context will be available to the handler via this.
If you target an element that is not rendered by the current template (e.g, rendered by a third-party library or belongs to a sub-template), it will not have a data contest, which is what causes your data context to be undefined).
Having that fixed, assuming the data context (the external function's this) is indeed what you expect in the event handler (i.e, has an email field), you need to make it available to the callback. You can capture it in a local variable and make it available in a closure or lexically bind it with an arrow function:
Template.ContactReplyModal.events({
'click .send-message':function(e) {
e.preventDefault();
console.log(this); // to make sure that it is what you are expecting.
Meteor.call('replyMessage', this._id, (e) => { // note the arrow function
if(e) {
// ...
} else {
const to = this.email;
// ...
if(message != '' && subject != '') {
Meteor.call('sendEmailContact', to, from, subject, message, (e) => {
if(e) {
// ...
} else {
// ...
}
});
} else {
// ...
}
}
});
},
// ...
});
However, it does not seem like a good idea to use multiple nested method calls. It would probably be better to do it all in a single method call.

You can not access the template variable in template events using this, you can access them by the 2nd parameter in your events, here is your code, hope it will work
Template.ContactReplyModal.events({
'click .send-message'(e, instance) {
e.preventDefault();
Meteor.call('replyMessage', this._id, function(error) {
if(error) {
Bert.alert({
title: 'Error',
message: error.reason,
type: 'danger'
});
} else {
var to = instance.email;
var from = "my#mail.com";
var subject = $('#reply-subject').val();
var message = $('#reply-message').val();
if(message != '' && subject != '') {
Meteor.call('sendEmailContact', to, from, subject, message, function (error) {
if(error) {
Bert.alert({
title: 'Error',
message: error.reason,
type: 'danger'
});
} else {
$('#reply-message').val('');
$('#reply-subject').val('');
Bert.alert({
title: 'Success',
message: 'Message sended.',
type: 'success'
});
}
});
} else {
Bert.alert({
title: 'Error',
message: 'Message error.',
type: 'danger'
});
}
}
});
},
});

Related

XMLHttpRequest open() function is not working even I use a copy of the same code in another part of my system

I´m going to try to explain what my problem is.
I have this code
const deleted = document.querySelector('form#delete');
const edited = document.querySelector('form#edit');
eventListeners();
function eventListeners() {
deleted.addEventListener('submit', markDeleted);
edited.addEventListener('submit', userEdit);
}
function markDeleted(event) {
event.preventDefault();
console.log('^*^*^ delete *^*^*');
var xhr = new XMLHttpRequest();
xhr.open('POST', '/models/comm.model.php', true);
console.log('^*^*^ after xhr.open *^*^*');
// SOME MORE CODE ...
My problem is where I open the communication
xhr.open('POST', '/models/comm.model.php', true);
because never executes comm.model.php file even I use the same file
to login and register new users
this is my comm.model.php file
include_once $_SERVER['DOCUMENT_ROOT'] . '/config/constants.php';
print_r("inside comm model");
$accion = filter_var($_POST['tipoAccion'], FILTER_SANITIZE_STRING);
if ($accion === 'login') {
// some code ...
}
if ($accion === 'register') {
// some code ...
}
if ($accion === 'userDelete') {
print_r($accion);
}
Here is the browser console image
So you can see there is no problem with comm.model.php file
thank you for any help
#epascarello
here is the complete code you asked for
function markDeleted(event) {
event.preventDefault();
console.log('^*^*^ delete *^*^*');
var xhr = new XMLHttpRequest();
xhr.open('POST', '/models/comm.model.php', true);
console.log('^*^*^ after xhr.open *^*^*');
xhr.onload = function () {
console.log('^*^*^ inside onload *^*^*');
if (this.status === 200) {
console.log('^*^*^ responseText no parse *^*^*');
console.log(xhr.responseText);
respuesta = JSON.parse(xhr.responseText);
switch (respuesta[0]) {
case 'nok':
Swal.fire({
title: 'Error',
text: 'El usuario NO pudo ser eliminado',
icon: 'error',
showClass: {
popup: 'animate__animated animate__zoomIn'
},
hideClass: {
popup: 'animate__animated animate__zoomOut'
}
})
break;
case 'ok':
if (respuesta[2] === 'userDelete') {
Swal.fire({
title: 'Usuario eliminado correctamente',
icon: 'success',
showClass: {
popup: 'animate__animated animate__zoomIn'
},
hideClass: {
popup: 'animate__animated animate__zoomOut'
}
}).then((resultado) => {
console.log(resultado.value);
if (resultado.value) {
window.location.href = '/master.php';
}
})
} else {
Swal.fire({
title: 'Error',
text: 'El usuario NO pudo ser eliminado',
icon: 'error',
showClass: {
popup: 'animate__animated animate__rotateIn'
},
hideClass: {
popup: 'animate__animated animate__rotateOut'
},
showConfirmButton: false,
timer: 2500
})
}
break;
// default:
// break;
}
xhr.send(usuario);
}
}
}

Error in generating invoice from sales order in suitescript 2.0?

I tried to include a button (created from user event) on Sales order. Upon clicking it, Invoice will be generated. As soon as the button is hit, ther comes an error and invoice doesnt get generated. Can anyone help me with this?
//Client script
function pageInit() {
}
function csForButton(ctx) {
var rec = curr.get();
var customer = rec.getValue({ "fieldId": "customer" });
log.error({
title: 'customer',
details: customer
});
var scriptURL = url.resolveScript({
"scriptId": "customscript_button_task_sl",
"deploymentId": "customdeploy_button_task_sl"
});
console.log('scriptURL', scriptURL);
window.onbeforeunload = null;
window.open(scriptURL + '&id=' + rec.id);
}
return {
pageInit: pageInit,
csForButton: csForButton
};
//User Event Script
function beforeLoad(ctx) {
//if (context.type == context.UserEventType.VIEW) {
addbutton(ctx);
// }
}
function addbutton(ctx) {
try {
var rec = ctx.newRecord;//record object, now we can get its properties through it(e.g. fields)
var statusOfTrans = rec.getValue({ fieldId: 'status' });
log.error('statusOfTrans', statusOfTrans);
ctx.form.clientScriptFileId = "16474"
if (ctx.type == "view" && statusOfTrans == 'Pending Fulfillment') {
ctx.form.addButton({
id: 'custpage_make_invoice',
label: 'create invoice!',
functionName: 'csForButton'
});
}
}
catch (error) {
log.error('addbutton', error);
}
}
return {
beforeLoad: beforeLoad,
}
//Suitelet Script
function onRequest(ctx) {
try {
var req = ctx.request;
var res = ctx.response;
if (req.method == 'GET') {
var objRecord = rec.transform({
fromType: rec.Type.SALES_ORDER,
fromId: req.parameters.id,
toType: rec.Type.INVOICE,
isDynamic: true,
});
objRecord.save({
ignoreMandatoryFields: true
});
res.write({output: 'Invoice created'});
}
} catch (error) {
log.error('onRequest', error);
}
}
return {
'onRequest': onRequest
}
error (in suitelet):
{"type":"error.SuiteScriptError","name":"USER_ERROR","message":"You must enter at least one line item for this transaction.","stack":["anonymous(N/serverRecordService)","onRequest(/SuiteScripts/button SL.js:39)"],"cause":{"type":"internal error","code":"USER_ERROR","details":"You must enter at least one line item for this transaction.","userEvent":null,"stackTrace":["anonymous(N/serverRecordService)","onRequest(/SuiteScripts/button SL.js:39)"],"notifyOff":false},"id":"","notifyOff":false,"userFacing":false}
As Error says to include atleast 1 line but i wanted it to be generated automatically. I am new in suitescript and taking all the help from the documentation. Can anyone jst guide me what is wrong i m doing?
Thank u
You need the SO status to be Pending Billing. If the status of the SO is Pending Fulfillment, then no line items are ready to be invoiced.

Ionic popup alert not closing properly

The first alert verifies whether the entered password corresponds to the user's password
If it does, then opens another alert where the user change the password if he has entered the same password in both fields
And finally, the third alert will open if it has successfully changed the password
The problem occurs if I click Cancel in the second alert or after confirming the third alert
After that, I'm not able to click on anything inside the app until I unload and restart the same application
So I guess the problem occurs because the alert is not closed properly
Here is my code:
$scope.changePass = function () {
$scope.newitem = {}
var myPopup = $ionicPopup.alert({
template: '<input type="password" placeholder="password" ng-model="newitem.password">',
title: 'Insert your password',
scope: $scope,
buttons: [
{ text: 'Cancel' },
{
text: '<b>Confirm</b>',
type: 'button-positive',
onTap: function(e) {
if (!$scope.newitem.password) {
console.log("preventing default");
e.preventDefault();
} else {
if($scope.newitem.password == $scope.user.password) {
$scope.new = {}
var newPass = $ionicPopup.alert({
template: '<input type="password" placeholder="password" ng-model="new.newpass"><br><input type="password" placeholder="Repeat password" ng-model="new.repeatpass">',
title: 'Insert your new password',
scope: $scope,
buttons: [
{ text: 'Cancel' },
{
text: '<b>Confirm</b>',
type: 'button-positive',
onTap: function(e) {
if (!$scope.new.newpass) {
console.log("preventing default");
e.preventDefault();
} else {
if (!$scope.new.repeatpass) {
$scope.new.newpass = "";
console.log("preventing default");
e.preventDefault();
} else {
if ($scope.new.newpass == $scope.new.repeatpass) {
$scope.user.password = $scope.new.newpass;
var uri = "http://someLink" + $window.localStorage.id;
$http({
method: 'PUT',
url: uri,
headers: {"Content-Type": "application/json;charset=UTF-8"},
data: $scope.user
}).success(function() {
var succesResponse = $ionicPopup.alert({
title: 'Ok',
template: "Password has changed"
});
succesResponse;
e.preventDefault();
});
}
else {
$scope.new.newpass = "";
$scope.new.repeatpass = "";
e.preventDefault();
}
}
}
}
}
]
});
}
else {
$scope.newitem.password = "";
e.preventDefault();
}
}
}
}
]
});
}
I found the answer to my question
Namely, the solution is to close the first alert before I open the second alert
But before I open second alert, it is necessary to have a timeout to close the first alert properly
myPopup.close();
$timeout(function() {
$scope.new = {}
var newPass = $ionicPopup.alert({...});
}, 500);

Meteor AutoForm stops proceeding submit

I would like to create a form, using the autoform package for Meteor, for my CAS_Entry collection. The code can be seen below. I also added the defined hooks, of which unfortunately only beginSubmit and before are executed and no entry is added to the collection. Using Meteor shell, the insert works like a charm.
I am grateful for any hint.
addCasEntry.html, Template for displaying the form:
{{#autoForm collection="CAS_Entry" type="insert" id="addCasEntryForm"}}
{{> afQuickField name="type" options="allowed"}}
{{> afQuickField name="description" rows="6" type="textarea"}}
{{> afQuickField name="file" type="cfs-file" collection="Images"}}
{{> afQuickField name="date" }}
<button type="submit" class="btn btn-primary">Add</button>
{{/autoForm}}
addCasEntry.js, adding debugging hooks:
AutoForm.hooks({
addCasEntryForm: {
before: {
insert: function(doc) {
console.log(doc);
}
},
after: {
insert: function(error, result) {
console.log('Occured error: ' + error);
}
},
beginSubmit: function() {
console.log('begin submit');
},
onSuccess: function(formType, result) {
console.log("Insert succeeded");
console.log('Result ' + result);
},
onError: function(formType, error) {
console.log('Error!!!');
console.log(error);
}
}
});
SimpleSchema.debug = true;
/lib/collection/cas_entry.js:
CAS_Entry = new Mongo.Collection("cas_entries");
CAS_Entry.attachSchema(new SimpleSchema({
type: {
type: String,
allowedValues: ['reflection', 'evidence']
},
description: {
type: String,
optional: true
},
file: {
type: String,
optional: true,
},
timeUploaded: {
type: Date,
optional: true,
autoValue: function() {
return new Date();
}
},
date: {
type: Date,
}
}));
CAS_Entry.allow({
'insert': function() {
return true;
},
'update': function() {
return true;
}
});
And here is the console output:
Your form won't be submitted because you are not returning or passing the document to this.result(); inside your before hook.
AutoForm.hooks({
addCasEntryForm: {
// ...
before: {
insert: function(doc) {
console.log(doc);
return doc;
}
}
// ...
}
});
According to the documentation, you should use one of the following statements depending on your defined preconditions:
Synchronous, submit: return doc;.
Synchronous, cancel: return false;.
Asynchronous, submit: this.result(doc);.
Asynchronous, cancel: this.result(false);.

I am unable to execute validation in Backbone.js.

Whenever I set the age attribute to negative value it doesn't return false.
I have also tried executing this code in the console and still nothing happens
<script>
var Human = Backbone.Model.extend({
// If you return a string from the validate function,
// Backbone will throw an error
defaults: {
name: 'Guest user',
age: 23,
occupation: 'worker'
},
validate: function( attributes ){
if( attributes.age < 0){
return "Age must me positive";
}
if( !attributes.name ){
return 'Every person must have a name';
}
},
work: function(){
return this.get('name') + ' is working';
}
});
var human = new Human;
human.set("age", -10);
human.on('error', function(model, error){
console.log(error);
});
</script>
There are a few things wrong with your code:
The event for validation is invalid, error is for ajax requests.
Validation on set doesn't happen by default, you need to pass { validate: true } as an option.
You are listening to the event AFTER setting, so it won't get called for that set.
i.e:
human.on('invalid', function(model, error) {
console.log(error);
});
human.set("age", -10, { validate: true });

Categories