SharePoint/PnP-JS-Core

pnp core set Content type

Closed this issue · 20 comments

Dear all,
I am trrying to se up the content type of a document I am creating dinamically as indicated in this post (#783).
I am able to creare a file and update some attributes (like the title).
But I can't set the content type (the document library where I am trying to save the document can store items of several content types).

Do you have any suggestion?

Hi @bulbapeppe,

Item's ContentTypeId should be updated for changing the content type of the document.
Content type's ID can be retrieved from the document library by the name.

Example:

const listUri = '/sites/site/Shared%20Documents';
const fileUri = '/sites/site/Shared%20Documents/image.png';

(async () => {

  const list = pnp.sp.web.getList(listUri);
  // validation is omitted for simplicity
  const [{ Id: { StringValue: ContentTypeId }}] = await list.contentTypes.select('Id')
    .filter(`Name eq 'Picture'`).get(); 
  const item = await pnp.sp.web.getFileByServerRelativePath(fileUri).getItem();
  await item.update({ ContentTypeId });

  console.log('Done')

})().catch(console.error);

P.S.
Thank you for your interest in the sp-pnp-js library. We wanted to mention that this library is being depricated in July, 2018 in favor of the new scoped pnpjs libraries. You should begin transitioning your existing projects when possible, or start new projects with the new libraries. Please see the transition guide for more details on migrating and be sure to let us know if you have any questions. Thanks!

Hi,
the code works properly, thanks!
Just a question (but if I must open another question, I do it).
The file is created and updated. So, everything seems to work properly.
But in the console, I get the error attached when I try to create the document
The error says "Cannot open file" but I can find the file and update it.
Err.txt
Any idea?

Many thanks

Just an update: it seems that the code of adding the file and then updating the properties runs two times and sometimes everything works and sometimes no. I'll provide you with some clear code.
Thanks.

I have this simple code that runs once I click on a button: the code should take a file and create a copy in another doc library setting some attributes like the title.

const CurrWeb2 = new Web(this.context.pageContext.web.absoluteUrl);
const CurrWebWorking2 = new Web('https://ecpb2020-acc.esa.int/sites/working/Council/Meet1/');
const pathLib = '/sites/working/Council/Meet1/Meeting Document/';
let PathDocTemplate: string = "/sites/official/Shared Documents/TemplateReference.docx";

CurrWeb2.getFileByServerRelativeUrl(PathDocTemplate).getBuffer().then((buffer: ArrayBuffer) => {
  CurrWebWorking2.getFolderByServerRelativeUrl(pathLib).files.add("test2.docx", buffer, true)
    .then(({ file }) => file.getItem())
    .then(itemFile => itemFile.update({ Title: "titletest" }))
    .then(console.log)
    .catch(console.error);
});

The problem is that looking at the network tab I see that the add function is called two times (see attachment Network.png) instead of just once. And sometimes I get the errors detailed in the file "Err2.txt". In this case the file is only created but not updated. Sometimes I get just one error ("cannot open the file") and the file is created and updated.

I guess that these random errors are due that the calls to add the file run two times instead of one.
But I cannot understand why there are two calls and how to fix it.
Could you please help?
Err2.txt
network

An update on the issue: I have copied the code in this topic #783
Sometimes it works sometimes if gives the error attached
Any suggestion?
Error.pdf

import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { Web } from 'sp-pnp-js';

export default class FileUploadWebPart extends BaseClientSideWebPart {

public render(): void {
this.domElement.innerHTML = <div> <span>Title</span> <input type="text" id="doc-title" value="Document name"> <input type="file" id="doc-upload-file"> <input type="button" value="Upload" id="doc-upload-btn"> </div>;
document.getElementById('doc-upload-btn')
.addEventListener('click', () => this.uploadDummyDocument());
}

private uploadDummyDocument() {
const title = (document.getElementById('doc-title') as HTMLInputElement).value;
const files = (document.getElementById('doc-upload-file') as HTMLInputElement).files;
if (!title || files.length === 0) {
return console.error('Missing data');
}
const web = new Web(this.context.pageContext.web.absoluteUrl);
const payload = { Title: title };
web.getFolderByServerRelativePath('Shared Documents')
.files.add(files[0].name, files[0], true)
.then(({ file }) => file.getItem())
.then(item => item.update(payload))
.then(console.log)
.catch(console.error);
}

}

Hi @bulbapeppe -

Can you share the error in the network tab for the 500 response? There is nothing in the library that would execute the call twice (unless it is being throttled) so I think the issue might be that your event handler is firing twice.

Thank you for your interest in the sp-pnp-js library. We wanted to mention that this library is being deprecated in July, 2018 in favor of the new scoped pnpjs libraries. You should begin transitioning your existing projects when possible, or start new projects with the new libraries. Please see the transition guide for more details on migrating and be sure to let us know if you have any questions. Thanks!

Dear Patrick,
thank you for your reply. I tried to check what is firing the code more than once, but without success. However, as indicated in above post, I created a new web part with only the following code (the code has been suggested in this post #783 ):

import { BaseClientSideWebPart } from '';
import { Web } from 'sp-pnp-js';

export default class FileUploadWebPart extends BaseClientSideWebPart {

public render(): void {
this.domElement.innerHTML =

Title
;
document.getElementById('doc-upload-btn')
.addEventListener('click', () => this.uploadDummyDocument());
}

private uploadDummyDocument() {
const title = (document.getElementById('doc-title') as HTMLInputElement).value;
const files = (document.getElementById('doc-upload-file') as HTMLInputElement).files;
if (!title || files.length === 0) {
return console.error('Missing data');
}
const web = new Web(this.context.pageContext.web.absoluteUrl);
const payload = { Title: title };
web.getFolderByServerRelativePath('Shared Documents')
.files.add(files[0].name, files[0], true)
.then(({ file }) => file.getItem())
.then(item => item.update(payload))
.then(console.log)
.catch(console.error);
}

}

That code simply loads a file from the local PC and then creates an item in a document library and then updates the title of the just created doc. The issue is that the code sometimes works properly, and sometimes it runs into error. When it runs into error, the error states that "Item does not exist. It may have been deleted by another user". On the contrary the files exists in the document library. It seems like the update starts before the .add command is executed.

I attach a file with more details on the error.
Error.pdf

Do you have any idea?
Once solved this, I try to rewrite the full code to check if and why it is fired two times.

Thank you.

Pasting the same code again doesn't really help. Can you share the error that 500 response is returning? From those requests I am still guessing the method is firing twice and you are ending up somehow with the wrong id for the item. If the update started before the add was executed you should get a null exception as the item object wouldn't exist. Unless you have another variable named item somewhere that is shadowed and is causing a conflict.

Sorry, maybe I don't understand what error I need to post.
Attached now I have put some messages I get since I click in the button that triggers the "uploadDummyDocument" function:
a) the "Console.txt" file: it contains the error I get in the Console tab
b) the "Error Network.pdf" file: it contains all the items I see in the Network tab with the corresponding message.
Error Network.pdf
Console.txt
Code.txt

