Jun 14 2017 04:25 PM
I have had great success debugging SPFx web parts using the Chrome extension. Is there a way to get this to work with the framework extensions? I'm trying to build an Application Customizer that builds a menu in the header based on the contents of a SP links list ('OrangeLinks') and having a heck of time figuring out why I can't get it to work.
The idea is pretty simple: in the onRender() event I am assigning some html to the _headerPlaceholder.domElement.innerHTML, then calling a method to get the links out of the list which in turn calls a method to use the returned data to construct the html for the header. When all the data has been turned into HTML links, I am assigning that to the <div> that has already been put in the _headerPlaceholder. Here's the code:
public onRender(): void { if (!this._headerPlaceholder) { this._headerPlaceholder = this.context.placeholders.tryAttach( 'PageHeader', { onDispose: this._onDispose }); // The extension should not assume that the expected placeholder is available. if (!this._headerPlaceholder) { console.error('The expected placeholder was not found.'); return; } if (this.properties) { let headerString: string = this.properties.Header; if (!headerString) { headerString = '(Header property was not defined.)'; } if (this._headerPlaceholder.domElement) { this._headerPlaceholder.domElement.innerHTML = ` <div class="${styles.app}"> <div class="ms-bgColor-orange ms-fontColor-white ${styles.header}"> <div id="orangeLinks"></div> </div> </div>`; } this.getOrangeLinks(); } } private getOrangeLinks(): void { pnp.sp.web.lists.getByTitle('OrangeLinks').items.select('URL').get().then((links: any) => { this.buildOrangeLinks(links); }); } private buildOrangeLinks(links: any): void { let linkList: string = ``; for (let i: number = 0; i < links.length; i++) { //--- if there are already some links, add spaces before this one if (i > 0) { linkList += ` | `; } let link = `<a href="${escape(links[i].URL.Url)}" target="_blank">${escape(links[i].URL.Description)}</a>`; linkList += link; } $('#orangeLinks').html(linkList); return; }
All I get is blank header.
Funny enough, this scenario (render()->pnp data function->html building function & assignment by jQuery) works just fine in a SPFx web part, so something about the scope or timing of execution must be different. Any thoughts would be most welcome.
Jun 15 2017 08:04 AM
Hi @Joseph Ackerman,
I suspect that
pnp.sp.web.lists.getByTitle('OrangeLinks').items.select('URL').get().then((links: any) => {
... }
isn't returning any links.
Can you add console.log("started") at the beginning of the getOrangeLinks method to make sure that the method is called at all.
then can you add a console.log(links) to make sure that we get any results returned?
Jun 15 2017 10:52 AM
Hello @Pieter Veenstra, Thanks for the reply.
It does indeed seem that no links are being returned but it turns out not to be what I thought. I added the console log call as you suggested like so:
But when I went to the console tab of the inspector window in my browser, I noticed this:
For some reason it seems that the translation from the PnP call to the REST call is adding the SitePages folder to the path; it's now obvious why the call is not returning any items! If I remove the offending folder from the path and just plug the result into the address bar of the browser, you can see that I am returning the items expected (in the default atom format, but it proves that the list is there and will return rows if the call is properly constructed):
So my question now is: why is the PnP.js not making the correct REST call? Have I missed a step somewhere? Am I making a faulty assumption that pnp.sp.web represents the context of the current default web site? Or is it something else?
Thanks for your input, btw. I've been doing traditional SP Development for 10 years and am still climbing the learning curve on the new "remote" platform.
Jun 15 2017 01:27 PM
Jun 15 2017 05:07 PM
>>Can you try without the select. You should get all items coming back. <<
The select is not the problem, it's not filtering any rows, only ensuring that I am only bringing back the one column I am actually interested in. If you look at the ATOM formatted results above, you'll see that I am returning 3 items, but only the URL field for each item. There are only 3 items in the list.
When I do as you suggest by removing the select, I am still seeing the incorrectly formed REST call:
I still only get back the same 3 items, I just get a whole lot more fields:
So, no, the select is not the issue...still trying figure out why the PnP.js is malforming the REST call...
Jun 16 2017 01:06 AM
It looks indeed that the REST Url isn't correct.
In my code I tried this:
pnp.sp.web.lists.getByTitle('MyList').items.get().then((items: any) => { return items; });
and the above worked as expected
The below call roughly does the same thing:
return this.context.spHttpClient.get(this.context.pageContext.web.absoluteUrl + `/_api/web/lists/GetByTitle('MyList')/items`, SPHttpClient.configurations.v1) .then((response: SPHttpClientResponse) => { return response.json(); });
Can you double check that you have the latest version of the pnp modules?
I can't see much of a difference between your code and my code.
Jun 16 2017 02:05 PM
Ran "npm ls" and this was the result:
Should be the latest because I only built this project this week! 😉
Jun 19 2017 12:06 PM
Solution
Apparently there are some additional instuctions required for the PnP JS and SPFx to play nicely with one another. Thanks to @Patrick Rodgers for pointing me to the missing key described in Using sp pnp js in SharePoint Framework - Establish Context.
A single call in the onInit() method override does the trick:
@override public onInit(): Promise<void> { return super.onInit().then(_ => { //--- ensure that the current web is used for all pnp calls pnp.setup({ spfxContext: this.context }); }); }
The REST call is now forming properly. It sure would be nice to have complete, organized documentation all together in ONE PLACE. 🙂
Thanks again for your interest and assistance.
Jun 19 2017 12:06 PM
Solution
Apparently there are some additional instuctions required for the PnP JS and SPFx to play nicely with one another. Thanks to @Patrick Rodgers for pointing me to the missing key described in Using sp pnp js in SharePoint Framework - Establish Context.
A single call in the onInit() method override does the trick:
@override public onInit(): Promise<void> { return super.onInit().then(_ => { //--- ensure that the current web is used for all pnp calls pnp.setup({ spfxContext: this.context }); }); }
The REST call is now forming properly. It sure would be nice to have complete, organized documentation all together in ONE PLACE. 🙂
Thanks again for your interest and assistance.