(JavaScript API 1.3 for Office) Custom Properties GetItemOrNull - javascript

I've created this a couple days ago in which i needed help regarding how to add custom properties to a said document.
First of all, I'm running Word 1701(7766.2047).
Let's say I have a method In which I return a said custom property. First I'd check if the custom property has been created already. I would do this with a simple getItemOrNullObject(key) and..
If returns null Then simply create it AND return it
Else return it
It is of my understanding that I need to do a return context.sync().then for the object get actually loaded with data? Am I doing too much return context.sync() calls for nothing?
Word.run(function(context) {
var customDocProps = context.document.properties.customProperties;
context.load(customDocProps);
return context.sync()
.then(function() {
var temp = customDocProps.getItemOrNullObject("X");
return context.sync()
.then(function() {
if (!temp) {
context.document.properties.customProperties.add("X", 1234);
temp = customDocProps.getItemOrNullObject("X");
return context.sync()
.then(function() {
return temp;
});
} else {
return temp;
}
});
});
});
The following code throws me an 'ReferenceError: 'Word' is undefined' at start but if I debug it it runs before it breaks
var customDocProps = context.document.properties.customProperties;
context.load(customDocProps);
return context.sync().{....}
Also have one more question. Say I want to update my custom property, would :
Word.run(function (context) {
context.document.properties.customProperties.add("X", 56789);
return context.sync();
});
override the old value with the new one?
If you read this far thank you! Any help is appreciated.
Cheers!

Thanks for asking this question.
Your code is correct except for one minor detail: All the *getItemOrNullObject methods do NOT return a JavaScript null, so your "if (!temp)" statement will not work as you expect. If you want to validate existence you need to call if(temp.isNullObject) instead.
Also a couple of suggestions:
customProperties.add() semantics is that if the property does exist, it will be replaced. So, If you want to create or change the property you don't need to check if it exists or not. If you want to read its current value you do. This answers your 2nd question.
I have a simplified and more efficient proposal for your code, if you are interested on loading a single property.
Word.run(function (context) {
var myProperty = context.document.properties.customProperties.getItemOrNullObject("X");
context.load(myProperty);
return context.sync()
.then(function () {
if (myProperty.isNullObject) {
//this means the Custom Property does not exist....
context.document.properties.customProperties.add("X", 1234);
console.log("Property Created");
return context.sync();
}
else
console.log("The property already exists, value:" + myProperty.value);
})
})
.catch(function (e) {
console.log(e.message);
})
We will update the documentation as this seems to be confusing.
Thanks!

I use these function to get or set custom properties
// sets a custom property on the current Word file
function setDocProperty (propName, propValue, callback) {
Word.run(context => {
context.document.properties.customProperties.add(propName, propValue)
return context.sync()
.then(() => {
callback(null)
})
.catch(e => {
callback(new Error(e))
})
})
}
// gets a custom property from the current Word file
function getDocProperty (propName, callback) {
Word.run(context => {
var customDocProps = context.document.properties.customProperties
// first, load custom properties object
context.load(customDocProps)
return context.sync()
.then(function () {
// now load actual property
var filenameProp = customDocProps.getItemOrNullObject(propName)
context.load(filenameProp)
return context.sync()
.then(() => {
callback(null, filenameProp.value)
})
.catch(err => {
callback(new Error(err))
})
})
.catch(err => {
callback(new Error(err))
})
})
}
You use them like this:
setDocProperty('docId', 28, () => {
console.log('property set')
})
getDocProperty('docId', (err, value) => {
if (err) {
console.log('Error getting property', err)
} else {
console.log('the property is ' + value)
}
})

Related

Adapting a function that isnt chainable to return a value

