6

Im working on a pretty simple spfx CommandSet extension. Its goal is to duplicate an item, when an item is selected. I have the working code. My problem is, that i searched the context property, but could not find a method, to update the ListView, to show the newly created(duplicated) item. (By default it not shows the created item because i created it with an API call)

Code:

@override
public onListViewUpdated(event: IListViewCommandSetListViewUpdatedParameters): void {
  const duplicateCommand: Command = this.tryGetCommand('DUPLICATE_ITEM');
  if (duplicateCommand) {
    duplicateCommand.visible = (event.selectedRows.length === 1);
  }
}

@override
public onExecute(event: IListViewCommandSetExecuteEventParameters): void {
  switch (event.itemId) {
    case 'DUPLICATE_ITEM': this.DuplicateItem(event); break;
    default: throw new Error('Unknown command');
  }
}
private DuplicateItem(event: IListViewCommandSetExecuteEventParameters): void {
  //stuff to do
  pnp.sp.web.lists.getByTitle(listName).items.add(data)
    .then((result: any) => {
      //refresh ListView to show new item
    }).catch(error => {
      //error handling
    });
}

How can i do this?

5 Answers 5

6
+50

There is a Microsoft sample project on github that does exactly the same. Looking at their code, it does mention that there is currently no way to refresh just the list view and their solution seems to be reloading the entire page using location.reload();. If you look at the file SpfxCloneCommandSet.ts they mention that there is currently no way to reload just the list view and just refresh entire page.

1

Unfortunately, you cannot update a list view with the help of user API without refreshing the whole page. But it is possible to extract corresponding method from a built-in require module with the below code:

var listEl = document.querySelector('.sp-App-root');
var listVM = __ko.dataFor(listEl);
var ii = listVM.resources.consume(require.s.contexts._.defined.eR.itemInvalidator);
ii.invalidateCurrentItemSet();

Since the module containing the 'itemValidator' can be renamed in future updates of SharePoint, I optimized the above by iterating through all loaded modules and checking whether the 'itemInvalidator' property exists:

var modules = window.require.s.contexts._.defined;
for (var p in modules) {
    if (modules.hasOwnProperty(p) && modules[p] && modules[p].itemInvalidator) {
        var itemInvalidatorKey = modules[p].itemInvalidator;
        var rootEl = document.getElementById('appRoot');
        var rootVM = window.__ko.dataFor(rootEl);
        var itemInvalidator = rootVM.resources.consume(itemInvalidatorKey);
        itemInvalidator.invalidateCurrentItemSet()
        break;
    }
}
1

I know it's old question but I got here looking for an answer so I'll put what I found here. My answer applies to SharePoint 2019 On Premise and is based on @Alberto and @Dmitry Kozlov answers:

var key = Object.keys(odstore.dataStore)[0].substr("item=".length);
var rootEl = document.getElementById('appRoot');
var rootVM = window.__ko.dataFor(rootEl);
var modules = window['require']['s'].contexts._.defined;
var itemProviderModule = modules["odsp-next/providers/item/ItemProvider"];
var itemProvider = rootVM.resources.consume(itemProviderModule.resourceKey);
itemProvider.invalidateItem(key, {triggerFetch: true});
1

SPFx version 1.14 added a new method that allows requesting a reflow of the ListView:
BaseListViewCommandSet.raiseOnChange: () => void

Reference:
SPFx v1.14 Release Notes: Inform ListView on Command Set Changes

Still, using location.reload(); can be a very convenient approach as it just refreshes the entire page immediately instead of requesting a reflow of the ListView which, as I noticed, takes some time to actually make the request to update the list.

1
  • raiseOnChange is not working for me. looks like it does absolutely nothing... Is a simple call to this.raiseOnChange(); after list.Update() is enough ? Commented Sep 29, 2023 at 18:00
0

Please check out this workaround which is working for me:

 public static forceListViewUpdate() {

    try {
      let modules = (window as any).require.s.contexts._.defined;
      for (var p in modules) {
        if (modules.hasOwnProperty(p) && modules[p] && modules[p].itemInvalidator) {
          let itemInvalidatorKey = modules[p].itemInvalidator;
          let rootEl = document.getElementById('appRoot');
          let rootVM = (window as any).__ko.dataFor(rootEl);
          let itemInvalidator = rootVM.resources.consume(itemInvalidatorKey);
          itemInvalidator.invalidateCurrentItemSet();
          break;
        }
      }
    } catch (e) {
      console.log('Unable to update ListView:', e);
    }

      }

If you like, check the source here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.