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.
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'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
So,I am trying to use the twitch API:
https://codepen.io/sterg/pen/yJmzrN
If you check my codepen page you'll see that each time I refresh the page the status order changes and I can't figure out why is this happening.
Here is my javascript:
$(document).ready(function(){
var ur="";
var tw=["freecodecamp","nightblue3","imaqtpie","bunnyfufuu","mushisgosu","tsm_dyrus","esl_sc2"];
var j=0;
for(var i=0;i<tw.length;i++){
ur="https://api.twitch.tv/kraken/streams/"+tw[i];
$.getJSON(ur,function(json) {
$(".tst").append(JSON.stringify(json));
$(".name").append("<li> "+tw[j]+"<p>"+""+"</p></li>");
if(json.stream==null){
$(".stat").append("<li>"+"Offline"+"</li>");
}
else{
$(".stat").append("<li>"+json.stream.game+"</li>");
}
j++;
})
}
});
$.getJSON() works asynchronously. The JSON won't be returned until the results come back. The API can return in different orders than the requests were made, so you have to handle this.
One way to do this is use the promise API, along with $.when() to bundle up all requests as one big promise, which will succeed or fail as one whole block. This also ensures that the response data is returned to your code in the expected order.
Try this:
var channelIds = ['freecodecamp', 'nightblue3', 'imaqtpie', 'bunnyfufuu', 'mushisgosu', 'tsm_dyrus', 'esl_sc2'];
$(function () {
$.when.apply(
$,
$.map(channelIds, function (channelId) {
return $.getJSON(
'https://api.twitch.tv/kraken/streams/' + encodeURIComponent(channelId)
).then(function (res) {
return {
channelId: channelId,
stream: res.stream
}
});
})
).then(function () {
console.log(arguments);
var $playersBody = $('table.players tbody');
$.each(arguments, function (index, data) {
$playersBody.append(
$('<tr>').append([
$('<td>'),
$('<td>').append(
$('<a>')
.text(data.channelId)
.attr('href', 'https://www.twitch.tv/' + encodeURIComponent(data.channelId))
),
$('<td>').text(data.stream ? data.stream.game : 'Offline')
])
)
})
})
});
https://codepen.io/anon/pen/KrOxwo
Here, I'm using $.when.apply() to use $.when with an array, rather than list of parameters. Next, I'm using $.map() to convert the array of channel IDs into an array of promises for each ID. After that, I have a simple helper function with handles the normal response (res), pulls out the relevant stream data, while attaching the channelId for use later on. (Without this, we would have to go back to the original array to get the ID. You can do this, but in my opinion, that isn't the best practice. I'd much prefer to keep the data with the response so that later refactoring is less likely to break something. This is a matter of preference.)
Next, I have a .then() handler which takes all of the data and loops through them. This data is returned as arguments to the function, so I simply use $.each() to iterate over each argument rather than having to name them out.
I made some changes in how I'm handling the HTML as well. You'll note that I'm using $.text() and $.attr() to set the dynamic values. This ensures that your HTML is valid (as you're not really using HTML for the dynamic bit at all). Otherwise, someone might have the username of <script src="somethingEvil.js"></script> and it'd run on your page. This avoids that problem entirely.
It looks like you're appending the "Display Name" in the same order every time you refresh, by using the j counter variable.
However, you're appending the "Status" as each request returns. Since these HTTP requests are asynchronous, the order in which they are appended to the document will vary each time you reload the page.
If you want the statuses to remain in the same order (matching the order of the Display Names), you'll need to store the response data from each API call as they return, and order it yourself before appending it to the body.
At first, I changed the last else condition (the one that prints out the streamed game) as $(".stat").append("<li>"+jtw[j]+": "+json.stream.game+"</li>"); - it was identical in meaning to what you tried to achieve, yet produced the same error.
There's a discrepancy in the list you've created and the data you receive. They are not directly associated.
It is a preferred way to use $(".stat").append("<li>"+json.stream._links.self+": "+json.stream.game+"</li>");, you may even get the name of the user with regex or substr in the worst case.
As long as you don't run separate loops for uploading the columns "DisplayName" and "Status", you might even be able to separate them, in case you do not desire to write them into the same line, as my example does.
Whatever way you're choosing, in the end, the problem is that the "Status" column's order of uploading is not identical to the one you're doing in "Status Name".
This code will not preserve the order, but will preserve which array entry is being processed
$(document).ready(function() {
var ur = "";
var tw = ["freecodecamp", "nightblue3", "imaqtpie", "bunnyfufuu", "mushisgosu", "tsm_dyrus", "esl_sc2"];
for (var i = 0; i < tw.length; i++) {
ur = "https://api.twitch.tv/kraken/streams/" + tw[i];
(function(j) {
$.getJSON(ur, function(json) {
$(".tst").append(JSON.stringify(json));
$(".name").append("<li> " + tw[j] + "<p>" + "" + "</p></li>");
if (json.stream == null) {
$(".stat").append("<li>" + "Offline" + "</li>");
} else {
$(".stat").append("<li>" + json.stream.game + "</li>");
}
})
}(i));
}
});
This code will preserve the order fully - the layout needs tweaking though
$(document).ready(function() {
var ur = "";
var tw = ["freecodecamp", "nightblue3", "imaqtpie", "bunnyfufuu", "mushisgosu", "tsm_dyrus", "esl_sc2"];
for (var i = 0; i < tw.length; i++) {
ur = "https://api.twitch.tv/kraken/streams/" + tw[i];
(function(j) {
var name = $(".name").append("<li> " + tw[j] + "<p>" + "" + "</p></li>");
var stat = $(".stat").append("<li></li>")[0].lastElementChild;
console.log(stat);
$.getJSON(ur, function(json) {
$(".tst").append(JSON.stringify(json));
if (json.stream == null) {
$(stat).text("Offline");
} else {
$(stat).text(json.stream.game);
}
}).then(function(e) {
console.log(e);
}, function(e) {
console.error(e);
});
}(i));
}
});
I know this similar question is out there, but I haven't really been able to take much from the answers found from them. I'm not trying to be lazy here and not do my research first, because I have!
I'm using Protractor/Jasmine in JavaScript.
I have a method stored in a page object file:
this.verifyUserExists = function (username){
driver.findElements(by.css('.ui-datatable-data > tr > td:nth-of-type(1)')).then(function (elements) {
for (i = 0; i < elements.length; i++) {
(function (index) {
elements[index].getText().then(function (users) {
if (users.trim().toLowerCase() == username.trim().toLowerCase()) {
console.log('Username: ' + users.trim() + ' found in the list.');
expect(true).toBeTruthy();
}
}).catch(function (err) { });
})(i);
}
}).catch(function (err) { });
}
And in my main test I would do obj.VerifyUserExists('anon123');
As seen, the method accepts a string username and searches for it via a list of usernames in a table. The code works, if the user exists, and the test passes. However, if the user DOES NOT exist and is not the last item in the list, it falsely fails.
For example:
if (users.trim().toLowerCase() == username.trim().toLowerCase()) {
console.log('Username: ' + users.trim() + ' found in the list.');
expect(true).toBeTruthy();
}
else {
//expect to fail
}
With the above - if we have 5 usernames, 4 of them will falsely fail.
I tried adding a boolean flag, but it comes back undefined everywhere. So my question is how would I do something like the following:
if(user == username){
//true
//exit
}
else if (end of list && name not found)
{
//false
//exit
}
Instead of looping through all the elements you can directly get the text of all elements and use expect().toContain() matcher to check if the required value exists in the locator. Look at below example.
var displayedUserNames = element.all(by.css('.ui-datatable-data > tr > td:nth-of-type(1)')).getText();
//above line will get you all available username as Array.
expect(displayedUserNames).toContain(actualUserNametobeDisplayed)
I am building a cross-platform app using AngularJS, Monaca and OnsenUI.
I have implemented a SQLite database to save data to be able to work offline. My implementation is based on the answer found HERE
I have a view where the user can select any number of options and those option values should then be saved to the SQLite database. Each option value can be saved to the same database table or separate tables - depending on the option value selected.
With this I am trying to refactor my insert statement to make it more efficient as it may be called many times. In my app.js controller I have a function that checks which option value was selected in the view, and then calls the goInsert() function that inserts the selected option value into the SQLite Database. Below is a sample of my function.
$scope.selectedIDOne = ""; // Variable to hold selected options value
$scope.changedValue = function (item, identifier) // item = selected option value; identifier = identifies table name to insert into
{
switch (identifier)
{
case "Square":
$scope.selectedIDOne = item;
db.transaction(goInsert(identifier), errorCB, successCB); // Error on refactored goInsert(identifier) function trying to pass identifier
break;
default:
// TODO
}
}
Then I try the following in my goInsert() function.
function goInsert(identifier) // Error here
{
switch (identifier)
{
case "Square":
db.transaction(insertSquareDB, errorCB, successCB);
break;
}
}
function insertSquareDB(tx)
{
tx.executeSql('INSERT OR IGNORE INTO tb_square (square_id) VALUES ("' + $scope.selectedIDOne + '" )');
}
When I run the code I get an error where indicated but the value is nonetheless inserted int the database. The error is thrown at the goInsert(identifier) function call. The error is:
TypeError: Failed to execute 'Transaction' on 'Database'. The callback provided as parameter 1 is not a function.
How can I implement this solution please? Or is there a better way? I would also ideally not like to create multiple insertSquareDB(tx) functions e.g. insertCircleDB(tx), insertROundDB(tx) etc. Is there a way I can have 1 function defined that inserts values dynamically e.g. (hypothetical)
function insertSquareDB(tx, tableName, columnName, optionValues)
{
tx.executeSql('INSERT OR IGNORE INTO tableName (columnName) VALUES ("' + optionValues + '" )');
}
You don't need to wrap your goInsert(identifier) call inside a transaction:
switch (identifier)
{
case "Square":
$scope.selectedIDOne = item;
goInsert(identifier);
...
}
If you want to be able to call one function to insert any shape into the database, your best bet is to dynamically generate the SQL statement:
function insertShapeDB(shape, value)
{
var tableName = '';
var columnName = '';
if (shape === 'Square') {
tableName = 'tb_square';
columnName = 'square_id';
}
else if (shape === 'Circle') {
tableName = 'tb_circle';
columnName = 'circle_id';
}
else if (shape === 'Round') {
tableName = 'tb_round';
columnName = 'round_id';
}
var sql = 'INSERT OR IGNORE INTO ' + tableName + ' (' + columnName + ') VALUES (?)';
db.transaction(function(tx) {
tx.executeSql(sql, [value]);
});
}