3

Summary

Does anyone know how to have the UI Fabric React TagPicker add a new item when whatever's typed in doesn't exist in the suggestions?

I'm going to want to send off an AJAX request to a service to add the new item in the backend, too.

Desired functionality

When someone types in something in the picker that doesn't exist in the options, they should have the option to adding that as a new option, and call some other code to add this new option to a back end API..

What I've tried so far

I've been able to add a button on pickerSuggestionsProps.onRenderNoResultFound with a click handler that will run some code to add the keyword, but this involves hooking onto onChange to keep track of what the user's typed in, but this breaks the selection of suggestions (when onChange is handled, the user is unable to pick suggestions from the list that appears under the picker).

James Love
  • 1,025
  • 9
  • 16
  • did you get anywhere with this? i want to do the same thing... – Leo Apr 29 '19 at 20:37
  • @LeonardoSeccia we are talking about [office-ui-fabric-react](https://github.com/OfficeDev/office-ui-fabric-react)? – Fabrizio Bertoglio May 07 '19 at 05:41
  • @fabriziobertoglio yes – Leo May 07 '19 at 06:23
  • @LeonardoSeccia I can not find my way around the docs, maybe this is the main reason the question went unanswered in the first place. It is indeed badly written and does not really include any code. Feel free to edit it and improve it. I believe well written questions always get an answer. – Fabrizio Bertoglio May 07 '19 at 06:57
  • This is how someone else dealt with this issue: https://github.com/OfficeDev/office-ui-fabric-react/issues/1001 – Leo May 07 '19 at 14:33

2 Answers2

1

I suggested that this control is improved on GitHub.

This codepen I forked from the reply I got on there, shows how this is done at the moment...

const {
TagPicker,  Fabric, mergeStyles
} = window.Fabric;

const _testTags: ITag[] = [
  'black',
  'blue',
  'brown',
  'cyan',
  'green',
  'magenta',
  'mauve',
  'orange',
  'pink',
  'purple',
  'red',
  'rose',
  'violet',
  'white',
  'yellow'
].map(item => ({ key: item, name: item, isNewItem: false }));

const newItem = mergeStyles({ color: '#f00', background: '#ddf', padding: '10px' });
const existingItem = mergeStyles({ color: '#222', padding: '10px' });

class TagPickerBasicExample extends React.Component<{}, {}> {
  private picker = React.createRef<IBasePicker<ITag>>();

  public render() {
    return <TagPicker
              onResolveSuggestions={this.onResolveSuggestions}
              componentRef={this.picker}
              onRenderSuggestionsItem={this.onRenderSuggestionsItem}
              onItemSelected={this.onItemSelected}
            />;
  }

  private onRenderSuggestionsItem = (props: any, itemProps: any): JSX.Element => {
    console.log({props, itemProps})
    return <div className={props.isNewItem ? newItem : existingItem} key={props.key}>
      {props.name}
      </div>;
  };

  private onResolveSuggestions = (filterText: string, tagList: ITag[]): ITag[] => {
     console.log({filterText, tagList});

    const existingMatches = filterText
      ? _testTags
          .filter(tag => tag.name.toLowerCase().indexOf(filterText.toLowerCase()) === 0)
          .filter(tag => !this.listContainsDocument(tag, tagList))
      : [];

    return existingMatches.some(a=> a.key === filterText) ? 
    existingMatches :
    [{ key: filterText, name: filterText, isNewItem: true } as ITag].concat(existingMatches);
  };

  private onItemSelected = (item: ITag): ITag | null => {
    if(item && item.isNewItem) {
      alert("New item added, make any necessary backend calls.");
    }
    return item;
  };

  private listContainsDocument(tag: ITag, tagList?: ITag[]) {
    if (!tagList || !tagList.length || tagList.length === 0) {
      return false;
    }
    return tagList.filter(compareTag => compareTag.key === tag.key).length > 0;
  }
}

ReactDOM.render(
  <Fabric>
    <TagPickerBasicExample />
  </Fabric>,
  document.getElementById('content')
);

Hope it helps - it really did for me.

Leo
  • 5,013
  • 1
  • 28
  • 65
0

Turns out this was fairly straight forward. You just have to set the createGenericItem prop to a method that returns a dummy item:

createGenericItem={(input: string) => {
  return { key: '', name: input} as ITag;
}}
James Love
  • 1,025
  • 9
  • 16