My question revolves around appending items to a dropdownlist based upon choices made on a previous dropdownlist. Here's my code in JS:
function propogateProgramListOptions(data) {
$.post('PostProgramData',
{ clientId: data },
function(result) {
var programs = result.data;
for (var i = 0; i < programs.length; i++) {
alert(programs[i]);
}
});
}
Here's my code in C#:
public JsonResult PostProgramData(string[] clientId) {
string domainUserName = GetDomainUserName();
ProgramRef[] programsArray = _manager.GetCPRPrograms(domainUserName, clientId);
List<ProgramRef> programsList = programsArray.ToList();
return Json(new {data = programsList}, JsonRequestBehavior.AllowGet);
}
Now each ProgramRef has a property of ProgramCode and ProgramName. Usually in C#, you can just iterate through the array and do programsArray[i].ProgramCode etc. But in this case i'm returning it as a Jsonresult. How would i access the ProgramCode and ProgramName properties of the returned item in JS and do result[i].programName?
Got the answer.
programs[i].ProgramCode
programs[i].ProgramName
I didn't think this would work because my IDE was complaining about it being a global variable.
Related
Multiple entity records have to be deleted in one call instead of multiple callbacks so trying to use Xrm.WebApi.online.executeMultiple to delete records. but the code written below is not working. Any help will be appreciated.
for (var i=0; i<Checkbox.length; i++)
{
if(Checkbox[i].checked)
{
var id = Checkbox[i].value;// GUID of the record to be deleted
Checkbox[i].checked = false;
DeleteRequests[i]={};
DeleteRequests[i].getMetadata = function(){
return{
boundParameter: undefined,
operationType: 2,
operationName: "Delete",
parameterTypes: {
}
}
}
DeleteRequests[i].etn="cme_entity";
DeleteRequests[i].payload=id;
}
}
window.parent.Xrm.WebApi.online.executeMultiple(DeleteRequests).then(
function (results) {alert("Success");},
function (error) {alert("Failed");});
Getting weird error that this operation could not be processed. Please contact Microsoft.
The issue has to do with how you are constructing the delete request objects. You need to declare a function that sets up the getMetadata function and the required entityReference object.
I've tested the below solution and it works.
var Sdk = window.Sdk || {};
Sdk.DeleteRequest = function (entityReference) {
this.entityReference = entityReference;
this.getMetadata = function () {
return {
boundParameter: null,
parameterTypes: {},
operationType: 2,
operationName: "Delete",
};
};
};
for (var i = 0; i < Checkbox.length; i++) {
if (Checkbox[i].checked) {
var id = Checkbox[i].value;
Checkbox[i].checked = false;
DeleteRequests[i] = new Sdk.DeleteRequest({ entityType: "account", id: id });
}
}
window.parent.Xrm.WebApi.online.executeMultiple(DeleteRequests).then(
function (results) { alert("Success"); },
function (error) { alert("Failed"); });
Unfortunately CRUD operations with Xrm.WebApi.online.execute and Xrm.WebApi.online.executeMultiple are not very well documented. I've written a blog post with some code samples.
The important parts are the declaration of the Sdk.DeleteRequest function as a property on window and instantiating a request object using new Sdk.DeleteRequest(). I experimented a little and determined that just simply creating a request object like you were doing before, even with the right attributes does not work either.
Hope this helps! :)
I'm building a webshop where users are able to add products for one of more stores in their basket and checkout (like AliExpress).
On the cart overview page, the content of the basket is shown sorted by store. If the same product is added multiple times over different stores, the product is show by every store.
Now, I want to create an order for every store with the products ordered by that store. I'm using Angular to create the list with products ordered/filtered by store.
That data will be sent to my Node.JS server, to loop the contents and create some orders with items.
The problem, I think, is that the data is processed like a 'object' and not an 'array'. I have found a function which converts a object to an array, but the length is still '0'.
How can I process the data so I can loop through the different items?
AngularJS code to sort cart by store
$scope.filterProducts = function(groupName) {
$scope.productList = [];
$http({
method: 'GET',
xhrFields: {withCredentials: true},
url: '/loadCart'
}).then(function successCallback(response){
if (response.data) {
var mapByShop = function(arr, groupName) {
return arr.reduce(function(result, item) {
result[item[groupName]] = result[item[groupName]] || {};
result[item[groupName]][item['productId']] = item;
console.log('GROUPNAME en RESULT', groupName, result);
return result;
}, {});
};
if (response.data.length > 0) {
if (groupName == 'shopName') {
$scope.productList = mapByShop(response.data, groupName);
} else {
$scope.checkoutList = mapByShop(response.data, groupName);
}
}
}
}, function errorCallback(response){
console.log(response);
});
}
The $scope.productList is sent as 'data' in a $http POST function.
Node.JS code to convert an object to an array
function convertObjectToArray(object, cb){
var cartContent = [];
for (var i in object) {
cartContent[i] = object[i];
}
console.log("convertObjectToArray");
return cb(cartContent);
}
Code to process the data (where length is zero)
convertObjectToArray(req.body.cart, function(result){
console.log(isArray(result));
console.log('result', result);
console.log("lenght", result.length);
})
FYI: the isArray function
function isArray(myArray) {
return myArray.constructor.toString().indexOf("Array") > -1;
}
if array order is not important, you should use
cartContent.push(object[i]);
It will update the .length property automaticly
Your problem is that you are adding properties to the array object, and not using the Array API to insert at integer locations in the object. This means the array essentially remains "empty". If you key on an integer when inserting into the array then your code will work better.
The broken bit:
for (var i in object) {
cartContent[i] = object[i];
}
i is a string key here and will not increment the length of the Array unless the value coerces to an integer value (I think).
Something like this might work:
// Untested...
var keys = Object.keys(object);
for (var i = 0; i < keys.length; i++) {
cartContent[i] = object[keys[i]];
}
Or like the other answer suggested, use the push API.
Aside:
If you are in a modern JS engine you can:
Use Object.values, or
Write a couple of utility functions and convert an object to an array using the following
:
var o = iterable({foo:'fooValue', bar: 'barValue'});
console.log([...o]);
The utility functions:
function iterable(o) {
if(o[Symbol.iterator]) {
return o;
}
o[Symbol.iterator] = iter.bind(null, o);
return o;
}
function* iter(o) {
var keys = Object.keys(o);
for (var i=0; i<keys.length; i++) {
yield o[keys[i]];
}
}
I'm trying to call a method from my c# hub class but when I try to return a string from the javascript code I get a System.Threading.Tasks.Task instead of a string. I'm wondering how I can edit the javascript method to return an actual string.
Here's the hub class:
public String getMessage(int id)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
var s = hubContext.Clients.All.getMessage(id);
return s;
}
JavaScript:
chat.client.getMessage = function (id) {
for (i = 0; i < messageArr.length; i++) {
if (messageArr[i].ID == id) {
var s = messageArr[i].ID + messageArr[i].Name + messageArr[i].Content;
window.alert(s);
return s;
}
}
}
SignalR does not support return values from clients to hubs. The task returned by Clients.All will complete when all clients have been called.
If you want return values from clients, you will have to manually call the hub with the correct value and match the caller using your own message id.
See the documentation
You can't get a return value from a client method; syntax such as int
x = Clients.All.add(1,1) does not work.
I am stuck in a concept of html5 data attributes. That attributes allows nesting like:
<div data-user--name="John" data-user--surname="Doe"></div>
I have seen plugins in the past (like select2) and some of them use the following similar syntax to make an AJAX call:
<div data-ajax--url="my/url" data-ajax--method="POST">
This code in background converts to a dataset in javascript and it returns something like this:
data = {
ajax: {
url: "my/url",
method: "POST"
}
}
But in the practice, vanilla javascript's dataset and jQuery data() methods return different object content.
Javascript
var el = document.getElementsByTagName("div")[0];
el.innerHTML = "<pre>"+JSON.stringify(el.dataset)+"</pre>";
<div data-ajax--url="my/url" data-ajax--method="POST"></div>
jQuery 1.x
$('div').html("<pre>"+JSON.stringify($('div').data())+"</pre>");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div data-ajax--url="my/url" data-ajax--method="POST"></div>
jQuery 2.x
$('div').html("<pre>"+JSON.stringify($('div').data())+"</pre>");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div data-ajax--url="my/url" data-ajax--method="POST"></div>
The code in error seems to be the jQuery 1.x versions, because in 2.x versions jQuery returns the same as vanilla Javascript. I found a related bug so it's confirmed: https://github.com/select2/select2/issues/2969
But I can't find where to construct a nested javascript object with the nested html syntax, like the following example:
{
ajax: {
url: "my/url"
method: "POST"
}
}
Is there any Javascript method, or a polyfill, that makes this kind of objects reading the data-* HTML attributes? Is it possible to parse the data javascript strings (i.e. ajax-Method) and return a nested object (ajax.method) ?
Ran into exact same need, but #artofcode's answer parses only 2 levels. So I had to figure out how to parse unlimited number of levels. Here's my solution without limiting levels, based on original answer.
function parseNestedDataSet(data_set) {
var keys = Object.keys(data_set);
var data = {};
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = data_set[key];
var splat = key.split('-');
data = parseNestedDataSetKey(splat, value, data);
}
return data;
}
function parseNestedDataSetKey(keys, value, data) {
data = data || {};
var key = keys[0].toLowerCase();
// Not tested, but should convert to camel case
// var key = keys[0].replace(/-([a-z])/g, function (g) {
// return g[1].toUpperCase();
// });
if (!data[key]) {
data[key] = {};
}
if (keys.length > 1) {
keys.splice(0, 1);
data[key] = parseNestedDataSetKey(keys, value, data[key]);
} else {
data[key] = value;
}
return data;
}
Didn't test it thoroughly, but it works in my case, like:
data-buttons--btn1--title;
data-buttons--btn1--icon;
data-buttons--btn2--title.
function parseDataset(dataset) {
data = {};
for(var i = 0; i < Object.keys(dataset).length; i++) {
var key = Object.keys(dataset)[i];
var value = dataset[key];
var splat = key.split("-");
console.log(key, data, splat);
if(!data[splat[0]]) {
data[splat[0]] = {};
}
data[splat[0]][splat[1]] = value;
}
return data;
}
Untested, but should work. Pass el.dataset into the method, get a data object out like:
data = {
'ajax': {
'Method': 'POST',
'Url': 'my/url'
}
};
I have a JavaScript object that I am stringifying with JSON.stringify that returns a JSON string with parent and children data.
When I try to parse this string back to an object, the children objects are now null.
function cacheForm(agency) {
var agency = ko.toJS(this); //easy way to get a clean copy
delete agency.contacts; //remove an extra property
for (i in agency.offices) {
for (val in agency.offices[i]) {
//delete agency.offices[i].agency;
//delete agency.offices[i].agencyID;
}
}
for (i in agency.offices) {
for (ii in agency.offices[i].contacts) {
for (val in agency.offices[i].contacts[ii]) {
//delete agency.offices[i].contacts[ii].office;
//delete agency.offices[i].contacts[ii].agencyID;
//delete agency.offices[i].contacts[ii].officeID;
}
}
}
var value = agency;
var cache = [];
parsed = JSON.stringify(value, function (key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// Circular reference found, discard key
return;
}
// Store value in our collection
cache.push(value);
}
return value;
});
var data = JSON.parse(parsed);
}
Edit
Agency part of my view model that I am passing into my cacheForm function and I am using
var agency = ko.toJS(this);
to have my data available in an object which can be parsed to JSON string. I may of deleted this code in my post because my original code had many annotations.
Your question initially showed a screen shot where data.offices = [null] was highlighted.
It's not a parsing error, but an error in stringify. Your paste already has data.offices = [null].
MDN states regarding replacer:
Note: You cannot use the replacer function to remove values from an array. If you return undefined or a function then null is used instead.
And furthermore regarding stringify:
If undefined, a function, or an XML value is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array).
I don't have access to your original object, and hence cannot tell which of the two you are hitting...
Implementing toJSON (or just explicitly constructing another object from the source object) instead of a replacer to filter arrays would be the way to go, if the problem is within your current replacer implementation.
there are various js libraries predefined for parsing json and to get children values . What i usually do to parse json is use http://developer.yahoo.com/yui/json/ YUI library.
So I eventually solved my problem and this is how I did it.
function cacheForm(agency) {
// GET my object from agency vm
var agency = ko.toJS(agency);
var s = YUI().use("json-stringify", function (Y) {
var jsonStrAgency = Y.JSON.stringify(agency, ["activities", "agencyName", "agencyID", "campaignBillings", "category", "declaredBillings", "immediateParent", "numberOfEmployees", "ultimateParent", "uRL"]); // Use an array of acceptable object key names as a whitelist.
var jsonStrOffices, jsonStrContacts;
for (i in agency.offices) {
jsonStrOffices = Y.JSON.stringify(agency.offices, ["address1", "address2", "address3", "address4", "address5", "agencyID", "faxNumber", "officeID", "postCode", "telephoneNumber"]);
for (ii in agency.offices[i].contacts) {
jsonStrContacts = Y.JSON.stringify(agency.offices[i].contacts, ["agencyID", "emailAddress", "firstName", "jobName", "officeID", "personID", "surName", "title"]);
}
}
localStorage.setItem('Agency', jsonStrAgency);
localStorage.setItem('Offices', jsonStrOffices);
localStorage.setItem('Contacts', jsonStrContacts);
});
}
Firstly I am passing in my ko.observableArray to the function cacheForm. This parameter is called agency and it is part of my viewmodel.
I want to parse my observableArray and convert it into a standard javascript object. By using ko.toJS I can do this. There will be no ko constructors after using toJS.
Then I have to get my JSON strings. Since my object has children and grandchildren I have to parse these parts separately. Stringify doesn't like arrays within an object, they will be changed to null and your children data will be lost.
Because of circular recursion, I have to use this:
var s = YUI().use("json-stringify", function (Y) {
This is part of the Yahoo API. This is the script reference:
<script src="http://yui.yahooapis.com/3.11.0/build/yui/yui-min.js"></script>
Y.JSON.stringify takes an object as one parameter and an option paremter which is an array. The purpose of this array is to contain the property names of the object you want to stringify. From other forums I found out this is known as whitelisting.
With all my JSON strings I can store them in HTML5 local storage.
When the page loads I then check to see if my local storage contains data. If true I retrieve my data and serialize from JSON string to a javascript object.
define(['services/datacontext'], function (dataContext) {
var initialized = false;
var agency;
if (localStorage.Agency && localStorage.Offices && localStorage.Contacts) {
var objAgency = new Object(ko.mapping.fromJSON(localStorage.getItem('Agency')));
var objOffices = new Object(ko.mapping.fromJSON(localStorage.getItem('Offices')));
var objContacts = new Object(ko.mapping.fromJSON(localStorage.getItem('Contacts')));
objAgency.offices = objOffices;
objAgency.offices._latestValue[0].contacts = objContacts;
agency = ko.observableArray([ko.mapping.fromJS(objAgency)]);
ko.applyBindings(agency);
initialized = true;
}
else {
agency = ko.observableArray([]);
}
Finally I reconstruct my object to how it was before stringify and map it back to an observableArray and finally bind it.
Hopefully this helps other people using a combination of knockoutJS and complicated objects.
See below for my full code:
define(['services/datacontext'], function (dataContext) {
var initialized = false;
var agency;
if (localStorage.Agency && localStorage.Offices && localStorage.Contacts) {
var objAgency = new Object(ko.mapping.fromJSON(localStorage.getItem('Agency')));
var objOffices = new Object(ko.mapping.fromJSON(localStorage.getItem('Offices')));
var objContacts = new Object(ko.mapping.fromJSON(localStorage.getItem('Contacts')));
objAgency.offices = objOffices;
objAgency.offices._latestValue[0].contacts = objContacts;
agency = ko.observableArray([ko.mapping.fromJS(objAgency)]);
ko.applyBindings(agency);
initialized = true;
}
else {
agency = ko.observableArray([]);
}
var save = function (agency, myStoredValue) {
// Clear Cache because user submitted the form. We don't have to hold onto data anymore.
//amplify.store("Agency", null);
return dataContext.saveChanges(agency);
};
var vm = { // This is my view model, my functions are bound to it.
//These are wired up to my agency view
activate: activate,
agency: agency,
title: 'agency',
refresh: refresh, // call refresh function which calls get Agencies
save: save,
cacheForm: cacheForm
};
return vm;
function activate() {
vm.agency;
if (initialized) {
return;
}
initialized = false;
return refresh();
}
function refresh() {
return dataContext.getAgency(agency);
}
function cacheForm(agency) {
// GET my object from agency vm
var agency = ko.toJS(agency);
var s = YUI().use("json-stringify", function (Y) {
var jsonStrAgency = Y.JSON.stringify(agency, ["activities", "agencyName", "agencyID", "campaignBillings", "category", "declaredBillings", "immediateParent", "numberOfEmployees", "ultimateParent", "uRL"]); // Use an array of acceptable object key names as a whitelist.
var jsonStrOffices, jsonStrContacts;
for (i in agency.offices) {
jsonStrOffices = Y.JSON.stringify(agency.offices, ["address1", "address2", "address3", "address4", "address5", "agencyID", "faxNumber", "officeID", "postCode", "telephoneNumber"]);
for (ii in agency.offices[i].contacts) {
jsonStrContacts = Y.JSON.stringify(agency.offices[i].contacts, ["agencyID", "emailAddress", "firstName", "jobName", "officeID", "personID", "surName", "title"]);
}
}
localStorage.setItem('Agency', jsonStrAgency);
localStorage.setItem('Offices', jsonStrOffices);
localStorage.setItem('Contacts', jsonStrContacts);
});
}
});