I am having trouble updating my HTML UI.
When the document load and calls "getAllProducts()", the HTML UI displays all my items and with the right css class for 'styleStatusCss', and the right 'displayName', the problem is that when I try to update my observableArray with a newly updated product (product name or status has changed), the html UI does not update and remains the same
So here is a quick list of what is happening:
getUpdatedProducts() is called every 25 sec, and returns, any product
that has been updated
I check how many products my observable array has: appVM.itemList.length and it does have 100 (as expected!), I also check that the json product that has been sent back has some modified data, and indeed it has changed!
I then create the javascrip obj MyProduct using that product json object
Now I add my newly created javascript obj MyProduct to the observablearray: appVM.itemList.push(newUpdatedProduct);
And finally I check how many items my observablearray has, after doing the push, (and since I cannot see any changes on the HTML UI), and appVM.itemList.length now says 101 !!! How can that be? the HTML UI still displays the data as it was after the initial load
Please see below most of the code
HTML
<table >
<tbody data-bind="foreach: itemList">
<tr>
<td>
<div data-bind="css: styleStatusCss"></div>
</td>
<td>
<div data-bind="text: displayName"></div>
</td>
</tr>
</tbody></table>
And here is the javascript:
<script type="text/javascript">
var appVM;
var initializeItems = false;
$.ajaxSetup({
// Disable caching of AJAX responses
cache: false
});
$(document).ready(function () {
getAllProducts();
});
setInterval(function () {
if (initializeItems) {
getUpdatedProducts();
}
}, 25000);
function getAllProducts() {
var url = '#Url.Action("_AllProducts", "Home")';
$.ajax({
url: url,
type: 'GET',
dataType: 'JSON',
})
.success(function (result) {
initializeItems = true;
appVM = new AppViewModel();
var mappedProducts = ko.utils.arrayMap(result.ItemList, function (item) {
var con = new MyProduct(item);
return con;
});
appVM.itemList = mappedProducts;
ko.applyBindings(appVM);
})
.error(function (xhr, status, error) {
alert("FATAL ERROR");
})
}
function getUpdatedProducts() {
var url = '#Url.Action("_UpdateProducts", "Home")';
$.ajax({
url: url,
type: 'GET',
dataType: 'JSON',
})
.success(function (result) {
if (result.HasNewData) {
alert("we have some data");
alert("START COUNT: " + appVM.itemList.length); //this shows all my 100 products -> START COUNT: 100
alert("Number of new items: " + result.ItemList.length); // i only get one product back for easy debugging
for (index = 0; index < result.ItemList.length; ++index) {
var updatedProdJson = result.ItemList[index]; //get the json for the product
alert("New prod json: " + objToString(updatedProdJson)); //just for debugging print out in a readable format
var newUpdatedProduct = new MyProduct(updatedProdJson);//create our MyProduct object (which has all properties as observable)
appVM.itemList.push(newUpdatedProduct); //add our new MyProduct to the list
alert("FINAL COUNT: " + appVM.itemList.length); // --> FINAL COUNT: 101
}
}
})
.error(function (xhr, status, error) {
alert("Error: " + status);
})
}
function AppViewModel() {
var self = this; //so it references the viewModel object
self.itemList = ko.observableArray([]);
self.doAlert = function () {
alert("HERE");
}
}
function MyProduct(data) {
//alert("DATA: " + objToString(data));
this.Id = ko.observable( data.Id);
this.Name = ko.observable(data.Name);
this.status = ko.observable(data.Status);
this.displayName = ko.computed(function () {
var fullnmae = this.Id() + " " + this.Name();
return fullnmae;
}, this);
this.styleStatusCss = ko.computed(function () {
var pStatus = 'divstatusnone';
if (this.status() === 'H')
pStatus = 'divlowstatus';
if (this.status() === 'D')
pStatus = 'divhighstatus';
return pStatus;
},this);
}
function objToString (obj) {
var str = '';
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
str += p + '::' + obj[p] + '\n';
}
}
return str;
}
Hope somebody can tell me where i went wrong.
Many thanks,
in getAllProducts, you're assigning the results to itemList, losing your observable array:
appVM.itemList = mappedProducts;
you need to do this instead:
appVM.itemList(mappedProducts);
Related
PHP/HTML:
<ul id="load-more-div"></ul>
<a id="load-more" data-ppp="<?php echo get_option('posts_per_page'); ?>">load more</a>
JavaScripts:
(function($) {
// Grab the load more button, since I only want to run the code if the button is on the page
var loadMoreButton = $("#load-more");
if (loadMoreButton) {
// Get the posts_per_page number set in Reading Options
var ppp = loadMoreButton.data("ppp");
// Initialize function
var loadPosts = function(page) {
var theData, loadMoreContainer, errorStatus, errorMessage;
// The AJAX request
$.ajax({
url: "/wp-json/wp/v2/posts",
dataType: "json",
data: {
// Match the query that was already run on the page
per_page: ppp,
page: page,
type: "post",
orderby: "date"
},
success: function(data) {
// Remove the button if the response returns no items
if (data.length < 1) {
loadMoreButton.remove();
}
// Create a place to store exactly what I need
// Alternatively, the response can be filtered to only return the needed data, which is probably more efficient as the following loop wont be needed
theData = [];
// Get only what I need, and store it
for (i = 0; i < data.length; i++) {
theData[i] = {};
theData[i].id = data[i].id;
theData[i].link = data[i].link;
theData[i].title = data[i].title.rendered;
theData[i].content = data[i].content.rendered;
}
// Grab the container where my data will be inserted
loadMoreContainer = $("#load-more-div");
// For each object in my newly formed array, build a new element to store that data, and insert it into the DOM
$.each(theData, function(i) {
loadMoreContainer.append(
'<li><a href="' +
theData[i].link +
'">' +
theData[i].title +
"</a></li>"
);
});
},
error: function(jqXHR, textStatus, errorThrown) {
errorStatus = jqXHR.status + " " + jqXHR.statusText + "\n";
errorMessage = jqXHR.responseJSON.message;
// Show me what the error was
console.log(errorStatus + errorMessage);
}
});
};
// Since our AJAX query is the same as the original query on the page (page 1), start with page 2
var getPage = 2;
// Actually implement the functionality when the button is clicked
loadMoreButton.on("click", function() {
loadPosts(getPage);
// Increment the page, so on the next click we get the next page of results
getPage++;
});
}
})(jQuery);
This is the trouble part, it doesn't remove the link.
// Remove the button if the response returns no items
if (data.length < 1) {
loadMoreButton.remove();
}
Console errors when click the load more link after reaching the end of posts:
400 Bad Request The page number requested is larger than the number of pages available.
I found two ways to solve it:
###Using data attribute
Get the max number of pages in the template, assign it to a data attribute, and access it in the scripts. Then check current page against total page numbers, and set disabled states to the load more button when it reaches the last page.
PHP/HTML:
<ul id="ajax-content"></ul>
<button type="button" id="ajax-button" data-endpoint="<?php echo get_rest_url(null, 'wp/v2/posts'); ?>" data-ppp="<?php echo get_option('posts_per_page'); ?>" data-pages="<?php echo $wp_query->max_num_pages; ?>">Show more</button>
JavaScripts:
(function($) {
var loadMoreButton = $('#ajax-button');
var loadMoreContainer = $('#ajax-content');
if (loadMoreButton) {
var endpoint = loadMoreButton.data('endpoint');
var ppp = loadMoreButton.data('ppp');
var pages = loadMoreButton.data('pages');
var loadPosts = function(page) {
var theData, errorStatus, errorMessage;
$.ajax({
url: endpoint,
dataType: 'json',
data: {
per_page: ppp,
page: page,
type: 'post',
orderby: 'date'
},
beforeSend: function() {
loadMoreButton.attr('disabled', true);
},
success: function(data) {
theData = [];
for (i = 0; i < data.length; i++) {
theData[i] = {};
theData[i].id = data[i].id;
theData[i].link = data[i].link;
theData[i].title = data[i].title.rendered;
theData[i].content = data[i].content.rendered;
}
$.each(theData, function(i) {
loadMoreContainer.append('<li>' + theData[i].title + '</li>');
});
loadMoreButton.attr('disabled', false);
if (getPage == pages) {
loadMoreButton.attr('disabled', true);
}
getPage++;
},
error: function(jqXHR) {
errorStatus = jqXHR.status + ' ' + jqXHR.statusText + '\n';
errorMessage = jqXHR.responseJSON.message;
console.log(errorStatus + errorMessage);
}
});
};
var getPage = 2;
loadMoreButton.on('click', function() {
loadPosts(getPage);
});
}
})(jQuery);
###Using jQuery complete event
Get the total pages x-wp-totalpages from the HTTP response headers. Then change the button states when reaches last page.
PHP/HTML:
<ul id="ajax-content"></ul>
<button type="button" id="ajax-button" data-endpoint="<?php echo get_rest_url(null, 'wp/v2/posts'); ?>" data-ppp="<?php echo get_option('posts_per_page'); ?>">Show more</button>
JavaScripts:
(function($) {
var loadMoreButton = $('#ajax-button');
var loadMoreContainer = $('#ajax-content');
if (loadMoreButton) {
var endpoint = loadMoreButton.data('endpoint');
var ppp = loadMoreButton.data('ppp');
var pager = 0;
var loadPosts = function(page) {
var theData, errorStatus, errorMessage;
$.ajax({
url: endpoint,
dataType: 'json',
data: {
per_page: ppp,
page: page,
type: 'post',
orderby: 'date'
},
beforeSend: function() {
loadMoreButton.attr('disabled', true);
},
success: function(data) {
theData = [];
for (i = 0; i < data.length; i++) {
theData[i] = {};
theData[i].id = data[i].id;
theData[i].link = data[i].link;
theData[i].title = data[i].title.rendered;
theData[i].content = data[i].content.rendered;
}
$.each(theData, function(i) {
loadMoreContainer.append('<li>' + theData[i].title + '</li>');
});
loadMoreButton.attr('disabled', false);
},
error: function(jqXHR) {
errorStatus = jqXHR.status + ' ' + jqXHR.statusText + '\n';
errorMessage = jqXHR.responseJSON.message;
console.log(errorStatus + errorMessage);
},
complete: function(jqXHR) {
if (pager == 0) {
pager = jqXHR.getResponseHeader('x-wp-totalpages');
}
pager--;
if (pager == 1) {
loadMoreButton.attr('disabled', true);
}
}
});
};
var getPage = 2;
loadMoreButton.on('click', function() {
loadPosts(getPage);
getPage++;
});
}
})(jQuery);
The problem appears to be an invalid query to that endpoint so the success: function() is never being run in this circumstance.
Add to All API Errors
You could add the same functionality for all errors like this...
error: function(jqXHR, textStatus, errorThrown) {
loadMoreButton.remove();
....
}
Though that may not be the desired way of handling of all errors.
Test for Existing Error Message
Another option could be to remove the button if you receive an error with that exact message...
error: function(jqXHR, textStatus, errorThrown) {
if (jqXHR.statusText === 'The page number requested is larger than the number of pages available.') {
loadMoreButton.remove();
}
....
}
but this would be susceptible to breaking with any changes to that error message.
Return Custom Error Code from API
The recommended way to handle it would be to return specific error code (along with HTTP status code 400) to specify the exact situation in a more reliable format...
error: function(jqXHR, textStatus, errorThrown) {
if (jqXHR.statusCode === '215') {
loadMoreButton.remove();
}
....
}
Here's an example on how to configure error handling in an API: Best Practices for API Error Handling
Return 200 HTTP Status Code
The last option would be to change the way your API endpoint handles this type of "error"/situation, by returning a 200 level HTTP status code instead, which would invoke the success: instead of the error: callback instead.
I stucked on one thing. I have a 2 grid inside checkboxes. When I selected that checkboxes I want to POST that row data values like array or List. Actually when i send one list item it's posting without error but when i get more than one item it couldn't post values.
Example of my grid
Here my ajax request and how to select row values function
var grid = $("#InvoceGrid").data('kendoGrid');
var sel = $("input:checked", grid.tbody).closest("tr");
var items = [];
$.each(sel, function (idx, row) {
var item = grid.dataItem(row);
items.push(item);
});
var grid1 = $("#DeliveryGrid").data('kendoGrid');
var sel1 = $("input:checked", grid1.tbody).closest("tr");
var items1 = [];
$.each(sel1, function (idx, row) {
var item1 = grid1.dataItem(row);
items1.push(item1);
});
$.ajax({
url: '../HeadOffice/CreateInvoice',
type: 'POST',
data: JSON.stringify({ 'items': items, 'items1': items1, 'refnum': refnum }),
contentType: 'application/json',
traditional: true,
success: function (msg) {
if (msg == "0") {
$("#lblMessageInvoice").text("Invoices have been created.")
var del = $("#InvoiceOKWindow").data("kendoWindow");
del.center().open();
var del1 = $("#InvoiceDetail").data("kendoWindow");
del1.center().close();
$("#grdDlvInv").data('kendoGrid').dataSource.read();
}
else {
$("#lblMessageInvoice").text("Problem occured. Please try again later.")
var del = $("#InvoiceOKWindow").data("kendoWindow");
del.center().open();
return false;
}
}
});
This is my C# part
[HttpPost]
public string CreateInvoice(List<Pm_I_GecisTo_Result> items, List<Pm_I_GecisFrom_Result> items1, string refnum)
{
try
{
if (items != null && items1 != null)
{
//do Something
}
else
{
Log.append("Items not selected", 50);
return "-1";
}
}
catch (Exception ex)
{
Log.append("Exception in Create Invoice action of HeadOfficeController " + ex.ToString(), 50);
return "-1";
}
}
But when i send just one row it works but when i try to send more than one value it post null and create problem
How can i solve this? Do you have any idea?
EDIT
I forgot to say but this way is working on localy but when i update server is not working proper.
$.ajax({
url: '../HeadOffice/CreateInvoice',
type: 'POST',
async: false,
data: { items: items, items1: items1 }
success: function (msg) {
//add codes
},
error: function () {
location.reload();
}
});
try to call controller by this method :)
Here I have this table:
If I click the button, I want to pass the table per row to the controller, then perform ADO.NET Query per row, like for example, perform "UPDATE tbluser SET note='INACTIVE' WHERE id=#id" per row.
One of the main purpose of this is when i filter the table, only the visible rows will be passed.
I already have a code here to pass to controller using AJAX but I don't know what to do afterwards.
JS:
var HTMLtbl =
{
getData: function (table) {
var data = [];
oTable.rows({ search: 'applied' }).every(function () {
var cols = [];
var rowNode = this.node();
$(rowNode).find("td").each(function () {
cols.push($(this).text().trim() || null);
});
data.push(cols);
});
return data;
}
}
$("btnSubmit").on("click", function () {
var data = HTMLtbl.getData($(".datatable"));
var parameters = {};
parameters.array = data;
var request = $.ajax({
async: true,
cache: false,
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/Home/SubmitTable",
data: JSON.stringify(parameters),
success: function () {
window.location.href = "/Home/Index";
}
});
request.fail(function (jqXHR, textStatus) {
alert("Request failed: " + textStatus);
});
});
Controller:
[HttpPost]
public JsonResult SubmitTable(string[][] array)
{
//I don't know what to do here now, please help
}
My Solution based on Mostafa's answer:
JS:
var HTMLtbl =
{
getData: function () {
var data = [];
oTable.rows({ search: 'applied' }).every(function () {
var cols = [];
var rowNode = this.node();
$(rowNode).find("td").each(function () {
cols.push($(this).text().trim() || null);
});
data.push(cols);
});
return data;
}
}
$("#btnSubmit").on("click", function () {
var data = HTMLtbl.getData($(".datatable"));
var parameters = {};
parameters.array = data;
var request = $.ajax({
async: true,
cache: false,
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/Home/SubmitTable",
data: JSON.stringify(parameters),
success: function () {
window.location.href = "/Home/Index";
}
});
request.fail(function (jqXHR, textStatus) {
alert("Request failed: " + textStatus);
});
});
Controller:
[HttpPost]
public JsonResult SubmitTable(string[][] array)
{
string result = string.Empty;
try
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["Connection"].ConnectionString);
con.Open();
foreach (var arr in array)
{
SqlCommand cmd = new SqlCommand("UPDATE tbluser SET remark='INACTIVE' WHERE id = #id", con);
cmd.Parameters.AddWithValue("#id", arr[0]);
cmd.ExecuteNonQuery();
}
con.Close();
}
catch (Exception ex)
{
result = ex.Message;
}
return Json("Records updated successfully.", JsonRequestBehavior.AllowGet);
}
I can now use this for more complicated stuff, thanks
If you want to update a custom row you can add a button for each row with custom text and icon, then add a "data-" attribute to this button and path your row id,
<input type="button" data-rowId="1" class="changeActivationState">activate</input>
in this example I added a data field to my imput after that I define this javascript method:
$(".changeActivationState").click(function(){
$(this).data("rowId")//get the selected row id and call you service
});
using this code you can read first element for each row and perform a web service call for all rows
var arr = [];
$("#yourtable tr").each(function(){
arr.push($(this).find("td:first").text()); //put elements into array
});
and using this code you can read all rows into a json object
var tbl = $('#yourtable tr').map(function() {
return $(this).find('td').map(function() {
return $(this).html();
}).get();
}).get();
assume that you passed the list to action
int[] passedIDsfromBrowser = ///filled with data that comes from browser;
SqlConnection connection = ....
SqlCommand command = new SqlCommand(connection);
command.CommandText = "Update MYTABLENAME Set Active = true where ID in (" string.Join(",", passedIDsfromBrowser ) + ")";
connection.Open();
command.ExecuteNonQuery();
connection.Close();
this is a pseudo code.
or if you want a loop and updating each row with a loop
SqlConnection connection = ....
SqlCommand command = new SqlCommand(connection);
connection.Open();
for(int i = 0 ; i < passedIDsfromBrowser.Length; i++){
command.CommandText = "YOURQUERY";
command.ExecuteNonQuery();
}
connection.Close();
I want to update textbox's value(that contains cookie's value) using Ajax in asp.net MVC5 . I'm very new in JavaScript and I wrote these codes , but my code didn't work . I didn't get any error but it's not working. I wrote JavaScript in foreign file 'UpdateTxtBox.js' and I added <script src="~/Scripts/UpdateTxtBox.js"></script> to Layout .
Could anyone tell me what's the problem ?
$(function () {
$("textCountProduct").change(function () {
var count = $(this).val();
var id = $(this).attr("productid");
$.ajax({
url: "/Goods/AddToCart",
data: { Id: id, Count: count },
type: "Post",
dataType: "Json",
success: function (result) {
if (result.Success) {
alert(result.Html);
$("#CartItems").html(result.Html);
}
eval(result.Script);
},
error: function () {
alert("error....");
}
});
});
});
a part of Basket.cshtml
#using (Html.BeginForm("AddToCart", "Goods", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.TextBoxFor(modelItem => item.Count, new { #class="text textCountProduct" , style="width:40px;" , productid=item.GoodDetails.DetailsGoodID})
}
Good controller
public ActionResult AddToCart (int Id , int Count)
{
try
{
if (Request.Cookies.AllKeys.Contains("NishtmanCart_" + Id.ToString()))
{
//Edit cookie
var cookie = new HttpCookie("NishtmanCart_" + Id.ToString(), (Convert.ToInt32(Request.Cookies["NishtmanCart_" + Id.ToString()].Value) + 1).ToString());
cookie.Expires = DateTime.Now.AddMonths(1);
cookie.HttpOnly = true;
Response.Cookies.Set(cookie);
}
else
{
//Add new cookie
var cookie = new HttpCookie("NishtmanCart_" + Id.ToString(), Count.ToString());
cookie.Expires = DateTime.Now.AddMonths(1);
cookie.HttpOnly = true;
Response.Cookies.Add(cookie);
}
List<HttpCookie> lst = new List<HttpCookie>();
for (int i = 0; i < Request.Cookies.Count; i++ )
{
lst.Add(Request.Cookies[i]);
}
bool isGet = Request.HttpMethod == "GET";
int CartCount = lst.Where(p => p.Name.StartsWith("NishtmanCart_") && p.HttpOnly != isGet).Count();
return Json(new MyJsonData()
{
Success = true,
Script = MessageBox.Show("Good added successfully", MessageType.Success).Script,
//Script = "alert('Good added successfully');",
Html = "cart items (" + CartCount.ToString() + ")"
}
);
}
Update post :
I added [HttpPost] to controller action result and add some alert to javascript
$(function () {
alert("aleeeert");
$(".textCountProduct").change(function () {
var count = $(this).val();
var id = $(this).attr("productid");
alert(count);
alert(id);
$.ajax({
url: "/Goods/AddToCart",
data: { Id: id, Count: count },
type: "Post",
dataType: "Json",
success: function (result) {
if (result.Success) {
alert(result.Html);
$("#CartItems").html(result.Html);
}
eval(result.Script);
},
error: function () {
alert("error....");
}
});
});
});
it's working fine but when I refresh page , data didn't saved
Since you have specified textCountProduct as CSS class, you need to prefix it with . to use Class Selector (“.class”), As of now its looking for Element textCountProduct which obviously doesn't exists.
Use
$(".textCountProduct").change(
You have made mistake here $("textCountProduct") use . as selector.
It should be $(".textCountProduct")
and
Check path of your script included
<script src="~/Scripts/UpdateTxtBox.js"></script>
acm_/membership_generator_html.htm code:
<!DOCTYPE html>
<html>
<head>
<title>lidmaatschapgenerator</title>
<script src="../ClientGlobalContext.js.aspx"></script>
<script type="text/javascript" src="Scripts/jquery_1.11.1.js"></script>
<script type="text/javascript" src="Scripts/membership_generation_js.js"></script>
<link type="text/css" rel="stylesheet" href="Styles/membership_css.css" />
</head>
<body>
<div id="container">
<div class="ms-crm-Dialog-Header">
<div class="ms-crm-Dialog-Header-Title" id="DlgHdTitle">Lidmaatschappen aanmaken</div>
<div class="ms-crm-Dialog-Header-Desc" id="DlgHdDesc">Maak de persoonlijke lidmaatschappen aan van dit bedrijfslidmaatschap.</div>
</div>
<div id="buttons">
<input type="button" class="ms-crm-Button" id="btnGenerateControls" value="Haal contacten op" />
<input type="button" class="ms-crm-Button" id="btnCreateMemberships" value="Maak lidmaatschappen aan" />
</div>
<div id="ProgressContainer">
<progress id="barProgress" value="0">
<label id="ProgressReplacer"></label>
</progress>
</div>
<div id="divCreated"></div>
<div id="ContactList">
<div class="ms-crm-FieldLabel-LeftAlign">
<label>Contacten</label>
</div>
</div>
</div>
</body>
</html>
Scripts/membership_generation_js.js code:
//CRM entities and records
debugger;
var ODataPath;
var serverUrl;
var EntityId;
var accountId;
var AccountPersonalMembershipList = [];
var AccountContactsList = [];
var CompanyMembershipType = {};
var CompanyMembershipTypeId;
//html controls
var btnCreateMemberships;
var divCreated;
var btnGenerateControls;
var progressBarControl;
var progressBarReplacer;
//binding
var membershipCount = 0;
var MembershipsCreated = false;
var CheckedBoxesCount = 0;
var ProgressCount = 0;
$(document).ready(function () {
getDataParam();
//get context, serverURL and relevant IDs
var context = GetGlobalContext();
serverUrl = context.getServerUrl();
//the CRM 2013 string ends with a slash, we need to trim this;
serverUrl = serverUrl.replace(/\/$/, "");
ODataPath = serverUrl + "/XrmServices/2011/OrganizationData.svc";
//use JQuery to get the relevant controls
btnCreateMemberships = $("#btnCreateMemberships");
divCreated = $("#divCreated");
btnGenerateControls = $("#btnGenerateControls");
progressBarControl = $("#barProgress");
progressBarReplacer = $("#ProgressReplacer");
//get the membership type for the company membership.
$.when(GetCurrentMemberships(), GetMembershipType(), GetContacts()).done(function () {
CheckMembershipCount();
});
btnGenerateControls.click(function () {
ProcessContacts(AccountContactsList);
btnGenerateControls.prop("disabled", true);
btnGenerateControls.off("click");
});
btnCreateMemberships.click(function () {
CreateMemberships();
});
});
function getDataParam() {
//Get the any query string parameters and load them
//into the vals array
var vals;
if (location.search != "") {
vals = location.search.substr(1).split("&");
for (var i in vals) {
vals[i] = vals[i].replace(/\+/g, " ").split("=");
}
//look for the parameter named 'data'
for (var i in vals) {
if (vals[i][0].toLowerCase() == "data") {
parseDataValue(vals[i][1]);
return;
}
}
}
}
function parseDataValue(datavalue) {
if (datavalue != "") {
var vals = decodeURIComponent(datavalue).split("&");
for (var i in vals) {
vals[i] = vals[i].replace(/\+/g, " ").split("=");
}
EntityId = vals[0][1];
accountId = vals[1][1];
CompanyMembershipTypeId = vals[2][1];
return;
}
}
//Retrieve the personal memberships linked to the current company membership;
function GetCurrentMemberships() {
return $.ajax({
type: "GET",
contentType: "application/json; charset=uft-8",
datatype: "json",
url: ODataPath + "/acm_lidmaatschappersoonlijkSet?$filter=acm_bedrijfslidmaatschap/Id eq guid'" + EntityId + "'",
beforeSend: function (xmlHttpRequest) {
xmlHttpRequest.setRequestHeader("Accept", "application/json");
},
success: function (data) {
AccountPersonalMembershipList = data.d.results;
},
error: function () {
error();
}
});
}
//helperfunction to adjust the progress bar
//progress bar is adjusted twice per ajax call.
function add1ToProgress() {
ProgressCount++;
progressBarControl.prop("value", ProgressCount);
}
//haal het type bedrijfslidmaatschap op, enkel de velden voor het aantal en soort onderliggende lidmaatschappen.
function GetMembershipType() {
return $.ajax({
type: "GET",
contentType: "application/json; charset=uft-8",
datatype: "json",
url: ODataPath + "/acm_lidmaatschapSet(guid'" + CompanyMembershipTypeId + "')?$select=acm_soortonderliggendelidmaatschappen,acm_aantallidmaatschappen",
beforeSend: function (xmlHttpRequest) {
xmlHttpRequest.setRequestHeader("Accept", "application/json");
},
success: function (data) {
CompanyMembershipType = data.d;
},
error: function () {
error();
}
});
}
//Retrieve all customers linked to the company linked to this membership.
function GetContacts() {
return $.ajax({
type: "GET",
contentType: "application/json; charset=uft-8",
datatype: "json",
url: ODataPath + "/ContactSet?$filter=ParentCustomerId/Id eq guid'" + accountId + "'",
beforeSend: function (xmlHttpRequest) {
xmlHttpRequest.setRequestHeader("Accept", "application/json");
},
success: function (data) {
AccountContactsList = data.d.results;
},
error: function () {
error();
}
});
}
//happens after the above 3 ajax calls are finished;
//checks if the company can make additional memberships
//disables the buttons if this is the case and shows the reason.
function CheckMembershipCount() {
//check if the company already has the max amount of memberships;
if (AccountPersonalMembershipList.length >= CompanyMembershipType.acm_aantallidmaatschappen) {
$("input:button").prop("disabled", true);
divCreated.append("Dit bedrijf kan geen extra lidmaatschappen meer krijgen omdat het al aan het maximum zit.");
$("#ProgressContainer").empty();
} else {
//check if the company has any contacts that aren't yet members for this company;
//ContactIsValid returns true if there is any contact that can be made member;
for (var i in AccountContactsList) {
if (ContactIsValid(AccountContactsList[i])) {
return;
}
}
$("input:button").prop("disabled", true);
divCreated.append("Dit bedrijf heeft geen contacten, of alle gekoppelde contacten hebben reeds een lidmaatschap van dit bedrijf.");
$("#ProgressContainer").empty();
}
}
//creates the memberships for the company;
function CreateMemberships() {
//disable the button so we don't have accidental double commits
btnCreateMemberships.off("click");
btnCreateMemberships.prop("disabled", true);
var createArray = [];
//get all the checked boxes;
var checkedBoxes = $(".contactChecks:checked");
//calculate the amount of checked boxes;
CheckedBoxesCount = checkedBoxes.size();
//we want the progress bar to increment once before we start ajaxing and then twice for each ajax (before and after)
progressBarControl.prop("max", (CheckedBoxesCount * 2) + 1);
progressBarReplacer.text("0 van " + CheckedBoxesCount);
//disable the checkboxes so we don't have accidental triggers;
$(".contactChecks").prop("disabled", true);
//We want to notify how many memberships have been made after they're all made. since they're async, we'll need to use promises
//however, because we repeat the same ajax call with different parameters, we need to push them to an array and then apply() them.
checkedBoxes.each(function () {
createArray.push(CreateMembershipsAjax(this));
});
//we did the work before we start creating them, so show some progress;
add1ToProgress();
$.when.apply(null, createArray).done(function () {
//this function will only start once all ajax calls have been successfull.
divCreated.append(membershipCount + " lidmaatschappen aangemaakt:");
MembershipsCreated = true;
});
}
//if something goes wrong, alert the user;
function error() {
alert("Er is een fout gebeurd bij het uitvoeren van deze actie. info: ");
}
//add checkboxes for each contact;
function ProcessContacts(result) {
for (var i in result) {
var contact = result[i];
//Check if the contact already exists as a membership for this company.
if (ContactIsValid(contact)) {
addCheckBox(contact);
}
}
}
function addCheckBox(contact) {
//get the container to use;
var container = $("#ContactList");
//get the contactId;
var id = contact["ContactId"].toString();
//create a checkbox
var checkBox = $(document.createElement('input')).attr({
id: "cb" + id,
value: id,
"class": "contactChecks",
type: "checkbox",
});
//add the onchange to the textbox;
checkBox.on("change", CheckContactCount);
//add the container to the checkbox;
container.append(checkBox);
//show the name for the contact;
container.append(contact["FirstName"] + " " + contact["LastName"]);
//whitespace;
container.append(
$(document.createElement("br"))
);
}
//notifies the user if he tries to select more contacts than can be turned to users;
function CheckContactCount() {
//calculate the potential memberships, the current memberships and the sum of these 2;
var checkedCount = $(".contactChecks:checked").size();
var bestaandLidmaatschapCount = AccountPersonalMembershipList.length;
var totalCount = checkedCount + bestaandLidmaatschapCount;
//if the sum is greater than allowed;
if (totalCount > CompanyMembershipType.acm_aantallidmaatschappen) {
//show a message to inform
divCreated.empty();
divCreated.append("U kunt maximum " + (CompanyMembershipType["acm_aantallidmaatschappen"] - bestaandLidmaatschapCount) + " nieuwe lidmaatschappen maken.");
divCreated.append(
$(document.createElement("br"))
);
divCreated.append("U heeft nu " + checkedCount + " contacten geselecteerd.");
//disable the create button;
btnCreateMemberships.off("click");
btnCreateMemberships.prop("disabled", true);
} else {
//if the sum not greater;
//empty the creation message;
divCreated.empty();
//rebind the button, unbinding previous ones in the process;
//We don't need to worry about accidentally rebinding the button when it's disabled:
//Either we disabled through this function, so it's reversing itself;
//or it was disabled through clicking the button, which disables these checkboxes anyway.
btnCreateMemberships.off("click");
btnCreateMemberships.click(function () {
CreateMemberships();
});
btnCreateMemberships.prop("disabled", false);
}
}
function ContactIsValid(contact) {
for (var i in AccountPersonalMembershipList) {
//localeCompare returns 0 if the strings are equal, so need to invert for a boolean
if (!AccountPersonalMembershipList[i]["acm_contactpersoon"].Id.localeCompare(contact["ContactId"])) {
//if it's equal, it returns false because the contact already has a membership with this company
return false;
}
}
//if no memberships with this name are found, it should return true so the Contact is valid for showing.
//still need to check if there's not yet a control with with that id as a value.
//get all checkboxes with this ContactId
var checkboxes = $(":checkbox[value=" + contact["ContactId"] + "]");
//if any exist
if (checkboxes.length > 0) {
//return false
return false;
}
return true;
}
function CreateMembershipsAjax(element) {
var contactId = element.defaultValue;
//create the membership
var persoonlijkLidmaatschap = new Object();
persoonlijkLidmaatschap.acm_contactpersoon = { Id: contactId.replace(/[{}]/g, "") };
persoonlijkLidmaatschap.acm_bedrijfslidmaatschap = { Id: EntityId.replace(/[{}]/g, "") };
persoonlijkLidmaatschap.acm_lidmaatschap = { Id: CompanyMembershipType.acm_soortonderliggendelidmaatschappen.Id.replace(/[{}]/g, "") };
//turn the json into a string using default browser set;
var lidmaatschapJson = JSON.stringify(persoonlijkLidmaatschap);
//increment the counter once before the ajax call;
add1ToProgress();
return $.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
datatype: "json",
url: ODataPath + "/acm_lidmaatschappersoonlijkSet",
data: lidmaatschapJson,
beforeSend: function (xmlHttpRequest) {
xmlHttpRequest.setRequestHeader("Accept", "application/json");
},
success: function () {
//increment the counter again after the ajax call;
membershipCount++;
add1ToProgress();
progressBarReplacer.text(membershipCount + " van " + CheckedBoxesCount + "memberships aangemaakt.");
},
error: function () {
error();
}
});
}
Method for loading the html page (added as a javascript action on a ribbon button):
function acm_mscrm2011_openModalDialog() {
var entityId = Xrm.Page.data.entity.getId();
var accountId = Xrm.Page.getAttribute("acm_account").getValue()[0].id;
var companyMembershipTypeId = Xrm.Page.getAttribute("acm_typelidmaatschap").getValue()[0].id;
var parameters = encodeURIComponent("entityId=" + entityId + "&accountId=" + accountId + "&membershiptype=" + companyMembershipTypeId);
Xrm.Utility.openWebResource("acm_/membership_generator_html.htm", parameters);
}
this code works fine in Chrome. However, in IE11, the html webresource is opened, Jquery is loaded, the CSS and ClientContext is loaded, but the javascript code above loads for a few seconds (checked using the F12 tools), but after a few seconds, it disappears from my resource list in the tools. I also get a message in my error log: "Id, something, or something else is expected" (not too sure about those other 2) on:
var checkBox = $(document.createElement('input')).attr({
id: "cb" + id,
value: id,
"class": "contactChecks",
type: "checkbox",
});
I don't know what causes this. it works fine in Chrome, but not in IE11. This is just some code to dynamically generate controls based on some CRM data, I don't know why it fails in one browser, but not the other.
I found out what caused this and as always, it's something really small.
IE11 complains about the comma at the end of my attribute array. I removed that and it worked.