pnp / pnpjs

Fluent JavaScript API for SharePoint and Microsoft Graph REST APIs
https://pnp.github.io/pnpjs/
Other
740 stars 300 forks source link

PNP/Graph Error: No observers registered for this request. #3045

Closed lovethakker02 closed 1 month ago

lovethakker02 commented 1 month ago

What version of PnPjs library you are using

4.x

Minor Version Number

1.0

Target environment

SharePoint Framework

Additional environment details

I'm using Node version 18.18.0. I am trying to get search result from a document library using graph api. I have installed latest version of PNP/graph. I didn't get much help on set up. So I am following below video which is probably outdated.

https://www.youtube.com/watch?v=2rfsdWoR7DM

When I am trying to get result using graph.query, I am getting error - Error: No observers registered for this request.

Can someone suggest what I am doing wrong?

Question/Request

Here is my code:

ts file

import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
  type IPropertyPaneConfiguration,
  PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart, WebPartContext } from '@microsoft/sp-webpart-base';

import * as strings from 'WpsearchWebPartStrings';
import Wpsearch from './components/Wpsearch';
import { IWpsearchProps } from './components/IWpsearchProps';
import { SPFx, graphfi,DefaultHeaders } from '@pnp/graph';

export interface IWpsearchWebPartProps {
  description: string;
  spfxcontext:WebPartContext;
}

export default class WpsearchWebPart extends BaseClientSideWebPart<IWpsearchWebPartProps> {

  public render(): void {
    const element: React.ReactElement<IWpsearchProps> = React.createElement(
      Wpsearch,
      {
        description: this.properties.description,        
        spfxcontext:this.context
      }
    );
   // const graph = graphfi().using(SPFx(this.context)).using(DefaultHeaders());
    ReactDom.render(element, this.domElement);
  }

  protected onInit(): Promise<void> {
    const graph = graphfi().using(SPFx(this.context)).using(DefaultHeaders());
    return Promise.resolve();
  }

  protected onDispose(): void {
    ReactDom.unmountComponentAtNode(this.domElement);
  }

  protected get dataVersion(): Version {
    return Version.parse('1.0');
  }

  protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: [
        {
          header: {
            description: strings.PropertyPaneDescription
          },
          groups: [
            {

              groupFields: [
                PropertyPaneTextField('Webpart Title', {
                  label: strings.DescriptionFieldLabel
                })
              ]
            }
          ]
        }
      ]
    };
  }
}

tsx file

import * as React from 'react';
import styles from './Wpsearch.module.scss';
import type { IWpsearchProps } from './IWpsearchProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { TextField, PrimaryButton, DetailsList } from '@fluentui/react';
import { graphfi } from "@pnp/graph";
import '@pnp/graph/search';
import { Constants } from './Constants';
const graph = graphfi();

export interface Iwpsearchstates {
  searchval: string;
}

export default class Wpsearch extends React.Component<IWpsearchProps, Iwpsearchstates> {
  constructor(props: IWpsearchProps) {
    super(props);
    this.state = {
      searchval: ""
    }
  }

  /**
   * Getsearchresults
 :promise<void>  */
  public Getsearchresults = async (): Promise<void> => {
    var searchtext = this.state.searchval;
    if (escape(searchtext) === "") {

    }
    else {
      const results = await graph.query({
        entityTypes: ["site"],
        query: {
          queryString: searchtext + " path:" + this.props.spfxcontext.pageContext.web.absoluteUrl + "/" + Constants.List_documents
        }
      })
        .then((response) => {
          console.log(response);
        });
    }

    return Promise.resolve();
  }

  private _onSearchTextChanged = async (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue: string) => {
    this.setState({
      searchval: newValue
    })
  }
  public render(): React.ReactElement<IWpsearchProps> {
    const {
      description,
      spfxcontext
    } = this.props;

    return (
      <div>
        <div>
          <TextField placeholder='search text' required={true} onChange={this._onSearchTextChanged}></TextField>
          <PrimaryButton text='Search' onClick={this.Getsearchresults}></PrimaryButton>
        </div>
      </div>
    );
  }
}
juliemturner commented 1 month ago

