I have the following script (see below). I have two questions regarding it:
1.What does the following line mean in the context of Knockoutjs?
ko.observable(null);
2.How can I invoke a function not yet defined as in here:
that.activePollingXhr(...
Here is the full script:
$(document).ready(function() {
function ChatViewModel() {
var that = this;
that.userName = ko.observable('');
that.chatContent = ko.observable('');
that.message = ko.observable('');
that.messageIndex = ko.observable(0);
that.activePollingXhr = ko.observable(null);
var keepPolling = false;
that.joinChat = function() {
if (that.userName().trim() != '') {
keepPolling = true;
pollForMessages();
}
}
function pollForMessages() {
if (!keepPolling) {
return;
}
var form = $("#joinChatForm");
that.activePollingXhr($.ajax({url: form.attr("action"), type: "GET", data: form.serialize(), cache: false,
success: function(messages) {
console.log(messages);
for (var i = 0; i < messages.length; i++) {
that.chatContent(that.chatContent() + messages[i] + "\n");
that.messageIndex(that.messageIndex() + 1);
}
},
error: function(xhr) {
if (xhr.statusText != "abort" && xhr.status != 503) {
resetUI();
console.error("Unable to retrieve chat messages. Chat ended.");
}
},
complete: pollForMessages
}));
$('#message').focus();
}
that.postMessage = function() {
if (that.message().trim() != '') {
var form = $("#postMessageForm");
$.ajax({url: form.attr("action"), type: "POST",
data: "message=[" + that.userName() + "] " + $("#postMessageForm input[name=message]").val(),
error: function(xhr) {
console.error("Error posting chat message: status=" + xhr.status + ", statusText=" + xhr.statusText);
}
});
that.message('');
}
}
that.leaveChat = function() {
that.activePollingXhr(null);
resetUI();
this.userName('');
}
function resetUI() {
keepPolling = false;
that.activePollingXhr(null);
that.message('');
that.messageIndex(0);
that.chatContent('');
}
}
//Activate knockout.js
ko.applyBindings(new ChatViewModel());
});
ko.observable(null); creates an observable with a value of null. Nothing different than ko.observable(5);, where the value would be 5.
I see that you're using the that.activePollingXhr observable by passing it the result of an ajax call. However, this call is asynchronous and $.ajax doesn't return the data it got from the server, but rather a jquery deferred. You need to use that.activePollingXhr insude the success callback. Here's is how your code might look like:
$.ajax({url: form.attr("action"), type: "GET", data: form.serialize(), cache: false,
success: function(messages) {
console.log(messages);
for (var i = 0; i < messages.length; i++) {
that.chatContent(that.chatContent() + messages[i] + "\n");
that.messageIndex(that.messageIndex() + 1);
}
that.activePollingXhr(messages); // <-- Note where the call to activePollingXhr is
},
error: function(xhr) {
if (xhr.statusText != "abort" && xhr.status != 503) {
resetUI();
console.error("Unable to retrieve chat messages. Chat ended.");
}
},
complete: pollForMessages
});
As for the comment under your question: that.activePollingXhr is defined as that.activePollingXhr = ko.observable(null); - an observable with value of null.
That just initializes an observable with null as the initial value.
If you need to invoke a function that is an observable, just add a second set of parenthesis.
that.activePollingXhr()()
Related
I have a Dynamic Grid in the ASP.NET page. When I click Add User, it creates an empty record with Textbox controls and when the user types something in the first Textbox - UserName, it should pull the UserNames from the Database and autocomplete should work for that Textbox control.
I have implemented jQuery.autocomplete function as below.
jQuery("#" + e.id).autocomplete({
source: function (request, response) {
var txtSearch = jQuery("#" + e.id).attr("id");
var t = jQuery("#" + txtSearch).val();
var URL = "../Users.aspx?UserName=" + t;
jQuery.ajax({
url: URL,
success: function (data) {
switch (data) {
case 'NOVALUESFOUND':
var rftspanID = e.id.replace("txt", "span");
break;
default:
var rftspanID = e.id.replace("txt", "span");
var rows = data.split("|");
var jsStr = "var datalist=[";
for (i = 0; i < rows.length - 1; i++) {
var s = rows[i].toString();
s = s.replace("'", "\\'");
s = s.replace('"', "\\'");
var row = s.split("~");
jsStr += "{id:'" + row[0].toString() + "'";
jsStr += ",name:'" + row[1].toString() + "'},";
}
jsStr = jsStr.slice(0, jsStr.length - 1);
jsStr += "];";
eval(jsStr);
if (typeof (datalist) != 'undefined') {
response(jQuery.map(datalist, function (items) {
if (items.id != undefined) {
return {
value: items.name,
id: items.id
}
}
}));
}
}
}
});
},
minlength: 1,
select: function (event, ui) {
if (Type == 1) {
document.getElementById("txtUser" + MemCount).value = ui.item.value;
}
else if (Type == 2) {
document.getElementById("txtRole" + MemCount).value = ui.item.value;
}
},
open: function () {
jQuery(this).removeClass("ui-corner-all").addClass("ui-corner-top");
},
close: function (event) {
jQuery(this).removeClass("ui-corner-top").addClass("ui-corner-all");
}
});
When I try to Debug this autocomplete, the data is coming at the end of response(jQuery.map(datalist, function (items) statement. But the select: option is not firing.
EDIT
The below screenshot shows how the data is formed.
And this is what is present in the Server-Side Users.aspx.vb Page_Load Event
Response.Clear()
Response.Write(GetUserName(Request.QueryString("UserName")))
Response.End()
What could be the problem?
First. In the response, you check the data variable in swith. And you get it as a string.
Second. the best way to work with ajax is JSON.
jQuery.ajax({
url: url,
dataType: 'json'
On successful response:
Make parsing.
json = JSON.parse(data)
And then you already apply your logic, I work with individual object variables.
swith(json.string){ .... }
And it will be easier to fill Textbox controls with the necessary parameters: json.user - the variable will contain an array of data about users.
Update code:
jQuery("#" + e.id).autocomplete({
source: function (request, response) {
var txtSearch = jQuery("#" + e.id).attr("id");
var t = jQuery("#" + txtSearch).val();
var URL = "../Users.aspx?UserName=" + t;
jQuery.ajax({
url: URL,
dataType: 'json',
/*
* format respone data (string!!!) -> {"result": [{"id": 1,"item": 2},{"id": 1,"item": 2}],"found": "VALUESFOUND"}
*/
success: function (data) {
let json = JSON.parse(data);
switch (json.found) {
case 'NOVALUESFOUND':
var rftspanID = e.id.replace("txt", "span");
break;
default:
var rftspanID = e.id.replace("txt", "span");
response(jQuery.map(json.result, function (items) {
if (items.id != undefined) {
return {
value: items.name,
id: items.id
}
}
}));
}
}
});
},
minlength: 1,
select: function (event, ui) {
if (Type == 1) {
document.getElementById("txtUser" + MemCount).value = ui.item.value;
}
else if (Type == 2) {
document.getElementById("txtRole" + MemCount).value = ui.item.value;
}
},
open: function () {
jQuery(this).removeClass("ui-corner-all").addClass("ui-corner-top");
},
close: function (event) {
jQuery(this).removeClass("ui-corner-top").addClass("ui-corner-all");
}
});
I'm getting an unexpected undefined return value from a function.
function createselbox(arr) {
console.log('In createselbox');
var startstr = `Something`;
mytemp = '';
for (var i = 0; i < arr.length; i++) {
mytemp = mytemp + '<option>' + arr[i] + '</option>';
}
var ret = startstr + mytemp + `Something else `;
IsStaff = CheckifStaff();
console.log('Checkifstaff is ' + IsStaff);
if (IsStaff) {
console.log('Got true createselbox');
console.log('In sel loop');
ret = startstr + mytemp + `Something more`;
} else {
console.log('Got false createselbox');
}
return ret;
}
function CheckifStaff() {
var data = {
"isstaff": 'check'
};
data = $(this).serialize() + "&" + $.param(data);
$.ajax({
type: "POST",
dataType: "html",
url: "/functions/checkifstaff",
data: data,
success: function (data) {
var recdata = data;
if (recdata.indexOf('Has add permissions') != -1) {
console.log('Returning true');
return 1;
} else {
console.log('Returning false');
return 0;
}
},
error: function (xhr, textStatus, errorThrown) {
console.log('An error occured in CheckifStaff');
return 0;
}
});
}
The console shows:
In createselbox
appointment.js ? dev = 96168005 : 292 Checkifstaff is undefined
appointment.js ? dev = 96168005 : 305 Got false createselbox
appointment.js ? dev = 96168005 : 158 Returning true
createselbox is called first. But the value from the function is being returned as false, apparently even before the function execution.
Why is this happening?
CheckifStaff() has no explicit return statement, so it returns what all functions return by default, which is undefined.
To make this work, CheckiStaff needs to return something, here, for example CheckiStaff will return a Promise which's value is either 1 or 0. createselbox will now await CheckiStaff and then use the value it returns afterwards
async function createselbox(arr) {
console.log('In createselbox');
var startstr = `Something`;
mytemp = '';
for (var i = 0; i < arr.length; i++) {
mytemp = mytemp + '<option>' + arr[i] + '</option>';
}
var ret = startstr + mytemp + `Something else `;
let IsStaff = await CheckiStaff();
console.log('Checkifstaff is ' + IsStaff);
if (IsStaff) {
console.log('Got true createselbox');
console.log('In sel loop');
ret = startstr + mytemp + `Something more`;
} else {
console.log('Got false createselbox');
}
return ret;
}
function CheckifStaff() {
var data = {
"isstaff": 'check'
};
data = $(this).serialize() + "&" + $.param(data);
return new Promise((resolve, reject) => {
$.ajax({
type: "POST",
dataType: "html",
url: "/functions/checkifstaff",
data: data,
success: function (data) {
var recdata = data;
if (recdata.indexOf('Has add permissions') != -1) {
console.log('Returning true');
resolve(1);
} else {
console.log('Returning false');
resolve(0);
}
},
error: function (xhr, textStatus, errorThrown) {
console.log('An error occured in CheckifStaff');
resolve(0);
}
});
});
}
you can also do change your ajax to be like this
$.ajax({
type: "POST",
dataType: "html",
url: "/functions/checkifstaff",
data: data,
success: function (data) {
var recdata = data;
if (recdata.indexOf('Has add permissions') != -1) {
console.log('Returning true');
return 1;
} else {
console.log('Returning false');
return 0;
}
},
error: function (xhr, textStatus, errorThrown) {
console.log('An error occured in CheckifStaff');
return 0;
}
}).then(function(data){
return data
});
i'm not an expert, but i think when you use ajax you should wait for the response - use promise, or async/await func
I'm trying to set up a script to get total page size, including images. I'm having serious problems with ajax asyncronicity and JQuery throws error when I try to set async: false due to negative effects in user experience.
The problem is, ajax calls to get image size return NaN in a random and very frequent way, surely due to too many concurrent connections.
Is there a way you can think to overcome this?
This is my code (it was much shorter in origin, callback approach was based on this post, but didn't work):
function getPageSize(callback)
{
var pageWeight = 0;
var lastWeight = 0;
var xhr = $.ajax({
type: "HEAD",
// async: false,
url: "test01.html",
success: function(msg)
{
if (xhr.readyState == 4)
{
if (xhr.status == 200 || xhr.status == 0)
{
if ( !isNaN(xhr.getResponseHeader('Content-Length')) && !isNaN($('#size').html()) )
{
pageWeight = parseInt(xhr.getResponseHeader('Content-Length'));
callback(pageWeight);
// lastWeight = parseInt($('#size').html());
// $('#size').html(pageWeight + lastWeight);
console.log("Page " + pageWeight);
}
}
}
}
});
}
function getImagesSize(callback)
{
var imageWeight = 0;
var lastWeight = 0;
$('img').each(function()
{
var imgPath = $(this).attr('src');
xhr = null;
xhr = $.ajax(
{
type: "HEAD",
url: $(this).attr('src'),
async: false,
success: function(msg)
{
if (xhr.readyState == 4)
{
if (xhr.status == 200 || xhr.status == 0)
{
if (!isNaN(xhr.getResponseHeader('Content-Length')) && !isNaN($('#size').html()) )
{
imageWeight = parseInt(xhr.getResponseHeader('Content-Length'));
callback(imageWeight);
// lastWeight = parseInt($('#size').html());
// $('#size').html(imageWeight + lastWeight);
console.log("Image " + imgPath + ": " + imageWeight);
}
}
}
}
});
});
}
function updateTotalPageSize(size)
{
var lastWeight = 0;
lastWeight = parseInt($('#size').html());
$('#size').html(size + lastWeight);
}
$(document).ready(function()
{
getPageSize(function(size)
{
//alert(size);
updateTotalPageSize(size);
});
getImagesSize(function(size)
{
//alert(size);
updateTotalPageSize(size);
});
});
SOLUTION by #Regent
function getImagesSize(callback)
{
var allImages = $('img');
function handleNext(index)
{
if (index >= allImages.length)
{
return;
}
var imgPath = allImages.eq(index).attr('src');
$.ajax({
type: "HEAD",
url: imgPath,
success: function(msg, status, xhr)
{
if (!isNaN(xhr.getResponseHeader('Content-Length')) && !isNaN($('#size').html()))
{
var imageWeight = parseInt(xhr.getResponseHeader('Content-Length'));
callback(imageWeight);
console.log("Image " + imgPath + ": " + imageWeight);
handleNext(index + 1);
}
}
});
}
handleNext(0);
}
I have one image attached with each record in my entity. I want to show these images in the records in a web resource just like a record picture. I am using the following code:
function GetData(){
// var base64image = document.getElementById('image').src.substr(document.getElementById('image').src.indexOf('base64')+7);
var recordId = window.parent.Xrm.Page.data.entity.getId();
var serverUrl = Xrm.Page.context.getServerUrl().toString();
var ODATA_ENDPOINT = "XRMServices/2011/OrganizationData.svc";
var objAnnotation = new Object();
var ODATA_EntityCollection = "/AnnotationSet";
var temp= "/AnnotationSet?$select=DocumentBody,FileName,MimeType,ObjectId&$filter=ObjectId/Id eq guid'" + recordId + "'";
var result =serverUrl + ODATA_ENDPOINT + ODATA_EntityCollection + temp;
// Parse the entity object into JSON
var jsonEntity = window.JSON.stringify(objAnnotation);
// Asynchronous AJAX function to Create a CRM record using OData
$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
datatype: "json",
url: result ,
//data: jsonEntity,
async: false,
beforeSend: function (XMLHttpRequest) {
XMLHttpRequest.setRequestHeader("Accept", "application/json");
},
success: function(status){
alert("success paa jee!!");
},
error: function (xmlHttpRequest, textStatus, errorThrown) {
alert("Status: " + textStatus + "; ErrorThrown: " + errorThrown);
}
});
}
</script>
But I get an error $ is undefined when I reach the Ajax part. Basically every record has one Image in its notes attached to the entity's record and I want to show this image in a web resource as a record picture.
I am open to suggestions if there is a better/another way.
EDIT: I have edited the code and have updated the ODATA url.
In CRM 2011, I have used two Custom Aspx pages to show, the attached image.
Page 1: AccountImage.aspx have the following Control:
<asp:Image ID="IMG_Logo" runat="server" Height="50px" ImageUrl="AccountImageForm.aspx" Visible="false" />
In AccountImage.aspx On PageLoad
if (Request.QueryString["id"] != null)
{
Id = new Guid(Request.QueryString["id"]);
if (!IsPostBack)
{
ResetCache();
}
ShowImages();
}
The ShowImages() functions has below code:
function ShowImages()
{
IMG_Logo.Visible = false;
QueryExpression query = new QueryExpression("annotation");
query.Criteria.AddCondition("objectid", ConditionOperator.Equal, Id);
query.Criteria.AddCondition("mimetype", ConditionOperator.In, new string[] { "image/x-png", "image/pjpeg", "image/png", "image/jpeg" });
query.Criteria.AddCondition("subject", ConditionOperator.NotEqual, "membershipcardthumbnail");
query.Criteria.AddCondition("subject", ConditionOperator.NotEqual, "membershipcardimage");
query.ColumnSet = new ColumnSet(true);
EntityCollection AllLogoImageNotes = Common.Common.RetrieveMultiple(query);
if (AllLogoImageNotes.Entities.Count > 0)
{
foreach (Entity Note in AllLogoImageNotes.Entities)
{
if (Note.Attributes.Contains("subject") && Note.Attributes.Contains("documentbody"))
{
if (Note["subject"].ToString().ToLower() == "accountlogoimage")
{
HttpRuntime.Cache.Remove("AccountLogoImage");
HttpRuntime.Cache.Remove("AccountLogoImageType");
HttpRuntime.Cache.Add("AccountLogoImage", Convert.FromBase64String(Note["documentbody"].ToString()), null, DateTime.Now.AddMinutes(5), TimeSpan.Zero, CacheItemPriority.Normal, null);
HttpRuntime.Cache.Add("AccountLogoImageType", Note["mimetype"].ToString(), null, DateTime.Now.AddMinutes(5), TimeSpan.Zero, CacheItemPriority.Normal, null);
IMG_Logo.ImageUrl = "AccountImageForm.aspx" + "?time=" + DateTime.Now.ToString();
IMG_Logo.Visible = true;
}
}
}
}
}
As you can see, the line below:
IMG_Logo.ImageUrl = "AccountImageForm.aspx" + "?time=" + DateTime.Now.ToString();
In AccountImageForm.aspx write below code:
protected void Page_Load(object sender, EventArgs e)
{
Response.Clear();
if (HttpRuntime.Cache["AccountLogoImage"] != null)
{
Response.ContentType = HttpRuntime.Cache["AccountLogoImageType"].ToString();
byte[] data = (byte[])HttpRuntime.Cache["AccountLogoImage"];
Response.BinaryWrite(data);
}
}
In ODATA you can do the following:
retrieveImages("/AnnotationSet?$select=DocumentBody,MimeType&$filter=ObjectId/Id eq guid'" + Xrm.Page.data.entity.getId()+ "'", function (JsonObject) {
if (JsonObject != null) {
// debugger;
var ByteString= JsonObject[0].DocumentBody;
var MimeType = JsonObject[0].MimeType
}
function retrieveImages(query, SuccessFunc) {
var retrieveRecordsReq = new XMLHttpRequest();
var ODataPath = Xrm.Page.context.getServerUrl() + "/xrmservices/2011/OrganizationData.svc";
retrieveRecordsReq.open('GET', ODataPath + query, false);
retrieveRecordsReq.setRequestHeader("Accept", "application/json");
retrieveRecordsReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
retrieveRecordsReq.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
if (this.status == 200) {
this.onreadystatechange = null; //avoids memory leaks
var data = JSON.parse(this.responseText, SDK.REST._dateReviver);
if (data && data.d && data.d.results)
SuccessFunc(JSON.parse(this.responseText, SDK.REST._dateReviver).d.results);
}
else {
alert(SDK.REST._errorHandler(this));
}
}
};
retrieveRecordsReq.send();
}
I am just wondering why one has to use a temporary variable "that" (initialized with the currently allocated object i.e. "this") in the script below:
$(document).ready(function() {
function ChatViewModel() {
var that = this;
that.userName = ko.observable('');
that.chatContent = ko.observable('');
that.message = ko.observable('');
that.messageIndex = ko.observable(0);
that.activePollingXhr = ko.observable(null);
var keepPolling = false;
that.joinChat = function() {
if (that.userName().trim() != '') {
keepPolling = true;
pollForMessages();
}
}
function pollForMessages() {
if (!keepPolling) {
return;
}
var form = $("#joinChatForm");
that.activePollingXhr($.ajax({url: form.attr("action"), type: "GET", data: form.serialize(), cache: false,
success: function(messages) {
console.log(messages);
for (var i = 0; i < messages.length; i++) {
that.chatContent(that.chatContent() + messages[i] + "\n");
that.messageIndex(that.messageIndex() + 1);
}
},
error: function(xhr) {
if (xhr.statusText != "abort" && xhr.status != 503) {
resetUI();
console.error("Unable to retrieve chat messages. Chat ended.");
}
},
complete: pollForMessages
}));
$('#message').focus();
}
that.postMessage = function() {
if (that.message().trim() != '') {
var form = $("#postMessageForm");
$.ajax({url: form.attr("action"), type: "POST",
data: "message=[" + that.userName() + "] " + $("#postMessageForm input[name=message]").val(),
error: function(xhr) {
console.error("Error posting chat message: status=" + xhr.status + ", statusText=" + xhr.statusText);
}
});
that.message('');
}
}
that.leaveChat = function() {
that.activePollingXhr(null);
resetUI();
this.userName('');
}
function resetUI() {
keepPolling = false;
that.activePollingXhr(null);
that.message('');
that.messageIndex(0);
that.chatContent('');
}
}
//Activate knockout.js
ko.applyBindings(new ChatViewModel());
});
Why can't I just use "this"? Can anyone please explain?
this always refers to the object that is in scope when the call has been made, and this can change depending on your code. If you want it to still be your object in a sub-function, then assigning it to a variable that won't change in value gets around this issue.
This refers to the owner.
You can rewrite your code like this :
$(document).ready(function() {
function ChatViewModel() {
var that = this;
this.userName = ko.observable('');
this.chatContent = ko.observable('');
this.message = ko.observable('');
this.messageIndex = ko.observable(0);
this.activePollingXhr = ko.observable(null);
var keepPolling = false;
this.joinChat = function() {
if (that.userName().trim() != '') {
keepPolling = true;
pollForMessages();
}
}
function pollForMessages() {
if (!keepPolling) {
return;
}
var form = $("#joinChatForm");
this.activePollingXhr($.ajax({url: form.attr("action"), type: "GET", data: form.serialize(), cache: false,
success: function(messages) {
console.log(messages);
for (var i = 0; i < messages.length; i++) {
that.chatContent(that.chatContent() + messages[i] + "\n");
that.messageIndex(that.messageIndex() + 1);
}
},
error: function(xhr) {
if (xhr.statusText != "abort" && xhr.status != 503) {
resetUI();
console.error("Unable to retrieve chat messages. Chat ended.");
}
},
complete: pollForMessages
}));
$('#message').focus();
}
this.postMessage = function() {
if (that.message().trim() != '') {
var form = $("#postMessageForm");
$.ajax({url: form.attr("action"), type: "POST",
data: "message=[" + that.userName() + "] " + $("#postMessageForm input[name=message]").val(),
error: function(xhr) {
console.error("Error posting chat message: status=" + xhr.status + ", statusText=" + xhr.statusText);
}
});
that.message('');
}
}
this.leaveChat = function() {
that.activePollingXhr(null);
resetUI();
that.userName('');
}
function resetUI() {
keepPolling = false;
that.activePollingXhr(null);
that.message('');
that.messageIndex(0);
that.chatContent('');
}
}
//Activate knockout.js
ko.applyBindings(new ChatViewModel());
//fixing bracet
});
Check this link: http://www.quirksmode.org/js/this.html