c) the "Code.txt" contains again the code where (with respect to the above code) I changed "web.getFolderByServerRelativePath" into "web.getFolderByServerRelativeUrl". And I changed item with itemABC.

Notes:

  • I am using pnp version 3.0.2.

My impression is that the " .files.add" command returns before it really ends and the ".update" commands runs into error as it cannot find the just created file.

Please tell me If you can see the attachmens and if you have any idea.
Thank you again for your help.

The problem (unsolved) seems to be a BUG due to the browser cache!
It is like the following issue #538.
It seems that this bug in pnp libraries is not still solved.
I am trying to find out a workaround.
As indicated above my pnp version is 3.0.2.

Do you have any suggestion?

Any reasons it's not the latest version (3.0.8)?

I am using 3.0.4 as if I update to higher version like the 3.0.8 I get the following error when I compile:
Error - typescript - node_modules\sp-pnp-js\lib\sharepoint\clientsidepages.d.ts(104,44): error TS1005: ',' expected.
Error - typescript - node_modules\sp-pnp-js\lib\sharepoint\clientsidepages.d.ts(110,40): error TS1005: ',' expected.
Error - typescript - src\webparts\mauTest\MauTestWebPart.ts(72,3): error TS1068: Unexpected token. A constructor, method, accessor, or property was expected.
Error - typescript - src\webparts\mauTest\MauTestWebPart.ts(141,0): error TS1128: Declaration or statement expected.

