I need to execute an ajax call after the user moves a node in a jsTree using the dnd plugin. If that ajax call returns an error, I need to either prevent the move from happening, or reverse the action altogether. From the move_node event listener, is there a way to tell the dnd plugin to reverse the drop or somehow cancel the action? I've tried returning false and setting e.preventDefault() and e.stopImmediatePropagation() but nothing seems to work.
$($tree).jstree({
core: {
data: $treeData,
check_callback: function (op, node, parent, position, more) {
switch (op) {
case 'move_node':
// moveNode() validates the move based on
// some data stored in each node, but doesn't
// (and shouldn't) make an ajax call to determine
// if the node *actually can* be moved
return moveNode(node, parent, position, more);
}
}
},
plugins: ['dnd']
}).on('move_node.jstree', function(e, data) {
// moveNode() returned true and the user moved the node
if (!doMoveNode(data)) {
// none of this works!!
e.preventDefault();
e.stopImmediatePropagation();
return false;
}
});
I figured this out... the key is to intercept the move before it happens, which can be done in the check_callback as so:
function doMoveNode (node, parent) {
// tell the server to move the node
var nodeID = node.original.appData.id,
parentID = parent.original.appData.id,
success = false;
// make a non-async call to move the node
$.ajax({
url: move-product.php',
type: 'post',
async: false,
data: {
nodeID: nodeID,
parentID: parentID
},
dataType: 'json',
success: function (json) {
if (json.errorCode === 0) {
// this happens when the server was happy
success = true;
}
},
error: function (xhr) {
alert('an error occurred');
}
});
return success;
}
function moveNode (node, parent, position, more) {
// perform client-side validation to see if we can move node into parent
// just a demo, so always return true
return true;
}
$($tree).jstree({
core: {
data: $treeData,
check_callback: function (op, node, parent, position, more) {
switch (op) {
case 'move_node':
if (more && more.core) {
// this is the condition when the user dropped
// make a synchronous call to the server and
// return false if the server rejects the action
return doMoveNode(node, parent);
}
// return true if we can move, false if not
return moveNode(node, parent, position, more);
}
}
},
plugins: ['dnd']
}).on('move_node.jstree', function(e, data) {
// the move already happened
});
Related
As described here https://developers.payrexx.com/docs/mobile-apps-javascript
I would like to interact with the javascript events of an iframe I want to create in the webview_flutter plugin.
The following example code is given in the official documentation
window.addEventListener('message', handleMessage(this), false);
and
function handleMessage(e) {
if (typeof e.data === 'string') {
try {
var data = JSON.parse(e.data);
} catch (e) {}
if (data && data.payrexx) {
jQuery.each(data.payrexx, function(name, value) {
switch (name) {
case 'transaction':
if (typeof value === 'object') {
if (value.status === 'confirmed') {
//handling success
} else {
//handling failure
}
}
break;
}
});
}
}
}
Do you know a way to do this? I have implemented an iframe in which there is the address of my gateway, but it is impossible to check if the payment has taken place.
Sounds good. The Payrexx iFrame sends a post message with the transaction details (including transaction status) to the parent window (e.g. your Flutter webview) after the payment (on the Payrexx result page). So you only need to add an event listener for type "message" in your webview as in the example:
window.addEventListener('message', handleMessage(this), false);
Please make sure you also send a post message into the Payrexx iFrame as soon as the iFrame is loaded (onload event):
let iFrame = document.getElementById('IFRAME-ID');
if (iFrame) {
iFrame.contentWindow.postMessage(
JSON.stringify({
origin: window.location.origin,
}),
iFrame.src,
);
}
Now you are ready to receive and handle the messages from the Payrexx iFrame:
private handleMessage(e): void {
try {
let message = JSON.parse(e.data);
if (typeof message !== 'object' ||
!message.payrexx ||
!message.payrexx.transaction) {
return;
}
let transaction = message.payrexx.transaction;
console.log(transaction);
} catch (e) {
}
};
Last but not least:
Make sure you also check the transaction status via transaction webhook (server-to-server notification):
https://docs.payrexx.com/developer/guides/webhook
Being new to Meteor JS, I'm confused on how to update a record. I have 2 templates AddSchoolLayout and Schoolcontactlayout, AddSchoollayout creates the record in Mongo db, now, for the same school I want to add its address still on the same DB NewSchoolDB but as I tried this I keep getting the error below about wrong ID. What wrong am I to right?
Note that my language might not correlate with Mongo's; I'm still fresh, coming from the SQL background.
This is the method.js where the record is been saved.
//methods.js
if (Meteor.isServer) {
Meteor.methods({
SchoolRegister: function (phone, schoolemail) {
if (!Meteor.userId()) {
throw new Meteor.error('Not authorized');
return false;
}else{
NewSchoolDB.insert({
authorId: Meteor.userId(),
phone: phone,
schoolemail
});
}
}
});
}
This is the event for saving a new school
//add school
Template.AddSchoolLayout.events({
'submit .addnewschool': function (event, template) {
event.preventDefault();
var newschoolname = trimInput(event.target.newschoolname.value);
if (isNotEmpty(newschoolname)) {
Meteor.call('SchoolRegister', newschoolname,
function (error, response) {
if (error) {
Bert.alert(error.reason, 'danger', 'growl-top-right');
return false;
}else{
Bert.alert("School successfully created", "success", "growl-top-right");
FlowRouter.redirect('/schoolcontact');
}
});
}
return false;
}
});
This is where I want to update the school address
//school contact
Template.SchoolContactLayout.events({
'submit .contactschool': function (event) {
event.preventDefault();
var phone = trimInput(event.target.phone.value);
if (isNotEmpty(phone)) {
Meteor.call('SchoolRegister', phone, function (error, response) {
if (error) {
Bert.alert(error.reason, 'danger', 'growl-top-right');
return false;
}else{
Bert.alert('School address updated successfully', 'success', 'growl-top-right');
FlowRouter.redirect('/logo-upload');
}
});
}
return false;
}
});
Error logged on the console
I20170524-17:44:14.051(1)? at packages/ddp-server/livedata_server.js:559:43
I20170524-17:51:54.678(1)? Exception from sub NewSchoolDB id onFTu2j3xRmbqC5WF TypeError: this.userId is not a function
I20170524-17:51:54.710(1)? at [object Object]._handler (lib/pulbish/published.js:3:13)
I20170524-17:51:54.712(1)? at maybeAuditArgumentChecks (packages/ddp-server/livedata_server.js:1737:12)
I20170524-17:51:54.713(1)? at [object Object]._.extend._runHandler (packages/ddp-server/livedata_server.js:1035:17)
I20170524-17:51:54.714(1)? at [object Object]._.extend._startSubscription (packages/ddp-server/livedata_server.js:853:9)
I20170524-17:51:54.715(1)? at [object Object]._.extend.protocol_handlers.sub (packages/ddp-server/livedata_server.js:625:12)
I20170524-17:51:54.719(1)? at packages/ddp-server/livedata_server.js:559:43
Your SchoolRegister method accepts 2 arguments: phone and schoolmail. When you create the school you call the methods with one argument called newschoolname. So something is wrong here with your naming convention, but it shouldn't really matter regarding your question.
With MongoDB, you use insert to add a new record to your collection. If you need to update it, use update. So one way to solve your problem is to:
In AddSchoolLayout, call your method to insert the document the first time. NewSchoolDB.insert(...) will return the ID of the created record. Keep that ID and pass it to your next page, for exemple in the URL.
In your next page, SchoolContactLayout, you need to call a new method which is going to update your school, using the ID returned by the previous method. This new method will look something like this:
SchoolUpdate: function (schoolId, phone) {
if (!Meteor.userId()) {
throw new Meteor.error('Not authorized');
return false;
}else{
NewSchoolDB.update(schoolId, { $set: { phone } });
}
}
The first argument of the update MongoDB function is the ID of the record you want to update. The second arguments are the modifiers you want to use to update your record. Here is the full list of modifier you can use: update modifiers
EDIT: how to pass ID from one layout to another:
I didn't use FlowRouter for a while and I can't test it right now so you may have to do some correction, but here is how I would do it to give you an idea:
First you need to define your route SchoolContactLayout with something like this:
FlowRouter.route('/schoolcontact/:schoolId', {
name: 'schoolContact',
});
This adds a schoolId parameter to the route.
In your SchoolRegister method, get the return ID and return it:
var id = NewSchoolDB.insert({
authorId: Meteor.userId(),
schooleName
});
return { id }
Edit your redirection FlowRouter.redirect('/schoolcontact'); with FlowRouter.go('/schoolcontact/' + response.id);
You can then edit your contactSchool event with something like this:
Template.SchoolContactLayout.events({
'submit .contactschool': function (event) {
event.preventDefault();
var schoolId = FlowRouter.getParam('schoolId');
var phone = trimInput(event.target.phone.value);
if (isNotEmpty(phone)) {
Meteor.call('SchoolUpdate', schoolId ,phone, function (error, response) {
if (error) {
Bert.alert(error.reason, 'danger', 'growl-top-right');
return false;
}else{
Bert.alert('School address updated successfully', 'success',
'growl-top-right');
FlowRouter.redirect('/logo-upload');
}
});
}
return false;
}
});
Notice the var schoolId = FlowRouter.getParam('schoolId'); to get the ID from URL parameter so I can use it in the update method.
I have to show only selected nodes and parents, and hide rest nodes.
There is no get_unchecked in current documentation. My json format is huge so taking it in string and formatting and reloading tree will be inefficient.
Is there a way to get all unchecked nodes and hide it?
if there is i can check all parent node of current checked node and then just hide all unchecked nodes, but i cannot find any method to retrive all unchecked nodes.
You can do it by:
getting all selected nodes along with the parents
going over all nodes and seeing if a node is not selected, then hiding it
Check code below and demo - codepen.
var $tree = $("#myTree").jstree(),
nodesSelected = $('#myTree').jstree('get_checked', true),
nodeIdsToStay = [];
nodesSelected.forEach(function(node) {
var path = $tree.get_path(node, false, true);
path.forEach(function(n) {
if (nodeIdsToStay.indexOf(n) === -1) {
nodeIdsToStay.push(n);
}
})
})
$('#myTree').find('li').each(function() {
if (nodeIdsToStay.indexOf(this.id) === -1) {
$(this).hide();
}
})
Work around for the question i made.
$('#jstree_demo_div').on('loaded.jstree', function(e, data) {
console.log("loaded");
checked_ids = $('#jstree_demo_div').jstree('get_selected', true);
$("#jstree_demo_div").jstree('select_all');
$.each(checked_ids, function(index, value) {
$("#jstree_demo_div").jstree('deselect_node', value);
uiParentsShow(value);
});
checked_ids = $('#jstree_demo_div').jstree('get_selected', true);
$.each(checked_ids, function(index, value) {
$("#jstree_demo_div").jstree('hide_node', value);
});
});
function uiParentsShow(node) {
try {
var parent = $("#jstree_demo_div").jstree('get_parent', node);
$("#jstree_demo_div").jstree('deselect_node', parent);
if (parent != '#') {
uiParentsShow1(parent);
}
} catch (err) {
console.log('Error in uiGetParents' + err);
}
}
Steps:
Step 1: Selecting All the elements
Step 2: Deselecting the checked node and the parent of checked node
Step 3: Hide Selected Nodes
From the response above I was able to create this function that filters the Treeview based on a node attribute value. (Replace '
$('#jstree').find('li').each(function () {
if ($("#" + this.id).attr("tag") == "<MYTAG>") {
$(this).hide();
}
else {
$(this).show();
}
})
I wrote another code maybe help someone.
var value = $(this).val();
if (value == "all") {
$('#permissions').jstree("show_all")
return;
}
checkedNodes = $('#permissions').jstree("get_checked", true);
if (value == "granted") {
$('#permissions').jstree("hide_all")
checkedNodes.forEach(function (node) {
$('#permissions').jstree("show_node", node.parent);
$('#permissions').jstree("show_node", node);
});
}
else if ("notgranted") {
$('#permissions').jstree("show_all")
checkedNodes.forEach(function (node) {
$('#permissions').jstree("hide_node", node);
});
}
I subscribe some collection from server. After I try to delete one document from client side it shows remove failed: Access denied. so I tried to delete it from server side by Meteor.call it works fine but client side has same number of documents.
Below code will explain you better.
ClientJS:
Template.Message.onCreated(function () {
this.autorun(function () {
this.subscription = Meteor.subscribe('mymessage');
}.bind(this));
});
Template.Message.onRendered(function () {
this.autorun(function () {
if (this.subscription.ready()) {
console.log(Message.find().count()); //10
}
}.bind(this));
});
ServerJS:
Meteor.publish('mymessage', function() {
console.log(Message.find().count()); //10
return Message.find();
});
In a click event
ClientJS:
Meteor.call("deletemsg", this._id._str, function(error, result){
if(!error){
console.log(Message.find().count()); // 10, Want to update document here.
}
});
Serverjs
Meteor.methods({
deletemsg: function (delmsg) {
if(Message.remove({"_id":new Mongo.ObjectID(delmsg)})){
console.log(Message.find().count()); //9
return true;
} else {
throw new Meteor.Error("some error message");
}
}
});
Note : I am using existing Mongodb.
Your error is probably related to your configuration of the native rules allow and deny. You should have somewhere on your server a piece of code looking like that (Message being your collection name):
Message.allow({
insert: function (userId, doc) {
//allow rule
},
update: function (userId, doc, fields, modifier) {
//allow rule
},
remove: function (userId, doc) {
//allow rule
}
});
Or an equivalent with deny. It looks like your current user is not allowed to delete (i.e. remove) messages from the collection.
Quick sidenote: you don't need to wrap your subscriptions in an autorun. If you use iron-router, you can use the built in functions to subscribe. In your routes options, you can add something like this:
action: function() {
if(this.isReady()) { this.render(); } else { this.render("loading");}
},
isReady: function() {
var subs = [
Meteor.subscribe("yourPublication")
];
var ready = true;
_.each(subs, function(sub) {
if(!sub.ready())
ready = false;
});
return ready;
},
You don't need to use _str as _id is already string.
Client JS
Meteor.call("deletemsg", this._id, function(error, result){ //Remove _str from here
if(!error){
console.log(Message.find().count()); // 10, Want to update document here.
}
});
Server JS
When you delete document, you need to pass only id, not object.
Meteor.methods({
deletemsg: function (delmsg) {
if(Message.remove(delmsg)){
console.log(Message.find().count()); //9
return true;
} else {
throw new Meteor.Error("some error message");
}
}
});
Allowing CRUD operations.
if above method doesnt work, try allowing CRUD operations for that collections from server block. Here is documentation.
I've a Parent and Child models in my app.
Parent.create receives parent_name and an array of children that I want to add to the Parent model, the following flow describes the function:
1) Create parent object
2) Create all children
3) Save parent with updated children array
The problem is that Parent.create is probably async, and the 'created_children' array when saved to parent is empty (because it doesn't wait until the Parent.create finishes.
How can I make Model.create dependent (or synchronic)?
See the code below (I commented the buggy part //BUG: EMPTY ARRAY!!!!!!!!!!):
create: function(req, res, next) {
var childrenInput = req.param('children');
var parentObj = {
name: req.param('parent_name')
};
Parent.create(parentObj, function parentCreated(err, parent) {
if (err) {
return res.redirect('/parent/new');
}
// assign children
var created_children = new Array();
for(var i=0; i < childrenInput.length; i++) {
var childObj = {
name: parentObj.childrenInput[i],
parent_id: parent.id
};
// create child
Child.create(childObj, function childCreated(err, child) {
if (err) {
for(var j=0; j < created_children.length; j++) {
Child.destroy(created_children[j].id, function childDestroyed(err) {
if (err)
{
// BIG ERROR
return next(err);
}
});
}
return res.redirect('/parent/new');
}
// add created child
created_children.push(child.id);
}) // end of Child.create;
} // end of for;
// save created children to parent
parent.children = created_children.slice();
parent.save(function(err, c) {
if (err)
{
// TODO: FUNCTION TO DESTROY ALL CHILDREN
return next(err);
}
});
return res.redirect('/parent/show/' + parent.id);
});
},
Parent model
module.exports = {
schema: true,
attributes: {
name: {
type: 'string',
required: true,
unique: true
},
children: {
type: 'array',
defaultsTo: []
}
}
};
Performing asynchronous operations on an array can be a real pain. I'd suggest using a module like async, which provides synchronous-like functionality for asynchronous code. You could then rewrite your code as:
Parent.create(parentObj, function parentCreated(err, parent) {
if (err) {
return res.redirect('/parent/new');
}
// You only really need this for error handling...
var created_children_ids = new Array();
// Create an array of child instances from the array of child data
async.map(
// Array to iterate over
childrenInput,
// Iterator function
function(childObj, callback) {
Child.create(childObj, function childCreated(err, child) {
if (err) {return callback(err);}
created_children_ids.push(child.id);
// 'null' indicates no error
return callback(null, child);
});
},
// Callback for when loop is finished.
// If any run of the iterator function resulted in the
// callback being called with an error, it will immediately
// exit the loop and call this function. Otherwise the function
// is called when the loop is finished, and "results" contains
// the result of the mapping operation
function (err, results) {
if (err) {return destroyChildren();}
// Save the children to the parent
parent.children = results;
parent.save(function(err, c) {
if (err) {return destroyChildren();}
return res.redirect('/parent/show/' + parent.id);
});
function destroyChildren(err) {
Child.destroy({id: created_children_ids}).exec(function() {
// Respond with an error
return res.serverError(err);
});
}
}
);
});
Note that if you're using Sails v0.10, you can use actual associations to bind the parent and child records, and use parent.children.add(childObj) (which is a synchronous operation) in a regular loop prior to calling parent.save(). Calling .add with an object will cause that model to be created during the save operation.