Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add multiple support to c-select. #499

Merged
merged 10 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 5.3.0

- Added `multiple` prop to `c-select`, allowing for the selection of multiple models.
- `c-select-many-to-many` is now based on `c-select` rather than `v-autocomplete`. As a result, it has gained support for all of the props and slots of `c-select`.

# 5.2.1

## Fixes
Expand Down
1 change: 1 addition & 0 deletions docs/stacks/vue/coalesce-vue-vuetify/components/c-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ A summary of the components delegated to, by type:
- date: [c-datetime-picker](/stacks/vue/coalesce-vue-vuetify/components/c-datetime-picker.md)
- model: [c-select](/stacks/vue/coalesce-vue-vuetify/components/c-select.md)
- [[ManyToMany]](/modeling/model-components/attributes/many-to-many.md) collection: [c-select-many-to-many](/stacks/vue/coalesce-vue-vuetify/components/c-select-many-to-many.md)
- Model collection (not many-to-many): [c-select](/stacks/vue/coalesce-vue-vuetify/components/c-select.md)
- Non-object collection: [c-select-values](/stacks/vue/coalesce-vue-vuetify/components/c-select-values.md)

Any other unsupported type will simply be displayed with [c-display](/stacks/vue/coalesce-vue-vuetify/components/c-display.md), unless a [default slot](https://vuejs.org/guide/components/slots.html) is provided - in that case, the default slot will be rendered instead.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,50 +19,17 @@ It is unlikely that you'll ever need to use this component directly - it is high
``` vue-html
<c-select-many-to-many
:model="case"
for="caseProducts"
dense
outlined
/>
```

``` vue-html
<c-select-many-to-many
v-model="case.caseProducts"
for="Case.caseProducts"
for="caseProducts"
variant="outlined"
density="compact"
/>
```

## Props

<Prop def="for: string | Property | Value" lang="ts" />

A metadata specifier for the value being bound. One of:

- A string with the name of the value belonging to `model`.
- A direct reference to a metadata object.
- A string in dot-notation that starts with a type name.

::: tip Note
c-select-many-to-many expects metadata for the "real" collection navigation property on a model. If you provide it the string you passed to [[ManyToMany]](/modeling/model-components/attributes/many-to-many.md), an error wil be thrown.
:::
See [c-select / Props](./c-select.md#props).

<Prop def="model?: Model" lang="ts" />

An object owning the value that was specified by the `for` prop. If provided, the input will be bound to the corresponding property on the `model` object.

<Prop def="value?: any // Vue 2
modelValue?: any // Vue 3" lang="ts" />

If binding the component with ``v-model``, accepts the ``value`` part of ``v-model``.

<Prop def="params?: ListParameters" lang="ts" />

An optional set of [Data Source Standard Parameters](/modeling/model-components/data-sources.md#standard-parameters) to pass to API calls made to the server.

<Prop def="cache?: ResponseCachingConfiguration | boolean" lang="ts" />

If provided and non-false, enables [response caching](/stacks/vue/layers/api-clients.md#response-caching) on the component's internal API caller.

Since `c-select-many-to-many` internally uses `c-select` as its implementation, all props of `c-select` are also supported by `c-select-many-to-many`.

## Events

Expand Down
36 changes: 27 additions & 9 deletions docs/stacks/vue/coalesce-vue-vuetify/components/c-select.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ Binding an arbitrary primary key value or an arbitrary object:
<c-select for="Person" v-model="selectedPerson" />
```

Multi-select:

``` vue-html
<!-- Binding selected primary keys: -->
<c-select for="Person" multiple v-model:key-value="selectedPeopleIds" />

<!-- Binding selected objects: -->
<c-select for="Person" multiple v-model="selectedPeople" />
<c-select for="Person" multiple v-model:object-value="selectedPeople" />
```

Examples of other props:

``` vue-html
Expand All @@ -48,17 +59,18 @@ Examples of other props:

## Props

Note: In addition to the below props, `c-select` also supports most props that are supported by Vuetify's [v-text-field](https://vuetifyjs.com/en/components/text-fields/).

<Prop def="for: string | ForeignKeyProperty | ModelReferenceNavigationProperty | ModelType" lang="ts" />

A metadata specifier for the value being bound. One of:

- The name of a foreign key or reference navigation property belonging to `model`.
- The name of a model type.
- A direct reference to a metadata object.
- A string in dot-notation that starts with a type name that resolves to a foreign key or reference navigation property.

::: tip
When binding by a key value, if the corresponding object cannot be found (e.g. there is no navigation property, or the navigation property is null), c-select will automatically attempt to load the object from the server so it can be displayed in the UI.
When the binding can only locate a PK value and the corresponding object cannot be found (e.g. there is no navigation property, or the navigation property is null), c-select will automatically attempt to load the object from the server so it can be displayed in the UI.
:::

<Prop def="model?: Model" lang="ts" />
Expand All @@ -72,13 +84,19 @@ modelValue?: any // Vue 3" lang="ts" />

When binding the component with ``v-model``, accepts the ``value`` part of ``v-model``. If `for` was specified as a foreign key, this will expect a key; likewise, if `for` was specified as a type or as a navigation property, this will expect an object.

<Prop def="keyValue?: any" lang="ts" />
<Prop def="multiple?: boolean" lang="ts" />

Enables multi-select functionality. Bindings for `modelValue`, `keyValue`, and `objectValue` will accept and emit arrays instead of single values.

<Prop def="keyValue?: TKey
'onUpdate:keyValue': (value: TKey) => void" lang="ts" />

When bound with `v-model:key-value="keyValue"`, allows binding the primary key of the selected object explicitly.
When bound with `v-model:key-value="keyValue"`, allows binding the primary key of the selected object explicitly. Binds an array when in multi-select mode.

<Prop def="objectValue?: any" lang="ts" />
<Prop def="objectValue?: TModel
'onUpdate:objectValue': (value: TModel) => void" lang="ts" />

When bound with `v-model:object-value="objectValue"`, allows binding the selected object explicitly.
When bound with `v-model:object-value="objectValue"`, allows binding the selected object explicitly. Binds an array when in multi-select mode.

<Prop def="clearable?: boolean" lang="ts" />

Expand Down Expand Up @@ -149,8 +167,8 @@ createMethods = {

## Slots

`#item="{ item, search }"` - Slot used to customize the text of both items inside the list, as well as the text of selected items. By default, items are rendered with [c-display](/stacks/vue/coalesce-vue-vuetify/components/c-display.md). Slot is passed a parameter `item` containing a [model instance](/stacks/vue/layers/models.md), and `search` containing the current search query.
`#item="{ item: TModel, search: string }"` - Slot used to customize the text of both items inside the list, as well as the text of selected items. By default, items are rendered with [c-display](/stacks/vue/coalesce-vue-vuetify/components/c-display.md). Slot is passed a parameter `item` containing a [model instance](/stacks/vue/layers/models.md), and `search` containing the current search query.

`#list-item="{ item, search }"` - Slot used to customize the text of items inside the list. If not provided, falls back to the `item` slot.
`#list-item="{ item: TModel, search: string, selected: boolean }"` - Slot used to customize the text of items inside the list. If not provided, falls back to the `item` slot. Contents are wrapped in a `v-list-item-title`.

`#selected-item="{ item, search }"` - Slot used to customize the text of selected items. If not provided, falls back to the `item` slot.
`#selected-item="{ item: TModel, search: string, index: number, remove: () => void }"` - Slot used to customize the text of selected items. If not provided, falls back to the `item` slot. The `remove` function will deselect the item and is only valid when using multi-select.
9 changes: 9 additions & 0 deletions playground/Coalesce.Domain/Case.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,15 @@ public override IQueryable<Case> GetQuery(IDataSourceParameters parameters) => D
.IncludeChildren();
}

public class MissingManyToManyFarSide(CrudContext<AppDbContext> context) : StandardDataSource<Case, AppDbContext>(context)
{
public override IQueryable<Case> GetQuery(IDataSourceParameters parameters) => Db.Cases
.Include(c => c.CaseProducts)
.Include(c => c.AssignedTo)
.Include(c => c.ReportedBy);
}


/// <summary>
/// Returns a list of summary information about Cases
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion playground/Coalesce.Domain/Person.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ public static string[] MethodWithStringArrayParameter(AppDbContext db, string[]
}

[Coalesce, Execute]
public static Person MethodWithEntityParameter(AppDbContext db, Person person)
public static Person MethodWithEntityParameter(AppDbContext db, Person person, Person[] people)
{
return person;
}
Expand Down
13 changes: 9 additions & 4 deletions playground/Coalesce.Web.Vue2/Api/Generated/PersonController.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion playground/Coalesce.Web.Vue2/src/api-clients.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions playground/Coalesce.Web.Vue2/src/metadata.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions playground/Coalesce.Web.Vue2/src/models.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions playground/Coalesce.Web.Vue2/src/viewmodels.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions playground/Coalesce.Web.Vue3/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"typescript.tsdk": "node_modules\\typescript\\lib",
"dotnet.defaultSolution": "Coalesce.Web.Vue3.sln"
}
13 changes: 9 additions & 4 deletions playground/Coalesce.Web.Vue3/Api/Generated/PersonController.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 4 additions & 8 deletions playground/Coalesce.Web.Vue3/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@
label="Dark Mode"
v-model="darkMode"
hide-details
class="ml-2"
class="mx-3"
density="compact"
/>

<v-btn variant="text" to="/">Home</v-btn>
<v-btn variant="text" to="/test">Test</v-btn>
<v-btn variant="text" to="/test-setup">Test2</v-btn>
<v-btn variant="text" to="/examples">Examples/Tests</v-btn>
<v-btn variant="text" to="/audit-logs">Audit</v-btn>
<v-btn variant="text" href="/swagger">Swagger</v-btn>
<v-btn variant="text" href="/scalar/v1">OpenAPI</v-btn>
Expand Down Expand Up @@ -46,10 +44,8 @@

<v-main>
<router-view v-slot="{ Component }">
<transition name="router-transition" mode="out-in" appear>
<!-- https://stackoverflow.com/questions/52847979/what-is-router-view-key-route-fullpath -->
<component ref="routerView" :is="Component" :key="$route.path" />
</transition>
<!-- https://stackoverflow.com/questions/52847979/what-is-router-view-key-route-fullpath -->
<component ref="routerView" :is="Component" :key="$route.path" />
</router-view>
</v-main>
</v-app>
Expand Down
3 changes: 2 additions & 1 deletion playground/Coalesce.Web.Vue3/src/api-clients.g.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading