Fetch XML and optionSets - dynamics-365

basic question. Am using Fetch XML builder in XRM. How do I tell Fetch to return the optionset label instead of the numeric value?
I have tried everything. I can see in the result set the name of the OptionSet item is "there":
<resultset morerecords="0" >
<result>
<est formattedvalue="4,337.50" >4337.5</est>
<sector name="Sustainable Industrial" formattedvalue="100000000" >100000000</sector>
<sector_new_nsectorname>Sustainable Industrial</sector_new_nsectorname>
</result>
<result>
<est formattedvalue="3,216.00" >3216</est>
<sector name="Renewable" formattedvalue="100000002" >100000002</sector>
<sector_new_nsectorname>Renewable</sector_new_nsectorname>
</result>
<result>
<est formattedvalue="2,329.25" >2329.25</est>
<sector name="Environmental" formattedvalue="100000001" >100000001</sector>
<sector_new_nsectorname>Environmental</sector_new_nsectorname>
</result>
</resultset>
But the actual results I get in column view are:

In the bottom left corner of the Results view, click Appearance - Friendly Names.

I am not sure if you wanted to know how to display the OptionSet Label in the FetchXrmBuilder or actually get the value of the label using fetchXML in your code.
Unfortunately you cannot get the label value through manipulating the fetch. I am adding a sample JS code to get the label value for an OptionSet by making another fetch request to stringmap entity. I have used product(Entity) and producttypecode(OptionSet) as to display how you can get the corresponding label values in a single result set by manipulating the result set of 2 different fetch.
var finalProductList = [];
var optionSetValueDict = {};
var productResultSet = [];
function OnLoad(executionContext)
{
RetrieveOptionSetValue();
RetrieveProduct();
}
function CreateOptionSetDict(optionSetValueResultSet)
{
optionSetValueResultSet.forEach(function (obj)
{
optionSetValueDict[obj.attributevalue] = obj.value;
});
}
function CreateProductList(productResultSet)
{
productResultSet.forEach(function (obj)
{
var finalProduct = {};
finalProduct['producttypecode'] = obj.producttypecode;
finalProduct['producttypecodevalue'] = optionSetValueDict[obj.producttypecode];
finalProduct['productnumber'] = obj.productnumber;
finalProductList.push(finalProduct);
});
}
function RetrieveProduct()
{
var results;
var fetchXmlQuery = '<fetch top="50" ><entity name="product" ><attribute name="productnumber" /><attribute name="producttypecode" /></entity></fetch>';
var req = new XMLHttpRequest();
req.open(
"GET",
Xrm.Page.context.getClientUrl() +
"/api/data/v9.0/products?fetchXml=" + encodeURIComponent(fetchXmlQuery),
false);
req.setRequestHeader("Prefer", 'odata.include-annotations="*"');
req.onreadystatechange = function ()
{
if (this.readyState === 4)
{
req.onreadystatechange = null;
if (this.status === 200)
{
var results = JSON.parse(this.response);
CreateProductList(results.value);
}
else
{
alert(this.statusText);
}
}
};
req.send();
}
function RetrieveOptionSetValue()
{
var fetchXmlQuery = '<fetch><entity name="stringmap" ><attribute name="attributevalue" /><attribute name="value" /><filter type="and" ><condition attribute="objecttypecodename" operator="eq" value="product" /><condition attribute="attributename" operator="eq" value="producttypecode" /></filter></entity></fetch>';
var req = new XMLHttpRequest();
req.open(
"GET",
Xrm.Page.context.getClientUrl() +
"/api/data/v9.0/stringmaps?fetchXml=" + encodeURIComponent(fetchXmlQuery),
false);
req.setRequestHeader("Prefer", 'odata.include-annotations="*"');
req.onreadystatechange = function ()
{
if (this.readyState === 4)
{
req.onreadystatechange = null;
if (this.status === 200)
{
var results = JSON.parse(this.response);
CreateOptionSetDict(results.value);
}
else
{
alert(this.statusText);
}
}
};
req.send();
}
Hope it helps. If you are looking for a way to this in C#, please comment so.

Related

how to get label of optionset in crm