I am trying to get all the pages of a pdf in one object using the pdfreader package. The function originally returns each page (as its own object) when it processes it. My goal is to write a wrapper that returns all pages as an array of page objects. Can someone explain why this didn't work?
I tried:
adding .then and a return condition - because I expected the parseFileItems method to return a value:
let pages = [];
new pdfreader.PdfReader()
.parseFileItems(pp, function(err, item) {
{
if (!item) {
return pages;
} else if (item.page) {
pages.push(lines);
rows = {};
} else if (item && item.text) {
// accumulate text items into rows object, per line
(rows[item.y] = rows[item.y] || []).push(item.text);
}
}
})
.then(() => {
console.log("done" + pages.length);
});
and got the error
TypeError: Cannot read property 'then' of undefined
The function I'm modifying (From the package documentation):
var pdfreader = require("pdfreader");
var rows = {}; // indexed by y-position
function printRows() {
Object.keys(rows) // => array of y-positions (type: float)
.sort((y1, y2) => parseFloat(y1) - parseFloat(y2)) // sort float positions
.forEach(y => console.log((rows[y] || []).join("")));
}
new pdfreader.PdfReader().parseFileItems("CV_ErhanYasar.pdf", function(
err,
item
) {
if (!item || item.page) {
// end of file, or page
printRows();
console.log("PAGE:", item.page);
rows = {}; // clear rows for next page
} else if (item.text) {
// accumulate text items into rows object, per line
(rows[item.y] = rows[item.y] || []).push(item.text);
}
});
There seem to be several issues/misconceptions at once here. Let's try to look at them once at a time.
Firstly, you seem to have thought that the outer function will return ("pass on") your callback's return value
This is not the case as you can see in the library source.
Also, it wouldn't even make sense, because the callback called once for each item. So, with 10 items, it will be invoked 10 times, and then how would parseFileItems know which of the 10 return values of your callback to pass to the outside?
It doesn't matter what you return from the callback function, as the parseFileItems function simply ignores it. Furthermore, the parseFileItems function itself doesn't return anything either. So, the result of new pdfreader.parseFileItems(...) will always evaluate to undefined (and undefined obviously has no property then).
Secondly, you seem to have thought that .then is some sort of universal chaining method for function calls.
In fact, .then is a way to chain promises, or to react on the fulfillment of a promise. In this case, there are no promises anywhere, and in particular parseFileItems doesn't returns a promise (it returns undefined as described above), so you cannot call .then on its result.
According to the docs, you are supposed to react on errors and the end of the stream yourself. So, your code would work like this:
let pages = [];
new pdfreader.PdfReader()
.parseFileItems(pp, function(err, item) {
{
if (!item) {
// ****** Here we are done! ******
console.log("done" + pages.length) // The code that was in the `then` goes here instead
} else if (item.page) {
pages.push(lines);
rows = {};
} else if (item && item.text) {
// accumulate text items into rows object, per line
(rows[item.y] = rows[item.y] || []).push(item.text);
}
}
})
However, I agree that it'd be nicer to have a promise wrapper so that you won't have to stuff all the following code inside the callback's if (!item) branch. You could achieve that like this, using new Promise:
const promisifiedParseFileItems = (pp, itemHandler) => new Promise((resolve, reject) => {
new pdfreader.PdfReader().parseFileItems(pp, (err, item) => {
if (err) {
reject(err)
} else if (!item) {
resolve()
} else {
itemHandler(item)
}
})
})
let pages = []
promisifiedParseFileItems(pp, item => {
if (item.page) {
pages.push(lines)
rows = {}
} else if (item && item.text) {
// accumulate text items into rows object, per line
(rows[item.y] = rows[item.y] || []).push(item.text)
}
}).then(() => {
console.log("done", pages.length)
}, e => {
console.error("error", e)
})
Note: You would get even nicer code with async generators but that is too much to explain here now, because the conversion from a callback to an async generator is less trivial than you may think.
If you want to chain a then, you need the callback function to return a Promise :
new pdfreader.PdfReader()
.parseFileItems(pp, function (err, item) {
return new Promise( (resolve, reject) => {
let pages = ...
// do stuff
resolve(pages);
}
})
.then( pages => {
console.log("done" + pages.length);
});

Exit from function after resolving protractor promise

I am fairly new to JavaScript and Protractor. I have simple task in my test that I am unable to complete.
Check on available Tab on web page.
Check if element is visible on web page.
a) If Yes, return deffered.fullfil(true)
b) If No,
- Click on In-Progress Tab
- Click on Available Tab.
- Go to Step 1.
I am trying to do this recursively and below is my code. It is printing Element found but never exits the function after that and times out.
var check_availability = function(counter, totalCount, element){
var deferred = potractor.promise.defer()
if(counter <= totalCount){
browser.wait(function(){
browser.wait(EC.visibilityOf(element),2000)
return element
}).then(function(success){
console.log('Element found.')
return deferred.fulfill(true)
}, function(err){
inprogressTab.click()
.then(() => availableTab .click())
.then(() => check_availability (counter+1 , totalCount, element))
})
} else{
return deferred.reject(false)
}
return deferred.promise
}
PS: This is a sample code that I am using, corrected some spelling mistakes and syntax.
I see several syntax errors on your shared code. Below i tried to fix those errors and provided the expected behavior but still don't know from where the inprogressTab is coming from.
const check_availibility = function(counter, totalCount, element) {
const deferred = protractor.promise.defer();
if (counter <= totalCount) {
browser
.wait(() => browser.wait(EC.visibilityOf(element), 2000))
.then(
element => {
console.log("Element found.");
return deferred.fulfill(true);
},
err => {
inprogressTab
.click()
.then(() => availableTab.click())
.then(() => check_availibility(counter + 1, totalCount, element));
}
);
} else {
return deferred.reject(false);
}
return deferred.promise;
};
First, this code has spelling mistakes. "deferred" is with one "f", and so the call promise.deffer() will give a runtime error. It should be .defer(). Also rpotractor is misspelled. Your code could not even run.
Secondly, you are using an anitpattern: there is no need to create a promise/deferred, when you already get a promise object from browser.wait. Just return that one (or one returned from a then chain).
Also, return element is a wrong return value. It executes synchronously, so before the waiting is over, and the then chain will kick in too soon. Instead, make sure to return the promise that browser.wait returns.
You could do something like this:
var check_availibility = function(counter, totalCount, element){
if (counter <= totalCount) {
return browser.wait(function () {
return browser.wait(EC.visibilityOf(element), 2000)
// ^^^^^^
}).then(function () {
console.log('Element found.')
return true;
}).catch(function (err) {
return inprogressTab.click()
// ^^^^^^
.then(() => availableTab.click())
.then(() => check_availibility(counter+1 , totalCount, element))
})
} else {
return protractor.promise.rejected(false);
}
}
Note that JavaScript has native Promise support since EcmaScript2015, so instead of using protractor.promise, you could just use Promise.

