I'm trying to migrate my tiny report from V3 to V4, but I have found an issue which is annoying me and making me feel like I'm totally dumb.
So I just took the example code at https://developers.google.com/analytics/devguides/reporting/core/v4/quickstart/web-js
and change a couple of things, and it works, it runs the report. But when I try to retrieve the data from the different rows with the below function:
function displayResults(response) {
var Objeto = response.result["reports"];
var Filas01 = Objeto["data"];
console.log(Objeto);
console.log(Filas01);
}
Objeto shows everything within .reports
But Filas01 shows undefined, I have tried to retrieve reponse.results.reports.data.rows;
And several variations but it says undefined all the time,
I have no clue why it was working on V3 and is not on V4,
Please any help would be much appreciated :)
There are samples of how to make requests and precess the response in various languages. But specifically here is a simple JavaScript function which processes the results into a table:
function handleReportingResults(response) {
if (!response.code) {
outputToPage('Query Success');
for( var i = 0, report; report = response.reports[ i ]; ++i )
{
output.push('<h3>All Rows Of Data</h3>');
if (report.data.rows && report.data.rows.length) {
var table = ['<table>'];
// Put headers in table.
table.push('<tr><th>', report.columnHeader.dimensions.join('</th><th>'), '</th>');
table.push('<th>Date range #</th>');
for (var i=0, header; header = report.columnHeader.metricHeader.metricHeaderEntries[i]; ++i) {
table.push('<th>', header.name, '</th>');
}
table.push('</tr>');
// Put cells in table.
for (var rowIndex=0, row; row = report.data.rows[rowIndex]; ++rowIndex) {
for(var dateRangeIndex=0, dateRange; dateRange = row.metrics[dateRangeIndex]; ++dateRangeIndex) {
// Put dimension values
table.push('<tr><td>', row.dimensions.join('</td><td>'), '</td>');
// Put metric values for the current date range
table.push('<td>', dateRangeIndex, '</td><td>', dateRange.values.join('</td><td>'), '</td></tr>');
}
}
table.push('</table>');
output.push(table.join(''));
} else {
output.push('<p>No rows found.</p>');
}
}
outputToPage(output.join(''));
} else {
outputToPage('There was an error: ' + response.message);
}
}
I would also recommend taking the time to review the overal structure of the response in the reference docs
Related
Using the Office JavaScript API, I am trying to fill a selected table using the following code:
Word.run(function (context) {
var table = context.document.getSelection().parentTable;
context.load(table);
return context.sync()
.then(function () {
if (table.isNullObject == true || !table) {
console.log("selection ist not table");
errorHandler("selection is not a table");
} else {
// loop over table
for (var row = 0; row < table.values.length; row++) {
for (var column = 0; column < table.values[row].length; column++) {
console.log(table.values[row][column]);
table.values[row][column] = "Test " + row + " " + column;
}
context.sync().then(function () {
console.log("done");
}).catch(function (e) {
console.log(e);
});
}
}
});
});
The scripts runs fine, the table object exists, the values are logged and the final "done" too. But the table stays as it is - no update of the values. What am I missing?
A few comments about the code you've posted:
Checking for isNullObject on the table object like your code is doing is not effective, because the lines above that will have already thrown an error if the parentTable doesn't exist.
Error handling (catch statement) should be located immediately after the Word.run -- that way it'll catch any error that occurs inside the Word.run.
Instead of loading the entire table object like you are, you should only load the properties that you need (in this case, values and rows/items/cells/items/body).
To set a cell value, use the insertText method on the body of the cell that you want to update.
The following code sample incorporates this feedback, and should successfully update the values in your table.
Word.run(function (context) {
var table = context.document.getSelection().parentTable;
table.load("values, rows/items/cells/items/body");
return context.sync()
.then(function () {
for (var row = 0; row < table.values.length; row++) {
for (var column = 0; column < table.values[row].length; column++) {
console.log(table.values[row][column]);
table.rows.items[row].cells.items[column].body.insertText("Test " + row + " " + column, "Replace");
}
}
return context.sync()
.then (function() {
console.log("Done");
});
});
}).catch(function (e) {
console.log(e);
});
Great question #BernhardWebstudio! To explain why your specific code doesn't work is because the add-in code is invoked in a separate process from the Office Application and it must make 'requests' to actually acquire the data. This is similar to asking for attributes within a REST or OData call. (e.g. $select)
What does this mean for you? Well with a just slight alteration to your code you just need to request what properties of the table you want to load. Since you only care about the values of the table, it would be easy to do this with this type of code. If you notice I also have a commented out line that is table.load("[Property Name]"); to use the Office Apps Object model as proxy objects as well. Kim has pointed out some other really good suggestions as well. Cheers and Happy Coding!
Word.run(function (context) {
let table = context.document.getSelection().parentTable;
//table.load("values");
context.load(table, "values");
//...
I have a ribbon button command which executes a javascript function and passes in the selected rows in a grid. I am looping through that list to create a $select filter to make a RetrieveMultiple request.
The problem is everytime I get the following error
400: Bad Request: No Property 'id' exists in type 'Microsoft.Xrm.Sdk.Entity' at position 1
I have tried with id instead of Id but I still get the same error.
My code is below
function approveMultipleApplications(selectedApplicationReferences) {
if (selectedApplicationReferences && selectedApplicationReferences.length > 0) {
var filter = '';
for (var i = 0; i < selectedApplicationReferences.length; i++) {
filter += '(id eq guid\'' + selectedApplicationReferences[i].Id + '\')';
if (i < selectedApplicationReferences.length - 1) {
filter += ' or ';
}
}
var options = "$select=new_assessmentcount,new_requiredassessmentcount&$filter=" + filter;
try {
SDK.REST.retrieveMultipleRecords("new_application", options, retrieveApplicationsCallBack, function (error) {
alert(error.message);
}, retrieveComplete);
}
catch (ex) {
Xrm.Utility.alertDialog('Something went wrong, please try again or contact your administrator ' + ex, null);
}
}
else {
Xrm.Utility.alertDialog('You must select at least one application to approve', null);
}
}
The selectedApplicationReferences[i].Id is in this format {guid-value}
Any help or guidance is appreciated
The error message is pretty much spot on: Use LogicalNameId instead of just Id. In your case that would be new_applicationId:
filter += '(new_applicationId eq guid\'' + selectedApplicationReferences[i].Id + '\')';
It can be a bit confusing since there is actually no Id-field in the database. If you use e.g. early bound classes, the Id field is set for you behind the scenes, so that might have confused you. The Id field is not returned by the OData endpoint.
I'm developing a small Chrome extension that would allow me to save some records to chrome.storage and then display them.
I've managed to make the set and get process work as I wanted (kinda), but now I'd like to add a duplicate check before saving any record, and I'm quite stuck trying to find a nice and clean solution.
That's what I came up for now:
var storage = chrome.storage.sync;
function saveRecord(record) {
var duplicate = false;
var recordName = record.name;
storage.get('records', function(data) {
var records = data.records;
console.log('im here');
for (var i = 0; i < records.length; i++) {
var Record = records[i];
if (Record.name === recordName) {
duplicate = true;
break;
} else {
console.log(record);
}
}
if (duplicate) {
console.log('this record is already there!');
} else {
arrayWithRecords.push(record);
storage.set({ bands: arrayWithRecords }, function() {
console.log('saved ' + record.name);
});
}
});
}
I'm basically iterating on the array containing the records and checking if the name property already exists. The problem is it breaks basic set and get functionality -- in fact, when saving it correctly logs 'im here' and the relative record object, but it doesn't set the value. Plus, after a while (generally after trying to list the bands with a basic storage.get function) it returns this error:
Error in response to storage.get: TypeError: Cannot read property
'name' of null
I'm guessing this is due to the async nature of the set and get and my incompetence working with it, but I can't get my head around it in order to find a better alternative. Ideas?
Thanks in advance.
I got a table with remote datasource. in one cell I got the userID. Because I want to show the username instead of the user ID I made a custom template function:
function getUserName(pmcreator){
var user = '';
var data = ''
ds_userList.fetch(function(){
var data = this.data();
for(var i = 0, length = data.length; i < length; i++){
if(data[i].uID == pmcreator){
console.log(data[i].uLastname)
user = data[i].uLastname
}
}
});
return user
}
But its not working as it should, the cells stay empty. I got no errors but I see that the remote request to fetch the usernames is not completed before the grid is filled out. I thought the custom function of fetch is waiting for the results to return but it don't seems so.
Any Idea? I find thousends of examples but all with static local data. I need one with both remote, the grid conent and the template data.
This is probably due the fact that when yuo call the dataSource.fetch it fires off an async function, which causes the thread running the template to continue on. According to kendo you will need to return a control, then set the content of that control inside the callback.
Quick sample using Northwind categories...
Here is the template function
function getDetails(e) {
$.getJSON("http://services.odata.org/V3/Northwind/Northwind.svc/Categories", null, function(data) {
var category = data.value.filter(function(item, i) {
return item.CategoryID === e.CategoryID;
});
$("#async_" + e.CategoryID).html(category[0].Description);
});
return "<div id='async_" + e.CategoryID + "'></div>";
}
http://jsbin.com/ODENUBe/2/edit
I kept getting a recursive error maximum call stack when I just tried to fetch the dataSource, so I switched to a simple getJSON, but it should work pretty much the same.
I grabbed a bit of code to do some paging with jQuery, via Luca Matteis here
Paging Through Records Using jQuery
I've made some edits to the paging script so that I can use the same code to provide paging of different content in different locations on the same site.
For the most part, I think it works, except that I get a jsonObj is undefined error in firebug.
When I use alert(jsonObj.toSource()), I am shown the variables that I am trying to populate, but at the same time, the script dies because of the error.
I can't figure out why I am getting this conflict of 'undefined' and yet I can easily out put the 'undefined' values in an alert. I can even say alert(jsonObj.name), and it will give me that value, but still launch an jsonObj is undefined error.
Here's the code I'm using
var pagedContent = {
data: null
,holder: null
,currentIndex : 0
,init: function(data, holder) {
this.data = data;
this.holder=holder;
this.show(0); // show last
}
,show: function(index) {
var jsonObj = this.data[index];
if(!jsonObj) {
return;
}
var holdSubset='';
for(i=0;i<=4; i++){
jsonObj=this.data[index+i];
this.currentIndex = index;
if(this.holder=='div#firstList'){
var returnedId = jsonObj.id;
var returnedName = jsonObj.name;
var calcScore=this.data[index+i].score/this.data[0].score*100;
var resultInput="<div ' id='"+returnedId+"'><div class='name'>"+returnedName+"</div><div class='score'><div style='width:"+calcScore+"%;'></div></div>";
}
if(this.holder=='div#secondList'){
var name=jsonObj.name;
var city=jsonObj.city;
var region=jsonObj.state;
var resultInput='<li><div>'+name+'</div<div>'+city+'</div><div>'+region+'</div></li>';
}
holdSubset= holdSubset+resultInput;
}
jQuery(this.holder).html('<br/>'+holdSubset);
if(index!=0){
var previous = jQuery("<a>").attr("href","#").click(this.previousHandler).text("< previous");
jQuery(this.holder).append(previous);
}
if(index+i<this.data.length){
var next = jQuery("<a style='float:right;'>").attr("href","#").click(this.nextHandler).text("next >");
jQuery(this.holder).append(next);
}
}
,nextHandler: function() {
pagedContent.show(pagedContent.currentIndex + 5);
return false;
}
,previousHandler: function() {
pagedContent.show(pagedContent.currentIndex - 5);
return false
}
};
I call the function like this
pagedContent.init(json.users.locations, 'div#secondList');
The json looks like this
{"locations" : [ {"id":"21319","name":"Naugatuck American Legion","city":"Ansonia","region":"Connecticut"},{"id":"26614","name":"Studio B789","city":"Acton","region":"Maine"},{"id":"26674","name":"Deering Grange Hall","city":"Bailey Island","region":"Maine"},{"id":"27554","name":"Accu Billiards","city":"Acushnet","region":"Massachusetts"}]}
I may have found the problem with your code:
for(i=0;i<=4; i++){
jsonObj=this.data[index+i];
(...)
When you call show(0) you set index to 0. You expect a fixed number of items in the array (5 in the range [0..4]) but there are only 4 locations in your data.
If you are using console.log to trace the problems in firebug you might find that it is a problem with firebug. Try just running console.log on it's own.
If it is a problem with firebug try updating it. There are some development versions around which might fix the problem.
I had a similar problem and fixed it by doing the above.