What I'm trying to do is return the result of a function in Python back to my javascript through AJAX. Currently I am getting This Response while I'm expecting "True" or "False"
jquery:
var test = $.getJSON("/chk_chn", {
name: channel_name
});
alert(test.toSource())
python:
#app.route("/chk_chn")
def chk_chn_unique():
"""Checks a name against all existing Channels in a Channel List. Returns True if name is unique, False otherwise"""
name = request.args.get("name")
for channel in Channels:
if channel.get_name() == name:
return jsonify(result=False)
return jsonify(result=True)
Have you tried:
return jsonify({result: True})
You're missing your callback function and just printing out the request object.
Try this:
$.getJSON('/chk_chn', { name: channel_name })
.done(function (data) {
console.log(data);
});
I had several problems.
First, my Ajax query did not have any callback function. Thanks to Rawri for pointing that out. The code is now the following.
$.getJSON("/chk_chn", { name: channel_name} )
.done(function( json ) {
console.log(json.result)
// Check Name Uniqueness
if (json.result === false) {
$("#chn_name").after('<span class="error">Channel name already exists</span>');
}
else {
// Check Channel Length
if (channel_name.length > 20) {
$("#chn_name").after('<span class="error">Channel Name exceeds maximum length</span>');
return false
}
else {
// Create Channel
socket.emit("create_channel", {"channel_name": channel_name})
// Close the modal
return true;
}
}
})
.fail(function(jqxhr, textStatus, error) {
var err = textStatus + ", " + error;
console.log("Request Failed: " + err);
});
My second and even more stupid issue was that the Ajax query was being called by a button that existed in a Modal. When the button was clicked, the Modal was closed and the javascript was regenerated on the new page, thus discarding my query entirely.
I fix this by returning false on my form
<form role="form" id="submit_channel" onsubmit="return false">
Related
So I am trying to write javascript code for a ribbon button in Dynamics CRM 2016 that will grab a phone number from a list of Leads that can be seen in the Active Leads window.
However, when I try to run it, I get an error telling me
As I step into my code (I'm debugging), I see this error
Here is the code I am working with.
function updateSelected(SelectedControlSelectedItemIds, SelectedEntityTypeName) {
// this should iterate through the list
SelectedControlSelectedItemIds.forEach(
function (selected, index) {
//this should get the id and name of the selected lead
getPhoneNumber(selected, SelectedEntityTypeName);
});
}
//I should have the lead ID and Name here, but it is returning null
function getPhoneNumber(id, entityName) {
var query = "telephone1";
Sdk.WebApi.retrieveRecord(id, entityName, query, "",
function (result) {
var telephone1 = result.telephone1;
// I'm trying to capture the number and display it via alert.
alert(telephone1);
},
function (error) {
alert(error);
})
}
Any help is appreciated.
What you have is an javascript error. In js you can only use forEach on an array. SelectedControlSelectedItemIds is an object not an array.
To loop though an object, you can do the following.
for (var key in SelectedControlSelectedItemIds){
if(SelectedControlSelectedItemIds.hasOwnProperty(key)){
getPhoneNumber(SelectedControlSelectedItemIds[key], SelectedEntityTypeName)
}
}
Okay, so I figured it out. I had help, so I refuse to take full credit.
First, I had to download the SDK.WEBAPI.
I then had to add the webAPI to my Javascript Actions in the Ribbon Tool Bench.
Then, I had to create a function to remove the brackets around the
SelectedControlSelectedItemIds
Firstly, I had to use the API WITH the forEach method in order for it to work.
These are the revisions to my code.
function removeBraces(str) {
str = str.replace(/[{}]/g, "");
return str;
}
function updateSelected(SelectedControlSelectedItemIds, SelectedEntityTypeName) {
//alert(SelectedEntityTypeName);
SelectedControlSelectedItemIds.forEach(
function (selected, index) {
getPhoneNumber(removeBraces(selected), SelectedEntityTypeName);
// alert(selected);
});
}
function getPhoneNumber(id, entityName) {
var query = "telephone1";
SDK.WEBAPI.retrieveRecord(id, entityName, query, "",
function (result) {
var telephone1 = result.telephone1;
formatted = telephone1.replace(/[- )(]/g,'');
dialready = "1" + formatted;
withcolon = dialready.replace(/(.{1})/g,"$1:")
number = telephone1;
if (Xrm.Page.context.getUserName() == "Jerry Ryback") {
url = "http://111.222.333.444/cgi-bin/api-send_key";
} else if(Xrm.Page.context.getUserName() == "Frank Jane") {
url = "http://222.333.444.555/cgi-bin/api-send_key";
}
else if( Xrm.Page.context.getUserName() == "Bob Gilfred"){
url = "http://333.444.555.666/cgi-bin/api-send_key";
}
else if( Xrm.Page.context.getUserName() == "Cheryl Bradley"){
url = "http://444.555.666.777/cgi-bin/api-send_key";
}
else if( Xrm.Page.context.getUserName() == "Bill Dunny"){
url = "http://555.666.777.888/cgi-bin/api-send_key";
}
if (url != "") {
var params = "passcode=admin&keys=" + withcolon + "SEND";
var http = new XMLHttpRequest();
http.open("GET", url + "?" + params, true);
http.onreadystatechange = function () {
if (http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
}
http.send(null);
}
},
function (error) {
// alert(error);
})
}
To elaborate, once I successfully get the number, I remove the parenthesis, dashes and white-space. Then, I add a "1" to the beginning. Finally, I insert colons in between each number. Then, I create an HTTP command and send it to the office phone of whoever is using CRM at the time. The user eval and HTTP message is my code. I'm showing you all of this because it was a great learning experience, and this feature really adds to the functionality.
I hope some of you find this useful.
Thanks for the help.
In my add-in I am making an HTTP request and receiving an output. I want to place that output into a binding and have it expand the binding if necessary because the user won't necessarily know how many rows x columns the output will be. How would I go about doing this? Currently I am binding to a range, but if that range does not match the size of the [[]] that I am providing, then the data is not displayed in the sheet. So, this ends up requiring the user to know the size of the output.
What I'm doing currently using Angular is as follows (the problem with this being that the output isn't always the same size as the Office.BindingType.Matrix that the user selected in the spreadsheet):
I create the binding to where the output should be placed as follows:
inputBindFromPrompt(parameterId: number): Promise<IOfficeResult> {
let bindType: Office.BindingType;
if(this.inputBindings[parameterId].type != 'data.frame' && this.inputBindings[parameterId].type != 'vector') {
bindType = Office.BindingType.Text;
} else {
bindType = Office.BindingType.Matrix;
}
return new Promise((resolve, reject) => {
this.workbook.bindings.addFromPromptAsync(bindType, { id: this.inputBindings[parameterId].name },
(addBindingResult: Office.AsyncResult) => {
if(addBindingResult.status === Office.AsyncResultStatus.Failed) {
reject({
error: 'Unable to bind to workbook. Error: ' + addBindingResult.error.message
});
} else {
this.inputBindings[parameterId].binding = addBindingResult.value;
resolve({
success: 'Created binding ' + addBindingResult.value.type + ' on ' + addBindingResult.value.id
});
}
})
})
}
Then when the user submits via a button, the inputs are passed to a HTTP request service which then receives an output that I process into an array of arrays so that it can go into an Office.BindingType.Matrix:
this.isBusy = true;
this.feedback = 'submitted';
// Grab the values from the form
// Send as a POST and receive an output
// Put the output in the Excel sheet
this.webServicesService.postWebServices(this.service, this.inputParameters)
.subscribe(
(data: any) => {
// Correctly received data
// Access the data by name while looping through output parameters
this.error = false;
this.feedback = 'received data';
let i = 0;
this.outputParameters.forEach(element => {
// temporary name to identify the parameter
let name = element.name;
// Set the data value in the parameter
if(element.type == 'data.frame') {
let parameter = data[name];
this.feedback = parameter;
let excelData = [];
for(var key in parameter) {
if(parameter.hasOwnProperty(key)) {
var val = parameter[key];
excelData.push(val);
}
}
element.value = excelData;
}
else {
element.value = data[name];
}
// Set value in the form
let param = (<FormArray>this.serviceForm.controls['outputParameters']).at(i);
param.patchValue({
value: element.value
});
// Set value in the spreadsheet
this.excelService.outputSetText(i, element.value)
.then((result: IOfficeResult) => {
this.onResult(result);
i++;
});
}, (result: IOfficeResult) => {
this.onResult(result);
});
},
(error) => {
if(error.status == 400 || error.status == 401) {
// Return user to authentication page
this.authService.logout();
this.router.navigate(['/']);
} else {
// Tell user to try again
this.error = true;
}
}
);
The line above that is setting the value to the Office.Matrix.Binding is this.excelService.outputSetText(i, element.value), which calls this method in the Excel Service:
outputSetText(parameterId: number, data: any): Promise<IOfficeResult> {
return new Promise((resolve, reject) => {
if(this.outputBindings[parameterId].binding) {
this.outputBindings[parameterId].binding.setDataAsync(data, function (result: Office.AsyncResult) {
if(result.status == Office.AsyncResultStatus.Failed) {
reject({ error: 'Failed to set value. Error: ' + result.error.message });
} else {
let test: Office.Binding;
resolve({
success: 'successfully set value'
});
}
})
} else {
reject({
error: 'binding has not been created. bindFromPrompt must be called'
});
}
})
}
It's essentially using addFromPromptAsync() to set an output spot for the HTTP request. Then the user submits which sends the request, receives the data back and processes it into an array of arrays [[]] so that it can be the correct data format for Office.BindingType.Matrix. However, unless this is the same number of rows and columns as the binding originally selected, it won't display in the sheet. So, is there a binding type that will dynamically grow based on the data I give it? Or would I just need to release the current binding and make a new binding according to the size of the HTTP response data?
So long as you're using the "shared" (Office 2013) APIs, you will have this issue.
However, in the host-specific (2016+) APIs, you can easily solve the problem by resizing the range to suit your needs. Or more precisely, getting the binding, then asking for its range, then getting just the first (top-left) cell, and then resizing it:
await Excel.run(async (context) => {
let values = [
["", "Price"],
["Apple", 0.99],
["Orange", 1.59],
];
let firstCell = context.workbook.bindings.getItem("TestBinding").getRange().getCell(0, 0);
let fullRange = firstCell.getResizedRange(
values.length - 1, values[0].length - 1);
fullRange.values = values;
await context.sync();
});
You can try this snippet live in literally five clicks in the new Script Lab (https://aka.ms/getscriptlab). Simply install the Script Lab add-in (free), then choose "Import" in the navigation menu, and use the following GIST URL: https://gist.github.com/Zlatkovsky/5a2fc743bc9c8556d3eb3234e287d7f3. See more info about importing snippets to Script Lab.
I'm using the jQuery Validation plugin for a signup form and trying to check if an email address is already taken as part of the validation. The problem is that the check happens via a promise, and I don't know how (or if) I can make this work with the validation plugin. Here's what I have currently:
$("#signup form").validate({
rules: {
emailSignup: {
email: true,
required: true,
remote: checkAvailable
}
}
});
function checkAvailable() {
var email = $("#emailSignup").val(),
available;
App.isEmailAvailable(email)
.then(function(response) {
available = response;
})
.error(function() {
available = response;
});
setTimeout(function() {
if (available == true) {
console.log("is available");
return true;
} else {
console.log("not available");
return false;
}
}, 100);
}
The setTimeout is just a hacky way to ensure I have the response before I try to log it (I know that's terrible, but it's just for testing).
The thing is, this will correctly log is available or not available depending on the email address I enter, so it works up to that point. But I can't seem to actually return true/false back up to the remote method so that it kicks off the error handling. So what happens is anything that's a syntactically valid email gets marked as valid, even if it's logging not available based on the response.
Is this at all possible?
Update: I also tried doing this in a custom method:
jQuery.validator.addMethod("email", function(value, element) {
var field = this,
available;
App.isEmailAvailable(value)
.then(function(response) {
available = response;
})
.error(function() {
available = response;
});
setTimeout(function() {
if (available == true) {
console.log("is available");
return field.optional(element) || /^[\w-+\.]+#([\w-]+\.)+[\w-]{2,4}$/.test(value);
} else {
console.log("not available");
return false;
}
}, 100);
}, jQuery.validator.format("Please enter a valid email address."));
So the idea here is it would check if the address is available, and if it is, then it would check that it's syntactically valid. Again, it correctly logs is available/not available based on the address I enter, but doesn't return true/false properly to mark the field as valid or invalid.
Based on the "remote" jquery validate rule :
$.validator.addMethod('users_email_exists', function (value, element) {
var method = 'remote';
var previous = this.previousValue(element, method);
var validator = this;
if (!this.settings.messages[element.name]) {
this.settings.messages[element.name] = {};
}
previous.originalMessage = previous.originalMessage || this.settings.messages[element.name][method];
this.settings.messages[element.name][method] = previous.message;
var optionDataString = $.param({data: value});
if (previous.old === optionDataString) {
return previous.valid;
}
previous.old = optionDataString;
this.startRequest(element);
new Promise(function (fulfill) {
// YOUR STUFF, YOUR AJAX GET/POST REQUEST AND URL WITH PARAMS
$.get('/backend/users/ajax/filtered-users-list', {email: value})
.done(function (data) {
// YOUR STUFF TO VALIDATE DATA
// IF VALID TRUE -> validation success
// IF VALID FALSE -> validation failure
var valid = !data.length;
fulfill(valid);
})
}).then(function(valid) {
validator.settings.messages[ element.name ][ method ] = previous.originalMessage;
if ( valid ) {
submitted = validator.formSubmitted;
validator.resetInternals();
validator.toHide = validator.errorsFor( element );
validator.formSubmitted = submitted;
validator.successList.push( element );
validator.invalid[ element.name ] = false;
validator.showErrors();
} else {
errors = {};
message = validator.defaultMessage( element, { method: method, parameters: value } );
// YOUR STUFF, YOUR VALIDATION MESSAGE HERE
errors[ element.name ] = previous.message = 'EMAIL ALREADY ASSIGNED TO AN USER';
validator.invalid[ element.name ] = true;
validator.showErrors( errors );
}
previous.valid = valid;
validator.stopRequest( element, valid );
});
return "pending";
},
"EMAIL ALREADY ASSIGNED TO AN USER"
);
Then call your custom rule :
$("#signup form").validate({
rules: {
emailSignup: {
email: true,
required: true,
users_email_exists: true
}
}
});
Include this script as Promise class reference :
<!-- promise -->
<script src="https://www.promisejs.org/polyfills/promise-7.0.4.min.js"></script>
<script src="https://www.promisejs.org/polyfills/promise-done-7.0.4.min.js"></script>
The function that you've passed to setTimeout() will execute in future (a.k.a asynchronously) - after your checkAvailable() is completed. So its returning value is meaningless for the checkAvailable().
You should do rather the following:
DisableTheForm();
App.isEmailAvailable(value)
.then(function(response) {
$("#signup form").validate();
if( it is valid) {
EnableTheForm();
PostFormData();
}
})
.error(function() {
CryAsItIsNotAvailable();
EnableTheForm();
});
So to do your validation in response of positive feedback of your isEmailAvailable
You cannot use the remote method because it's looking for a URL parameter to access "remotely" via ajax().
You obviously would not call a JavaScript function with ajax(), so using remote to call a JavaScript function makes no sense.
You might be able to create a custom function using the .addMethod() method. However, you will have issues if any part of that is performed asynchronously as the custom rule will be evaluated before you have the result.
I had a read of the meme example but it doesn't seem to update, just create new objects! What I want is to
a. find some given db table
b. update some fields in the db table
c. save the db table back to the database
Given this code, what is the missing piece so that I can actually update an object?
query.find(
function(results){
if (results.length > 0){
return results[0];
} else {
//no object found, so i want to make an object... do i do that here?
return null;
}
},
function(error){
response.error("ServerDown");
console.error("ServerDown - getModuleIfAny URGENT. Failed to retrieve from the ModuleResults table" + +error.code+ " " +error.message);
}
).then(
function(obj){
var module;
if (obj != null){
console.log("old");
module = obj;
module.moduleId = 10; //let's just say this is where i update the field
//is this how i'd update some column in the database?
} else {
console.log("new");
var theModuleClass = Parse.Object.extend("ModuleResults");
module= new theModuleClass();
}
module.save().then(
function(){
response.success("YAY");
},
function(error) {
response.error('Failed saving: '+error.code);
}
);
},
function(error){
console.log("sod");
}
);
I thought the above code would work - but it does not. When it finds an object, it instead refuses to save, stupidly telling me that my object has no "save" method.
First I would double check the version of the javascript sdk you're using in your cloud code. Make sure it's up to date e.g. 1.2.8. The version is set in the config/global.json file under your cloud code directory.
Assuming you're up to date I would try modifying your code by chaining the promises using multiple then's like so:
query.find().then(function(results){
if (results.length > 0){
return results[0];
} else {
//no object found, so i want to make an object... do i do that here?
return null;
}
},
function(error){
response.error("ServerDown");
console.error("ServerDown - getModuleIfAny URGENT. Failed to retrieve from the ModuleResults table" + +error.code+ " " +error.message);
}).then(function(obj){
var module;
if (obj != null){
console.log("old");
module = obj;
module.moduleId = 10; //let's just say this is where i update the field
//is this how i'd update some column in the database?
} else {
console.log("new");
var theModuleClass = Parse.Object.extend("ModuleResults");
module= new theModuleClass();
}
module.save();
}).then(function(result) {
// the object was saved.
},
function(error) {
// there was some error.
});
I think this should work. Fingers crossed. Cheers!
I have a function that validates some fields when a button is pressed. First one checks if the username and password are authentic or not. then if the password is appropriately secure. Then if it matches the confirm-password box. However, it seems that the ajax that check to see if the user is authentic does not complete before the alert in the first function pops up.
$(document).ready(function() {
$('#submit_pw_ch').on('click', function(){
var alertString = "Please correct the following errors: \n"
//current username and password are correct
var vUsr = valUsr();
//new password is sufficiently secure
var vPwd = valPwd();
//confirmation of new password is same as previous box
var sPwd = samePwd();
console.log('valid user : ' + vUsr + ' valid password : ' + vPwd + ' same password : ' + sPwd);
//append appropriate warnings to alert string
if ( !vUsr ) { alertString += "-Your current username and password are invalid. \n"; }
if ( !vPwd ) { alertString += "-The new password you have selected is not strong enough. \n"; }
if ( !sPwd ) { alertString += "-The confirmation of your new password does not match the previous entry. \n"; }
if ( !vUsr || !vPwd || !sPwd ) {
alert(alertString);
return false;
} else {
//change password
}
});
});
So the line that checks for that is var vUsr = valUsr(); which calls
function valUsr() {
var un = $('#uNameInput').val();
var pw = $('#uPwdInput').val();
//return value
var validUsr = false;
$.ajax({
type: "post",
url: "queries/confirm_user.php?<?=time()?>",
data: "un=" + un + "&pw=" + pw,
dataType: "json",
success: function (returnedData) {
console.log(returnedData)
if (data == 'true') {
validUsr = true;
} else {
validUsr = false;
}
}
});
return validUsr;
}
Somehow though the alert is not waiting for the ajax to finish getting it's data. The console.log(returnedData) in the valUsr() function appears in the console after I've dismissed the alert box. Why is this happening? How can I prevent it? Thanks!
Thomas,
You need to cater for the inherent asynchronicity of ajax, in other words you need to wait until a response to the ajax request has arrived before deciding what to do.
jQuery's Deferreds (and promises) allow us to write simple code but Deferreds tend to blow you mind at first, at least very slightly.
There's no unique way in which to use Deferreds for a problem like this but here's one.
$(document).ready(function() {
$('#submit_pw_ch').on('click', function() {
var form = this.form; //the form containing the submit button and the fields .
//`alertArr` is an array to which messages will be added.
var alertArr = ["Please correct the following errors:"];
//`addAlert` will be called from a `Deferred.resolveWith(this, ...)` statement.
//The context, `this`, is unused.
function addAlert(index, txt) {
alertArr[index] = txt;
}
//`compositeAction` will be called from a promise.done() statement.
function compositeAction() {
//first filter out null messages (ie. validation successes) from alertArr.
var message = $.map(alertArr, function(txt, i){
return txt || null;
});
if(message.length > 1) {
//then alert the complete message with line breaks
alert(message.join("\n"));
} else {
//submit the form to change the password
//or another ajax call as required
form.submit();
}
}
// Invoke ajax validators and assign returned promises.
// An index is passed, so the text messages can be displayed in a logical order,
// regardless of the order in which the validation promises are resolved.
//If we didn't care about the order of the messages then the code would be slighly simpler.
var vUsr = valUsr(0),
vPwd = valPwd(1),
sPwd = samePwd(2);
//All validations adopt a similar pattern regardless of whether ajax is involved or not.
//Here, we establish what is to be done when the promise are resolved, or
//what is to be done immediately if the promise are alrady resolved.
vUsr.done(addAlert);
vPwd.done(addAlert);
sPwd.done(addAlert);
//At this stage, `addAlert` will contain entries for successful as well as unsuccessful validations. Successful entries will be filtered out by `compositeAction`
//Establish what is to be done when all three promises are resolved.
$.when(vUsr, vPwd, sPwd).done(compositeAction);
//Return false unconditionally
return false;
});
function valUsr(index) {
var messages = {
validated: '',//important - this message must be an empty string - do not change
notValidated: '- Your current username and password are invalid.',
ajaxError: '- Validation error: username and password.'
};
//Create a Deferred object, which will be resolved in response to several different outcomes.
var def = $.Deferred();
$.ajax({
type: "post",
url: "queries/confirm_user.php?<?=time()?>",
data: {
'un': $('#uNameInput').val(),
'pw': $('#uPwdInput').val()
},
dataType: "json",
success: function (returnedData) {
if (returnedData == 'true') {
def.resolveWith(this, [index, messages.validated]);
} else {
def.resolveWith(this, [index, messages.notValidated]);
}
},
error: function() {
def.resolveWith(this, [index, messages.ajaxError]);
}
});
return def.promise();//Return a promise derived from the as yet unresolved Deferred.
}
function samePwd(index) {
var pw1 = $('#uPwdInput').val();
var pw2 = $('#uPwdInput2').val();
var errMessage = (pw1 === pw2) ? '' : '-The confirmation of your new password does not match the previous entry';
var def = $.Deferred();//Create a Deferred object, which will be resolved immediately
def.resolveWith(this, [index, errMessage]);
return def.promise();//Return a promise derived from the already resolved Deferred.
}
});
valPwd() will be of the same format as either valUsr() or samePwd(), depending on whether ajax is involved or not.
Ajax is run on the fly, synchronously
You will need to check the validation of the other fields after the ajax request has completed in the success callback. You can turn off the synchronous request but the browser will freeze up 'till it gets one, not advised.
You will need to restructure your calls to reflect this; I would suggest that as soon as they have finished typing the password and the field blurs you send the request to check. That way, if there are any errors you will be able to prevent the wait time at the end of the form.