Why can't I use then on a resolved promise?

I have my function whose job is to go over a number of files (that use the values from the array as building blocks for file names) and download them using a reduce. It's more of a hack as of now but the Promise logic should work. Except it doesn.t
Here's my code:
function import_demo_files(data) {
/**
* Make a local copy of the passed data.
*/
let request_data = $.extend({}, data);
const get_number_of_files_1 = Promise.resolve({
'data' : {
'number_of_files' : 2
}
});
return new Promise((resolve, reject) => {
let import_files = get_number_of_files_1.then(function(response) {
new Array(response.data.number_of_files).fill(request_data.step_name).reduce((previous_promise, next_step_identifier) => {
let file_counter = 1;
return previous_promise.then((response) => {
if( response !== undefined ) {
if('finished_import' in response.data && response.data.finished_import === true || response.success === false) {
return import_files;
}
}
const recursively_install_step_file = () => import_demo_file({
demo_handle: request_data.demo_handle,
'step_name': request_data.step_name,
'file_counter': file_counter
}).call().then(function(response) {
file_counter++;
if('file_counter' in response.data && 'needs_resume' in response.data) {
if(response.data.needs_resume === true) {
file_counter = response.data.file_counter;
}
}
return response.data.keep_importing_more_files === true ? recursively_install_step_file() : response
});
return recursively_install_step_file();
}).catch(function(error) {
reject(error);
});
}, Promise.resolve())
}).catch(function(error) {
reject(error);
});
resolve(import_files);
});
}
Now, when I do:
const import_call = import_demo_files({ 'demo_handle' : 'demo-2', 'step_name' : 'post' });
console.log(import_call);
The console.log gives me back that import_call is, in fact a promise and it's resolved. I very much like the way return allows me to bail out of a promise-chain, but I have no idea how to properly resolve my promise chain in there, so clearly, it's marked as resolved when it isn't.
I would like to do import_call.then(... but that doesn't work as of now, it executes this code in here before it's actually done because of the improper handling in import_demo_files.
An asynchronous recursion inside a reduction isn't the simplest of things to cut your teeth on, and it's not immediately obvious why you would want to given that each iteration of the recursion is identical to every other iteration.
The reduce/recurse pattern is simpler to understand with the following pulled out, as outer members :
1. the `recursively_install_step_file()` function
1. the `new Array(...).fill(...)`, as `starterArray`
1. the object passed repeatedly to `import_demo_file()`, as `importOptions`)
This approach obviates the need for the variable file_counter, since importOptions.file_counter can be updated directly.
function import_demo_files(data) {
// outer members
let request_data = $.extend({}, data);
const importOptions = {
'demo_handle': request_data.demo_handle,
'step_name': request_data.step_name,
'file_counter': 1
};
const starterArray = new Array(2).fill(request_data.step_name);
function recursively_install_step_file() {
return import_demo_file(importOptions).then((res) => {
if('file_counter' in res.data && 'needs_resume' in res.data && res.data.needs_resume) {
importOptions.file_counter = res.data.file_counter; // should = be += ?
} else {
importOptions.file_counter++;
}
return res.data.keep_importing_more_files ? recursively_install_step_file() : res;
});
}
// the reduce/recurse pattern
return starterArray.reduce((previous_promise, next_step_identifier) => { // next_step_identifier is not used?
let importOptions.file_counter = 1; // reset to 1 at each stage of the reduction?
return previous_promise.then(response => {
if(response && ('finished_import' in response.data && response.data.finished_import || !response.success)) {
return response;
} else {
return recursively_install_step_file(); // execution will drop through to here on first iteration of the reduction
}
});
}, Promise.resolve());
}
May not be 100% correct but the overall pattern should be about right. Be prepared to work on it some.