SPFx for On-Prem?

yes.
I am developing an app for a Sharepoint 2016 on premises. And I select the corresponding option when creating the initial project with yo @microsoft/sharepoint.

If you need other information (e.g. version...), please tell me know.
Thanks for any suggestion you can provide me.
In addition: Is the cache problem solved if I install pnp 3.0.8?

Could you try to update TypeScript in your SPFx solution like mentioned in this workaround and try 3.0.8.

Is the cache problem solved if I install pnp 3.0.8?

Not sure. But anyway, changes will be unlikely applied for an old version. I have not experienced the issue that you described, so it would be great to know if the issue was fixed after the version that you stuck with because of TypeScript compatibility.

If you read the thread of the issue you linked, #538, you will see it is not a bug with the library. As explained there, we do not do anything with the browser cache - on purpose. A workaround to that specific issue was also provided.

If I run the code below it works as expected. I am just executing the code you pasted and I have included the part where I read the file in node just to be complete. The file is uploaded and the title is then updated as well.

const fs = require("fs"),
    path = require("path"),
    filePath = path.join(__dirname, "../../debug/alm.ts");

fs.readFile(filePath, { encoding: "utf-8" }, function (err, data) {
    if (!err) {

        callCode(data);

    } else {
        console.log(err);
    }
});

function callCode(data: any) {

    const web = new Web("https://{tenant}.sharepoint.com/sites/dev/");
    const payload = { Title: "new title" };
    web.getFolderByServerRelativePath("/sites/dev/Shared Documents")
        .files.add(`${Util.getRandomString(6)}.txt`, data, true)
        .then(({ file }) => file.getItem())
        .then(item => item.update(payload))
        .then(console.log)
        .catch(console.error);
}

Is it possible that the button click code is somehow being executed twice? We are trying to help but this doesn't appear to be an issue with the library as best I can tell. Always happy to be wrong and want of course to fix any bugs.

@koltyakov I have the typescrypt version only in the "npm-shrinkwrap.json" file. It was already "version": "2.4.2". However, I did what indicated in the post. Command "gulp bundle --ship" still runs into error.

@patrick-rodgers I am sorry, but I didn't understand: if I use your code I get errors in "__dirname" and in "Utils" and in "getFolderByServerRelativePath". I guess you are using sharepoint Online. I am using Sharepoint on premises 2016 (project created with npm and yeoman). Moreover: if I create the same filename instead of a new one (e.g. as you do with "${Util.getRandomString(6)}.txt" ) I get the error in my code (the error doesn't occur in case of a new file as the answer of the browser is not "304 not modified" as it happens when I use the same filename).

It seems that a code like the following one works properly (but I need to execute more tests). Indeed the .filter options seems that causes the browser executes the rest call every time.

private uploadDummyDocument() {
const title = (document.getElementById('doc-title') as HTMLInputElement).value;
const files = (document.getElementById('doc-upload-file') as HTMLInputElement).files;
if (!title || files.length === 0) {
return console.error('Missing data');
}
const web = new Web(this.context.pageContext.web.absoluteUrl);
const payload = { Title: title };
web.getFolderByServerRelativeUrl('Shared Documents')
.files.add(files[0].name, files[0], true)
.then((far: FileAddResult) => {

web.lists.getByTitle("Documents").items
.filter(encodeURIComponent('FileLeafRef eq '' + files[0].name + '''))
.get()
.then((itm: ISPItem[]) => {

 console.log('item id: ' + itm[0].ID);
 pnp.sp.web.lists.getByTitle("Documents").items.getById(itm[0].ID).update({
  Title: "A Title"
 })
 .then(() => {
  console.log('updated');
 })
 .catch((ex) => {
  console.log('error: ' + ex);
 });
})
.catch((ex2) => {
 console.log('error 2: ' + ex2);
});

})
.catch(console.error);
}

I'll keep you posted if I get a solution.
Any suggestion is well accepted :-D

Thanks.

The code I shared was used in nodejs - I just included it to show how I was loading the file contents. I used your code as pasted in the "callCode" method and it worked fine.

Dear All,
I confirm that the code I pasted above is the only that works for me.
Thank you.

Great, glad to hear it!