Skip to content

Commit beb5f0c

Browse files
committed
1.5 documentation changes
1 parent d06974c commit beb5f0c

8 files changed

+357
-10
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ We’ll also monitor [#spfx](http://sharepoint.stackexchange.com/tags/spfx/), [#
1515
You can also tweet / follow [@officedev](https://twitter.com/officedev) or [@officedevpnp](https://twitter.com/officedevpnp).
1616

1717
## SharePoint Framework Releases
18+
* **June 5, 2018**
19+
* **SPFx v1.5** - Release notes coming soon
20+
1821
* **February 15, 2018**
1922
* **SPFx v1.4.1** [See the release notes here](https://github.com/SharePoint/sp-dev-docs/wiki/Release-Notes-for-SPFx-Package-Version-1.4.1)
2023

-59.7 KB
Loading

docs/spfx/dynamic-data.md

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
# Connect SharePoint Framework components using dynamic data
2+
3+
Using the dynamic data capability, you can connect SharePoint Framework client-side web parts and extensions to each other and exchange information between the components. This allows you to build rich experiences and compelling end-user solutions.
4+
5+
> [!IMPORTANT]
6+
> The dynamic data capability is currently in preview and using it in production solutions is not supported.
7+
8+
## Expose data using dynamic data source
9+
10+
Dynamic data in the SharePoint Framework is based on the source-notification model. Component designated as a dynamic data source, provides data and notifies about its changes. Other components on the page can subscribe to notifications issued by a dynamic data source. When handling notifications, dynamic data consumers can retrieve the current value of the dynamic data set exposed by the data source.
11+
12+
Each data source implements the `IDynamicDataController` interface. Following, is an example of a web part that displays a list of upcoming events. For each event, it includes some information such as its name, description and ___location. The events web part exposes information about the selected event to other components on the page in two ways: the complete event information and the ___location address.
13+
14+
```ts
15+
export default class EventsWebPart extends BaseClientSideWebPart<IEventsWebPartProps> implements IDynamicDataController {
16+
private _selectedEvent: IEvent;
17+
18+
private _eventSelected = (event: IEvent): void => {
19+
this._selectedEvent = event;
20+
this.context.dynamicDataSourceManager.notifyPropertyChanged('event');
21+
this.context.dynamicDataSourceManager.notifyPropertyChanged('___location');
22+
}
23+
24+
protected onInit(): Promise<void> {
25+
this.context.dynamicDataSourceManager.initializeSource(this);
26+
27+
return Promise.resolve();
28+
}
29+
30+
public getPropertyDefinitions(): ReadonlyArray<IDynamicDataPropertyDefinition> {
31+
return [
32+
{
33+
id: 'event',
34+
title: 'Event'
35+
},
36+
{
37+
id: '___location',
38+
title: 'Location'
39+
}
40+
];
41+
}
42+
43+
public getPropertyValue(propertyId: string): IEvent | ILocation {
44+
switch (propertyId) {
45+
case 'event':
46+
return this._selectedEvent;
47+
case '___location':
48+
return this._selectedEvent ? { city: this._selectedEvent.city, address: this._selectedEvent.address } : undefined;
49+
}
50+
51+
throw new Error('Bad property id');
52+
}
53+
54+
public render(): void {
55+
const element: React.ReactElement<IEventsProps> = React.createElement(
56+
Events,
57+
{
58+
displayMode: this.displayMode,
59+
onEventSelected: this._eventSelected,
60+
title: this.properties.title,
61+
updateProperty: (value: string): void => {
62+
this.properties.title = value;
63+
},
64+
siteUrl: this.context.pageContext.web.serverRelativeUrl
65+
}
66+
);
67+
68+
ReactDom.render(element, this.domElement);
69+
}
70+
71+
// ... omitted for brevity
72+
}
73+
```
74+
75+
> [!IMPORTANT]
76+
> The `IDynamicDataController` interface can be implemented by any class, not just web parts and extensions. If the dynamic data source requires complex logic, you should consider moving it into a separate class rather than implementing it directly inside a web part or extension.
77+
78+
The class implementing the `IDynamicDataController` interface must define two methods: `getPropertyDefinitions` and `getPropertyValue`. The `getPropertyDefinitions` method returns an array of types of data that the particular dynamic data source returns. In the previous example, the web part exposes detailed information about an event and its ___location. Even though the information comes from a single object (`_selectedEvent`), by exposing it in two different shapes, the web part is more reusable and could be used in combination with other web parts that aren't specific to events, such as a map web part that can show a map for the specified address. The list of the data types exposed by the data source is displayed to end-users when connecting web parts to the data source.
79+
80+
> [!IMPORTANT]
81+
> The object returned by the `getPropertyValue` method should be flat, for example:
82+
> ```json
83+
> {
84+
> "date": "2018-06-01T11:21:59.446Z",
85+
> "name": "Tampa Home Show",
86+
> "organizerEmail": "Grady Archie",
87+
> "organizerName": "[email protected]"
88+
> }
89+
> ```
90+
>
91+
> Object with a nested structure will be flattened during serialization, which could lead to undesired effects. For example, object like:
92+
> ```json
93+
> {
94+
> "date": "2018-06-01T11:21:59.446Z",
95+
> "name": "Tampa Home Show",
96+
> "organizer": {
97+
> "email": "Grady Archie",
98+
> "name": "[email protected]"
99+
> }
100+
> }
101+
> ```
102+
>
103+
> would after serialization become:
104+
> ```json
105+
> {
106+
> "date": "2018-06-01T11:21:59.446Z",
107+
> "name": "Tampa Home Show",
108+
> "organizer.email": "Grady Archie",
109+
> "organizer.name": "[email protected]"
110+
> }
111+
> ```
112+
113+
The `getPropertyValue` method returns the value for the particular type of data. The value of the `propertyId` argument corresponds to the `id` of the definition specified in the `getPropertyDefinitions` method.
114+
115+
To register a component as a dynamic data source, call the `this.context.dynamicDataSourceManager.initializeSource()` method, passing the instance of the dynamic data source as a parameter. In the previous example, the web part itself implements the `IDynamicDataController` interface, which is why the `initializeSource` method is called with `this` as its argument.
116+
117+
In the example code, the web part displays upcoming events in a list. Each time, the user selects an event from the list, the web part calls the `_eventSelected` method. In that method, the web part assigns the selected event to the `_selectedEvent` class variable and issues a notification that the information about the selected event and ___location has changed by calling the `this.context.dynamicDataSourceManager.notifyPropertyChanged()` method passing the `id` of the definition that represents the changed data set.
118+
119+
## Consume dynamic data
120+
121+
Components on the page can subscribe to data notifications exposed by dynamic data sources. The subscription is recorded using the ID of the data source and the ID of the data set it exposes.
122+
123+
Following is the code of a web part showing detailed information about the event selected in the events list web part showed previously. The displayed data is retrieved from the events data source configured in web part properties.
124+
125+
```ts
126+
export default class EventDetailsWebPart extends BaseClientSideWebPart<IEventDetailsWebPartProps> {
127+
protected onInit(): Promise<void> {
128+
this.render = this.render.bind(this);
129+
130+
return Promise.resolve();
131+
}
132+
133+
public render(): void {
134+
let event: IEvent = undefined;
135+
const needsConfiguration: boolean = !this.properties.sourceId || !this.properties.propertyId;
136+
137+
if (this.renderedOnce === false && !needsConfiguration) {
138+
try {
139+
this.context.dynamicDataProvider.registerPropertyChanged(this.properties.sourceId, this.properties.propertyId, this.render);
140+
this._lastSourceId = this.properties.sourceId;
141+
this._lastPropertyId = this.properties.propertyId;
142+
}
143+
catch (e) {
144+
this.context.statusRenderer.renderError(this.domElement, `An error has occurred while connecting to the data source. Details: ${e}`);
145+
return;
146+
}
147+
}
148+
149+
if (!needsConfiguration) {
150+
const source: IDynamicDataSource = this.context.dynamicDataProvider.tryGetSource(this.properties.sourceId);
151+
event = source ? source.getPropertyValue(this.properties.propertyId) : undefined;
152+
}
153+
154+
const element: React.ReactElement<IEventDetailsProps> = React.createElement(
155+
EventDetails,
156+
{
157+
needsConfiguration: needsConfiguration,
158+
event: event,
159+
onConfigure: this._onConfigure,
160+
displayMode: this.displayMode,
161+
title: this.properties.title,
162+
updateProperty: (value: string): void => {
163+
this.properties.title = value;
164+
}
165+
}
166+
);
167+
168+
ReactDom.render(element, this.domElement);
169+
}
170+
171+
// ... omitted for brevity
172+
}
173+
```
174+
175+
Dynamic data consumer subscribes to notifications issues by the dynamic data source by calling the `this.context.dynamicDataProvider.registerPropertyChanged(sourceId, propertyId, handler);` method. The first argument, `sourceId` refers to the ID of the dynamic data source which issues notifications. The second argument, `propertyId` refers to the ID of the data set exposed by the data source. The last arguments points to the handler method which is called whenever the data source issued a notification.
176+
177+
When the data source notifies the data consumer that its data is changed, the specified handler method is called. In that method, you should retrieve the latest data from the data source. You do this, by first retrieving the source using the `this.context.dynamicDataProvider.tryGetSource(sourceId)` method. Once you have the reference to the data source, you can retrieve the data by calling the `source.getPropertyValue(propertyId)` method, specifying the id of the data set.
178+
179+
```ts
180+
const source: IDynamicDataSource = this.context.dynamicDataProvider.tryGetSource(this.properties.sourceId);
181+
event = source ? source.getPropertyValue(this.properties.propertyId) : undefined;
182+
```
183+
184+
> [!IMPORTANT]
185+
> Depending on the order in which components on the page are loaded, it could happen that the particular dynamic data source is not available when retrieving it using the `tryGetSource` method. Before calling the `getPropertyValue` method to retrieve the current value for the dynamic data, you should first check if the source is available to avoid runtime errors.
186+
187+
## Connect to a dynamic data source
188+
189+
Dynamic data consumers subscribe to notifications issued by dynamic data sources by specifying the unique ID of the dynamic data source. Because these IDs are generated on runtime, the easiest way to allow users to connect components is by using the web part property pane enumerating all available dynamic data sources and their data sets. Following code shows how to provide configuration interface for end-users to connect web part to a dynamic data source on the page:
190+
191+
```ts
192+
import * as React from 'react';
193+
import * as ReactDom from 'react-dom';
194+
import { Version } from '@microsoft/sp-core-library';
195+
import {
196+
BaseClientSideWebPart,
197+
IPropertyPaneConfiguration,
198+
PropertyPaneTextField,
199+
IPropertyPaneDropdownOption,
200+
PropertyPaneDropdown
201+
} from '@microsoft/sp-webpart-base';
202+
203+
import * as strings from 'EventDetailsWebPartStrings';
204+
import { EventDetails, IEventDetailsProps } from './components';
205+
import { IDynamicDataSource } from '@microsoft/sp-dynamic-data';
206+
import { IEvent } from '../../data';
207+
208+
export interface IEventDetailsWebPartProps {
209+
propertyId: string;
210+
sourceId: string;
211+
title: string;
212+
}
213+
214+
export default class EventDetailsWebPart extends BaseClientSideWebPart<IEventDetailsWebPartProps> {
215+
private _lastSourceId: string = undefined;
216+
private _lastPropertyId: string = undefined;
217+
218+
// ... omitted for brevity
219+
220+
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
221+
const sourceOptions: IPropertyPaneDropdownOption[] =
222+
this.context.dynamicDataProvider.getAvailableSources().map(source => {
223+
return {
224+
key: source.id,
225+
text: source.metadata.title
226+
};
227+
});
228+
const selectedSource: string = this.properties.sourceId;
229+
230+
let propertyOptions: IPropertyPaneDropdownOption[] = [];
231+
if (selectedSource) {
232+
const source: IDynamicDataSource = this.context.dynamicDataProvider.tryGetSource(selectedSource);
233+
if (source) {
234+
propertyOptions = source.getPropertyDefinitions().map(prop => {
235+
return {
236+
key: prop.id,
237+
text: prop.title
238+
};
239+
});
240+
}
241+
}
242+
243+
return {
244+
pages: [
245+
{
246+
groups: [
247+
{
248+
groupFields: [
249+
PropertyPaneDropdown('sourceId', {
250+
label: strings.SourceIdFieldLabel,
251+
options: sourceOptions,
252+
selectedKey: this.properties.sourceId
253+
}),
254+
PropertyPaneDropdown('propertyId', {
255+
label: strings.PropertyIdFieldLabel,
256+
options: propertyOptions,
257+
selectedKey: this.properties.propertyId
258+
})
259+
]
260+
}
261+
]
262+
}
263+
]
264+
};
265+
}
266+
267+
protected onPropertyPaneFieldChanged(propertyPath: string): void {
268+
if (propertyPath === 'sourceId') {
269+
this.properties.propertyId =
270+
this.context.dynamicDataProvider.tryGetSource(this.properties.sourceId).getPropertyDefinitions()[0].id;
271+
}
272+
273+
if (this._lastSourceId && this._lastPropertyId) {
274+
this.context.dynamicDataProvider.unregisterPropertyChanged(this._lastSourceId, this._lastPropertyId, this.render);
275+
}
276+
277+
this.context.dynamicDataProvider.registerPropertyChanged(this.properties.sourceId, this.properties.propertyId, this.render);
278+
this._lastSourceId = this.properties.sourceId;
279+
this._lastPropertyId = this.properties.propertyId;
280+
}
281+
}
282+
```
283+
284+
The `this.context.dynamicDataProvider.getAvailableSources()` method returns information about all dynamic data source available on the current page. For each dynamic data source you get the information about its unique ID as well as other information included in its component manifest. Additionally, for each data source you can call the `IDynamicDataSource.getPropertyDefinitions()` method to get information about all data sets it exposes.
285+
286+
When using the web part property pane to allow users to connect components, it's important that you remove the previously configured event handler, before registering the new handler by calling the `this.context.dynamicDataProvider.unregisterPropertyChanged(sourceId, propertyId, handler)` method.
287+
288+
## Considerations
289+
290+
- each page can have multiple dynamic data sources and consumers
291+
- each component can both provide dynamic data to other components and consume dynamic data from other components
292+
- components can consume data from multiple dynamic data sources
293+
- to persist subscriptions to dynamic data sources, store the subscription information in web part properties
294+
295+
296+
## See also
297+
298+
[Dynamic Data sample code](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-events-dynamicdata)
299+
300+

docs/spfx/roadmap.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ The first release of the SharePoint Framework contained only support for client-
1818
- [Asset packaging and site collection app catalog](../general-development/site-collection-app-catalog.md)
1919
- [Improved MS Graph integration with additional scopes](./use-msgraph.md)
2020
- [Call securely Azure AD secured applications from SharePoint Framework](./use-aadhttpclient.md)
21+
- [Connect SharePoint Framework components using dynamic data](./dynamic-data.md)
22+
- Support for Yarn and PNPM package managers
23+
- Relocating preview code to isolated packages
24+
- SPFx Yeoman package extensibility
2125

2226
> [!NOTE]
2327
> This is a list of areas that SharePoint engineering has in the backlog and is looking into. This does **NOT** mean that all of them will be delivered, but we are looking into getting items and topics from this list gradually released with the future releases of SharePoint Framework.
@@ -27,11 +31,15 @@ The first release of the SharePoint Framework contained only support for client-
2731
- Site level WebHooks
2832
- Updated 'store' story with SharePoint Framework support
2933
- 'Store' story for SharePoint Framework solutions with easy distribution channel for ISVs
34+
- React 16 support
35+
- Modern experiences in SharePoint 2019
36+
- Content Security Policy (CSP) support
37+
- Additional placeholder, like content header, content footer, navigation extensions, search extensions
38+
- "App pages" - developer defined pages which end users cannot edit
3039

3140
## Client-side web parts++ and add-ins
3241

3342
- Support more complex scenarios and interactions with web parts
34-
- Part-to-part communication
3543
- JavaScript Framework isolation
3644
- "Citizen developer" model for lightweight development
3745

@@ -40,6 +48,8 @@ The first release of the SharePoint Framework contained only support for client-
4048
- Native responsive support
4149
- Build add-ins with SharePoint Framework
4250

51+
- Host SharePoint Framework web parts as Microsoft Teams tabs
52+
4353

4454
## Application Lifecycle Management
4555

@@ -48,12 +58,13 @@ The first release of the SharePoint Framework contained only support for client-
4858
- Tenant admin gets automatically notified.
4959
- Settings to control the default experience around approval process.
5060

61+
- Tenant wide deployment of extensions
5162

5263
## Developer experience
5364

5465
- SharePoint Framework Workbench 2.0: Development story for SharePoint Framework Extensions
5566
- Toolchain components
56-
- Additional Yeoman templates
67+
- Additional Yeoman templates - like Angular Elements
5768

5869
## Already shipped capabilities
5970

docs/spfx/toolchain/scaffolding-projects-using-yeoman-sharepoint-generator.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,18 @@ Option | Description
6060
--componentDescription|Description of the component.
6161
--componentName|Name of the component.
6262
--framework|Framework to use for the solution. Choose one from "none", "react", "knockout".
63+
--plusbeta| Use the beta packages. Scaffolding should be done with @plusbeta
6364
--extensionType|The type of extension: Currently ApplicationCustomizer, FieldCustomizer, ListViewCommandSet.
6465
--solutionName|Client-side solution name, as well as folder name.
6566
--environment|The target environment for the solution. Either "onprem" or "spo".
67+
--package-manager|The package manager for the solution. Options are: "npm", "pnpm", or "yarn". Default: *npm*
68+
--skip-feature-deployment|If specified, allow the tenant admin the choice of being able to deploy the components to all sites immediately without running any feature deployment or adding apps in sites. Default: *false*.
6669

6770
<br/>
6871

69-
**Available arguments**
7072

71-
Argument | Description | Type | Required |
72-
-- | -- | -- | -- |
73-
skipFeatureDeployment | If specified, allow the tenant admin the choice of being able to deploy the components to all sites immediately without running any feature deployment or adding apps in sites. | Boolean | false |
74-
75-
<br/>
73+
> [!WARNING]
74+
> *skip-feature-deployment* command line support was introduced with the SharePoint Framework v1.5. This option was previously a command line argument called *skipFeatureDeployment*.
7675
7776
Following is an example of a command that creates a solution called "hello-world" with:
7877
- A web part "HelloWorld"
@@ -89,7 +88,8 @@ yo @microsoft/sharepoint
8988
--componentName "HelloWorld"
9089
--componentDescription "HelloWorld web part"
9190
--skip-install
92-
--environment "spo" skipFeatureDeployment true
91+
--environment "spo"
92+
--skip-feature-deployment true
9393
```
9494

9595
<br/>

0 commit comments

Comments
 (0)