mongoose pre hook findOneAndUpdate modify some field getting from another collection

I'm trying to modify a document in findOneAndUpdate pre hook, code given below
userSchema.pre('findOneAndUpdate', function (next) {
// this._update.$set
//var user = new User(this._update.$set)
var promiseArray = []
promiseArray.push(// Fetching some data from other collection)
Promise.all(promiseArray).then(function (values) {
if (values[0].length === 0) {
return next('Data not found')
} else {
this._update.$set.salary = values.salary
return next()
}
}).catch(function (err) {
next(err)
})
})
I'm getting error
TypeError: Cannot read property '$set' of undefined
I know why i'm getting this error because i'm accessing "this" keyword inside promise, "this" inside promise is different than "this" after pre method
How to solve this problem, I have tried to solve this problem by assigning
this._update.$set to different value shown example in commented code but after saving it's not modifying document we need to only change this._update.$set.salaray value. Any help appreciated
You can use .bind()
Promise.all(promiseArray).then(function (values) {
if (values[0].length === 0) {
return next('Data not found')
} else {
this._update.$set.salary = values.salary
return next()
}
}.bind(this)).catch(function (err) {
next(err)
})
})

Simple Check Function with node.js, pg-promise

I am trying to write a simple check() function following this example. The function successfully prints out "True!" but the return value of the function is undefined when I do console.log(submitModels.verifyOrganizationString(formInputs.organizationString));. How can I get the function to return true and false?
Right now the function only returns "Invalid organization string" even when the string is valid.
I am thinking this result has to do with the pg-promise library, but am not sure.
In the file submit.js, I have:
function verifyOrganizationString(organizationString) {
db.one("select * from organizations where organization_string=$1", [organizationString])
.then(data => {
console.log("True!");
return true;
})
.catch(error => {
console.log("False!");
return false;
});
}
module.exports = {
verifyOrganizationString,
};
In another file, I have
const submitModels = require('../models/submit.js');
function proccessSubmission(req, res) {
var formInputs = req.body;
console.log(submitModels.verifyOrganizationString(formInputs.organizationString));
if (submitModels.verifyOrganizationString(formInputs.organizationString)) {
submitModels.insertIntoDatabase(formInputs);
res.redirect('/');
} else {
res.send("Invalid organization string");
}
}
Your problem is due to both invalid use of promises and bad use of pg-promise.
If you want the function to return true/false depending on whether a record is found, change the function to this:
function verifyOrganizationString(organizationString) {
return db.oneOrNone("select * from organizations where organization_string = $1",
organizationString, a => !!a);
}
Then you can use it as one would a regular promise:
verifyOrganizationString('bla-bla')
.then(data => {
// data = true/false
})
.catch(error => {
// error
});
See also: SELECT ⇒ INSERT examples.
The reason you're getting undefined is because 'verifyOrganizationString' doesn't actually return anything. You need to return the promise created by your db.one chain.
Although the method will still not return true or false in that case but the Boolean wrapped in another promise so that you can chain the result of your verify function the same way you did with the result of db.one.

Categories