The video isn't outdated, although it encourages things that aren't best practices and doesn't really explain. I would suggest you review this documentation and for a getting started project to follow, you can use this reference. That all said to summarize your issue you're correctly setting up your graph variable in the onInit method, but you don't pass that in any way to the component file... you then create a new constant for graph in the component file, but you didn't register any observers (the using part of the call you made in onInit). Please review the documentation, videos, and sample project I've shared above.

lovethakker02 commented 1 month ago

Thank you Julie for quick response. The documentation is not that straight forward to implement. I am not sure how to pass the graph variable from oninit to component file. Its type is not working with any and I am not able to find any reference where it is passed as props. So, I tried to follow the reference project method. I created separate file "pnpjsconfig.ts" and implemented below code:

import { WebPartContext } from "@microsoft/sp-webpart-base";
import { graphfi, GraphFI, SPFx as graphSPFx } from "@pnp/graph";
// Here getting error for null value assign 
var _graph: GraphFI = null;
export const getGraph = (context?: WebPartContext): GraphFI => {
    if (_graph === null) {
      //You must add the @pnp/logging package to include the PnPLogging behavior it is no longer a peer dependency
      // The LogLevel set's at what level a message will be written to the console

      //Here getting error for context undefined 
      _graph = graphfi().using(graphSPFx(context));
    }
    return _graph;
  };

I am getting 2 errors on this: first on line var _graph: GraphFI = null; error - Type 'null' is not assignable to type 'GraphFI'.ts

second on line _graph = graphfi().using(graphSPFx(context));

error - Argument of type 'WebPartContext | undefined' is not assignable to parameter of type 'ISPFXContext'. Type 'undefined' is not assignable to type 'ISPFXContext'.

Is there anything I am missing? I have not added getSP method as I don't have installed @pnp/sp.

Please provide your thoughts.

juliemturner commented 1 month ago

That error would have to do with your tsconfig settings.. just change the line to

var _graph: GraphFI;

and then change this line: if (_graph === null) { to if (_graph == null) {

lovethakker02 commented 1 month ago

What about the context error? any way to fix that?

juliemturner commented 1 month ago

I believe the fix would be _graph = graphfi().using(graphSPFx(context as ISPFXContext));

You might want to consider taking a TypeScript course because the issues you're running into here are linting/TypeScript errors that you will need to be able to deal with in your code. Although we're happy to provide support for PnPjs these questions aren't really related to that as much as they are to how to write TypeScript.

lovethakker02 commented 1 month ago

Hi Julie. Thank you for the response. I will surely checkout typescript course. The issue I am facing here is I am not able to find clear documentation about how this code works. So, I am not able to provide any typecasting. In above code I am facing same issue with ISPFXContext. It throws error - Cannot find name 'ISPFXContext'.

I tried typecase as below as well but it is throwing same error: _graph = graphfi().using(graphSPFx((context as unknown) as ISPFXContext))

does this have dependencies on any packages? Do I need to import anything else?

juliemturner commented 1 month ago

I'm sorry you're struggling; our documentation assumes TypeScript knowledge so it's clear it's just not clear if you don't fully understand TypeScript. The demo version works for me, so I suspect you have different linting or tsconfig settings than the sample does which is why you're having problems. This could be because I upgraded that solution from SPFx 1.15 instead of starting from scratch with 1.18.2 and the linting rules could have changed. I'll try to find some try and recreate your issue but yes ISPFxContext is something you could import from @pnp/sp it's a type. you should not coerce it to unknown and then to ISPFXContext.

juliemturner commented 1 month ago

@lovethakker02 - If you link to the PR that was merged above (#3047) you can see the updated sample project that uses a clean 1.19.0 SPFx project with all the default linting and tsconfig settings. I made the appropriate adjustments to pass those rules. At this point I think you should be able to continue. If you have further questions related to PnPjs specifically, please don't hesitate to submit a new issue.

github-actions[bot] commented 1 month ago

This issue is locked for inactivity or age. If you have a related issue please open a new issue and reference this one. Closed issues are not tracked.