I'm using D-CRM 2016 I'm trying to get a label of optionset in client-side (js) ,I get an error and can't find a reason why:
thats my code:
SDK.Metadata.RetrieveAttribute("myEntity", "myFieldName", null, false,
function (result) {
alert(result);
for (var i = 0; i < result.OptionSet.Options.length; i++) {
var loopText = result.OptionSet.Options[i].Label.LocalizedLabels[0].Label;
var loopValue = result.OptionSet.Options[i].Value;
}
},
function (error) { }
,false);
My error:
Uncaught TypeError: _Context(...).getServerUrl is not a function
at _getUrl (/SDev/%7B636656731400000359%7D/WebResources/xnes_SDK.MetaData?ver=1561501807:451)
at Object.RetrieveAttribute (/SDev/%7B636656731400000359%7D/WebResources/xnes_SDK.MetaData?ver=1561501807:323)
at <anonymous>:1:14
at Mscrm.CommandHandler.$Ce_1 (JsProvider.ashx:8)
at Mscrm.CommandHandler.$Ag_1 (JsProvider.ashx:8)
at Mscrm.CommandHandler.handleCommand (JsProvider.ashx:8)
at Mscrm.CommandBarData.executeCommand (JsProvider.ashx:8)
at Mscrm.ButtonControl.executeCommand (ribbon.js:1)
at Mscrm.ButtonControl.click (ribbon.js:1)
at Mscrm.CommandBar.onClickHandler (ribbon.js:1)
Anytime you're attempting to do client side rest calls, I always recommend Jason Lattimer's CRM Rest Builder (https://github.com/jlattimer/CRMRESTBuilder) You can access the text labels using the "Formatted Values" option.
The principal problem is with the SDK you are using. I can't really debug it for you.
However, here's a working sample that is returning the label and the value. There a header (Prefer) you can add to tell the api to return the label as well:
var odataEndPoint = Xrm.Page.context.getClientUrl() + '/api/data/v8.2/';
function GetDomainName(entityId) {
var result = null;
var req = new XMLHttpRequest();
req.open("GET", odataEndPoint + 'systemusers(' + TrimGuid(entityId) + ')/', false);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");
req.onreadystatechange = function () {
if (this.readyState == 4) {
req.onreadystatechange = null;
if (this.status == 200) {
if (this.response) {
result = JSON.parse(this.response);
}
}
else {
var parsed = JSON.parse(this.response);
console.error(parsed.error.message)
}
}
};
req.send();
return result;
};

How to load XML from an external site server (using AJAX)?

Here I have an example I'm building (with help).
Currently, the XML is stored in a data island. But what if I wanted to make a request to an external server? Would I use an XMLHttpRequest?
How would I code that in this example, and avoid the Cross Origin XMLHttpRequest problem?
In this example, I've tried playing with function loadXMLDoc(statelabel) but without success.
Am I on the right track with this function?
function loadXMLDoc( statelabel ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myFunction(this);
}
};
xhttp.open("GET", "state_data.xml", true);
xhttp.send();
}
Fully functional code here:
<!DOCTYPE html>
<!--
SO
datalist / xml handling
Q 51200490 (https://stackoverflow.com/questions/51200490/how-to-find-the-node-position-of-a-value-in-an-xml-tree/51201494)
A
-->
<html>
<head>
<title>SO sample</title>
<script>
// Setup of keypress event handler, default selection of xml data.
function setupEH () {
var n = document.getElementById("myInputId");
n.addEventListener("keyup", function(event) {
event.preventDefault();
if (event.keyCode === 13) {
document.getElementById("myButton").click();
}
});
loadXMLDoc('Alabama'); // comment out this line if you want a vanilla UI after loading the html page.
}
// Load the xml document
function loadXMLDoc( statelabel ) {
// The xml document is retrieved with the following steps:
// 1. Obtain the (in-document) source as a DOM node.
// 2. Extract textual content.
// 3. Instantiate the xml parser (a browser built-in)
// 4. Parse textual content into an xml document
//
// When retrieving the xml document by means of ajax, these steps will be handled by the library for you - a parsed xml document will be available as a property or through calling a method.
//
let x_xmlisland = document.getElementById("template_xml");
let s_xmlsource = x_xmlisland.textContent;
let parser = new DOMParser();
let xmlDoc = parser.parseFromString(s_xmlsource, "application/xml");
myFunction(xmlDoc, statelabel); // Actual work ...
}
// Processing the xml document
function myFunction(xmlDoc, statelabel) {
// debugger; // uncomment to trace
//
// Every bit of information is processed as follows:
// - Get the relevant xml subtree ( `UNIT` element of the selected state incl.descendants )
// - Extract the textual value.
// - Feed the textual value to the Html elements prsenting the result.
//
var xpr_current_unit = xmlDoc.evaluate("/STATE_DATA/UNIT[./STATE[./text() = '"+statelabel+"']]",xmlDoc,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null);
var node_current_unit = xpr_current_unit.iterateNext();
//
// The subsequent calls to xmlDoc.evaluate set the current UNIT element as their context node ('starting point'/'temporary root' for the xpath expression).
// The context node is referenced by '.' (dot)
//
var xpr_s = xmlDoc.evaluate("./STATE/text()",node_current_unit,null,XPathResult.ORDERED_ANY_TYPE,null);
var node_s = xpr_s.iterateNext();
var s = node_s.textContent
document.getElementById("state").innerHTML = s;
var xpr_g = xmlDoc.evaluate("./GDP/text()",node_current_unit,null,XPathResult.ORDERED_ANY_TYPE,null);
var node_g = xpr_g.iterateNext();
var g = "Unknown";
if ( node_g !== null ) {
g = node_g.textContent;
}
document.getElementById("gdp").innerHTML = g;
var xpr_p = xmlDoc.evaluate("./POPULATION/text()",node_current_unit,null,XPathResult.ORDERED_ANY_TYPE,null);
var node_p = xpr_p.iterateNext();
var p = "Unknown";
if ( node_p !== null ) {
p = node_p.textContent;
}
document.getElementById("population").innerHTML = p;
// cf. https://stackoverflow.com/a/3437009
var xpr_u = xmlDoc.evaluate("count(./preceding::UNIT)+1.",node_current_unit,null,XPathResult.ORDERED_ANY_TYPE,null);
var n_ucount = xpr_u.numberValue;
document.getElementById("inputValue").innerHTML = s;
document.getElementById("nodePosition").innerHTML = n_ucount;
}
// Setup the submit click handler
function ehClick ( ) {
let node_choice = document.getElementById('myInputId');
loadXMLDoc(node_choice.value);
}
</script>
<style>
</style>
</head>
<body onload="setupEH()">
<script id="template_xml" type="text/xml"><?xml version="1.0" encoding="UTF-8"?>
<STATE_DATA>
<UNIT>
<STATE>Wisconsin</STATE>
<GDP>232,300,000,000</GDP>
<POPULATION>5,800,000</POPULATION>
</UNIT>
<UNIT>
<STATE>Alabama</STATE>
<GDP>165,800,000,000</GDP>
<POPULATION>4,900,000</POPULATION>
</UNIT>
<UNIT>
<STATE>California</STATE>
<!-- Note: the GDP node for this unit is missing -->
<POPULATION>39,600,000</POPULATION>
</UNIT>
<UNIT>
<STATE>Texas</STATE>
<GDP>1,600,000,000,000</GDP>
<POPULATION>28,300,000</POPULATION>
</UNIT>
<UNIT>
<STATE>Michigan</STATE>
<GDP>382,000,000</GDP>
<POPULATION>10,000,000</POPULATION>
</UNIT>
</STATE_DATA>
</script>
<input list="myInput" id="myInputId" value="">
<button id="myButton" onClick="ehClick()">submit</button>
<p>input value: <span id="inputValue"></span></p>
<p>XML tree node position of input value: <span id="nodePosition"></span></p>
<p>State: <span id="state"></span></p>
<p>GDP: <span id="gdp"></span></p>
<p>Population: <span id="population"></span></p>
<datalist id="myInput">
<option id="AL">Alabama</option>
<option id="CA">California</option>
<option id="MI">Michigan</option>
<option id="TX">Texas</option>
<option id="WI">Wisconsin</option>
</datalist>
</body>
</html>
This gets a little bit involved using vanilla XMLHttpRequest to load XML. Here is a quick sample.
function loadXMLDoc() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myFunction(this);
}
};
xmlhttp.open("GET", "sample.xml" , true);
xmlhttp.send();
}
function myFunction(xml) {
var x, i, xmlDoc, table;
xmlDoc = xml.responseXML;
table = "<tr><th>Artist</th><th>Title</th></tr>";
x = xmlDoc.getElementsByTagName("CD")
for (i = 0; i < x.length; i++) {
table += "<tr><td>" +
x[i].getElementsByTagName("ARTIST")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("TITLE")[0].childNodes[0].nodeValue +
"</td></tr>";
}
document.getElementById("demo").innerHTML = table;
}
However, it gets much more complicated esp. if you want to do a cross-domain request.
That's probably the right moment to pick up jQuery to make things easier:
$.ajax({
url: 'https://www.w3schools.com/xml/note.xml',
dataType: 'XML',
type: 'GET',
async: false,
crossDomain: true,
success: function () { },$.ajax({
url: 'https://www.w3schools.com/xml/note.xml',
dataType: 'XML',
type: 'GET',
async: false,
crossDomain: true,
success: function () { },
failure: function () { },
complete: function (xml) {
// Parse the xml file and get data
var xmlDoc = $.parseXML(xml);
$xml = $(xmlDoc);
$xml.find('body').each(function () {
console.log($(this).text());
});
}
});

AJAX Dynamic Buttons

Does anyone have suggestions on how to complete the following assignment? I've listed the instructions from the teacher below along with my JavaScript code. Thanks in advance!
Instructions:
The primary task is to dynamically generate the "genre" buttons that are currently hardcoded into the correlating HTML file.
The genre buttons should work the same way the hardcoded buttons currently work meaning they should have an event listener attached to them that should display podcasts from the ajax response that match the genre that was clicked.
JavaScript Code:
/**
* Ajax GET requester
*
*/
function get(url){
// Return a new promise.
return new Promise(function (resolve, reject){
// Do the usual XHR stuff
var req = new XMLHttpRequest();
req.open('GET', 'js/data.json');
req.onload = function(){
// This is called even on 404 etc
// so check the status
if(req.status === 200){
// Resolve the promise with the response text
resolve(req.response);
}else{
// Otherwise reject with the status text
// which will hopefully be a meaningful error
reject(Error(req.statusText));
}
};
// Make the request
req.send();
});
}
function get_podcasts(genre){
var url = 'js/data.json';
get(url).then(function (response){
var body = document.getElementById('mainContent');
response = JSON.parse(response);
if(response.results.length > 0){
body.innerHTML = '';
for(var i = 0; i < response.results.length; i++ ){
if(response.results[i].primaryGenreName === genre ){
var image = '<img src="' + response.results[i].artworkUrl100 + '">';
var image = document.createElement('img');
image.src = response.results[i].artworkUrl100;
body.appendChild(image);
body.innerHTML += '<div>' + response.results[i].trackName + '</div>' ;
}
}
}else{
body.innerHTML = 'No results found.';
}
console.log(response);
}, function (error){
console.log('No hits Found');
});
}
window.onload = function(){
//create an array with all button names
var genreNames = ['TV & Film', 'News & Politics', 'Society & Culture', 'Music', 'Hobbies'];
//loop through the array
for(var i = 0; i < genreNames.length; i++){
//create button element called "TV and Film" or whatever
var dynamicButtons = document.createElement('BUTTON');
var buttonText = document.createTextNode(genreNames);
//add it to the DOM (document)
dynamicButtons.appendChild(buttonText);
document.body.appendChild(dynamicButtons);
}
/*
for(i =0; i <= response.results.length; i++) {
for (key in response.results[i].primaryGenreName) {
if(response.results[i].primaryGenreName.hasOwnProperty(key)) {
output += '<li><button type="button">' + response.results[i].primaryGenreName + '</button></li>';
var update = document.getElementById('genres');
update.innerHTML = output;
}
}
}
*/
};

How do I add html tags in jquery plugins?

I am doing the live search using the jquery plugins. When I tried to search that doesn't exist, it only shows the table. I would like to put some message "No result found" if it doesnt exist. The question is how can I add message "No result found"
Note: In my codes I add some validation, the user need input minimum of 3 characters
/**
**options to have following keys:
**searchText: this should hold the value of search text
**searchPlaceHolder: this should hold the value of search input box placeholder
**/
(function($)
{
$.fn.tableSearch = function(options)
{
if(!$(this).is('table'))
{
return;
}
var tableObj = $(this),
searchText = (options.searchText)?options.searchText:'Search: ',
searchPlaceHolder = (options.searchPlaceHolder)?options.searchPlaceHolder:'',
divObj = $('<div style="font-size:20px;">'+searchText+'</div><br /><br />'),
inputObj = $('<input style="min-width:25%;max-width:50%;margin-left:1%" type="text" placeholder="'+searchPlaceHolder+'" />'),
caseSensitive = (options.caseSensitive===true)?true:false,
searchFieldVal = '',
pattern = '';
inputObj.off('keyup').on('keyup', function(){
searchFieldVal = $(this).val();
if(searchFieldVal.length == 0)
{
tableObj.find('tbody tr').show();
}
else if(searchFieldVal.length >= 3)
{
pattern = (caseSensitive)?RegExp(searchFieldVal):RegExp(searchFieldVal, 'i');
tableObj.find('tbody tr').hide().each(function()
{
var currentRow = $(this);
currentRow.find('td').each(function()
{
var result = "No result";
$("tbody tr").append(result);
if(pattern.test($(this).html()))
{
currentRow.show();
return false;
}
});
});
}
});
tableObj.before(divObj.append(inputObj));
return tableObj;
}
}(jQuery));
Here into JQ plugin(Posted at your question), the handler for empty result is exist. See piece of code from it.
else if(searchFieldVal.length >= 3)
{
pattern = (caseSensitive)?RegExp(searchFieldVal):RegExp(searchFieldVal, 'i');
tableObj.find('tbody tr').hide().each(function()
{
var currentRow = $(this);
currentRow.find('td').each(function()
{
var result = "No result";
$("tbody tr").append(result);
if(pattern.test($(this).html()))
{
currentRow.show();
return false;
}
});
});
}
Paraphrase you mistaken at your end. Re check it.

Knockout JS - update viewModel with AJAX Call

there are some similar questions but didn't help me to solve my issue. I can't update my results on page / view after updating my viewModel with AJAX. I am getting valid AJAX response that updates the view if I reload the page, but not when I click btnAdvancedSearch
I have simple HTML:
<div>
<input type="button" id="btnAdvancedSearch" data-bind="click: refresh" />
</div>
<div id="resultslist1" data-bind="template: { name: 'rest-template', foreach: restaurants }">
</div>
And I bind in on document load:
$(document).ready(function () {
ko.applyBindings(new RestaurantsListViewModel());
});
My viewModel is like this, and in it I call refresh that is bound with button
// Overall viewmodel for this screen, along with initial state
function RestaurantsListViewModel() {
var self = this;
self.restaurants = ko.observableArray([]);
var mappedRests = $.map($.parseJSON(sessionStorage.getItem('searchResults')), function (item) { return new Restaurant(item) });
self.restaurants = mappedRests;
self.refresh = function () {
updateRestaurantsList(); //Method executes AJAX and saves result to session.
var mappedRests2 = $.map($.parseJSON(sessionStorage.getItem('searchResults')), function (item) { return new Restaurant(item) });
self.restaurants= mappedRests2;
}
}
What am I missing here?
Thanks
I have tried waiting for AJAX to finish like this:
// Overall viewmodel for this screen, along with initial state
function RestaurantsListViewModel() {
var self = this;
self.restaurants = ko.observableArray([]);
var mappedRests = $.map($.parseJSON(sessionStorage.getItem('searchResults')), function (item) { return new Restaurant(item) });
self.restaurants = mappedRests;
self.refresh = function () {
var latitude = sessionStorage.getItem('latitude');
var longitude = sessionStorage.getItem('longitude');
var query = '{"Accepts_Reservations":"' + $('#chkReservation').is(":checked") + '","Accepts_Cards":' + $('#chkAcceptsCards').is(":checked") + '"}';
var searchResults = getRestaurantsAdvancedSearchAJAX(query, latitude, longitude, 40);
searchResults.success(function (data) {
var information = data.d;
var mappedRests2 = $.map($.parseJSON(information), function (item) { return new Restaurant(item) });
self.restaurants = mappedRests2;
});
};
};
Edit 1
Once you have declared your observable like so:
self.restaurants = ko.observableArray([]);
When you want to update restaurants you cannot do this:
self.restaurants = mappedRests2;
Instead, you need to do this:
self.restaurants(mappedRests2);
updateRestaurantsList(); //Method executes AJAX and saves result to session.
The comment after the above line indicates that this method is making an asynchronous call. Therefore, it is likely that the line after it is executing before sessionStorage has been populated. Maybe consider having updateRestaurantsList return a promise. Then you could update your code to something like this:
updateRestaurantsList().then(function() {
var mappedRests2 = $.map($.parseJSON(sessionStorage.getItem('searchResults')), function (item) { return new Restaurant(item) });
self.restaurants= mappedRests2;
});
This way the call to populate your mappedRests2 variable won't happen until after your updateRestaurantsList method has completed.
Edit 1
Be sure to never assign values to an observable using an equal sign.
// Overall viewmodel for this screen, along with initial state
function RestaurantsListViewModel() {
var self = this;
self.restaurants = ko.observableArray([]);
var mappedRests = $.map($.parseJSON(sessionStorage.getItem('searchResults')), function (item) { return new Restaurant(item) });
self.restaurants(mappedRests);
self.refresh = function () {
var latitude = sessionStorage.getItem('latitude');
var longitude = sessionStorage.getItem('longitude');
var query = '{"Accepts_Reservations":"' + $('#chkReservation').is(":checked") + '","Accepts_Cards":' + $('#chkAcceptsCards').is(":checked") + '"}';
var searchResults = getRestaurantsAdvancedSearchAJAX(query, latitude, longitude, 40);
searchResults.success(function (data) {
var information = data.d;
var mappedRests2 = $.map($.parseJSON(information), function (item) { return new Restaurant(item) });
self.restaurants(mappedRests2);
});
};
};

Resources