Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Admin UI extension in Vendure
prasanna malla
prasanna malla

Posted on

     

Admin UI extension in Vendure

When creating a plugin, we canextend the Admin UI in order to expose a graphical interface to the plugin's functionality.

This is possible by definingAdminUiExtensions. UI extension is anAngular module that gets compiled into the Admin UI application bundle by thecompileUiExtensions function exported by the@vendure/ui-devkit package. Internally, the ui-devkit package makes use of the Angular CLI to compile an optimized set of JavaScript bundles containing your extensions. The Vendure Admin UI is built with Angular, andwriting UI extensions in Angular is seamless and powerful. But we can write UI extensions usingReact, Vue, or other frameworks if not familiar with Angular.

Previously, weextended Vendure with a custom Back-In-Stock notification plugin. Now we add a list component to display the subscriptions in the Vendure Admin UI. TypeScript source files of your UI extensions must not be compiled by your regular TypeScript build task. They will instead be compiled by the Angular compiler withcompileUiExtensions()
Exclude them intsconfig.json by adding a line to the "exclude" array

{"exclude":["src/plugins/**/ui/*"]}
Enter fullscreen modeExit fullscreen mode

Install the ui-devkit package withyarn add @vendure/ui-devkit -D Using GraphQL schema first approach we write our query so that we can usecodegen to generate the types for the extension

// src/plugins/vendure-plugin-back-in-stock/ui/components/back-in-stock-list.graphql.tsimportgqlfrom'graphql-tag';exportconstBACKINSTOCK_FRAGMENT=gql`    fragment BackInStock on BackInStock {        id        createdAt        updatedAt        status        email        productVariant {            id            name            stockOnHand        }        customer {            id        }    }`;exportconstGET_BACKINSTOCK_SUBSCRIPTION_LIST=gql`    query GetBackInStockSubscriptionList($options: BackInStockListOptions) {        backInStockSubscriptions(options: $options) {            items {                ...BackInStock            }            totalItems        }    }${BACKINSTOCK_FRAGMENT}`;
Enter fullscreen modeExit fullscreen mode

Modifycodegen.json to addgenerated-types to the generates object and run it withyarn codegen from the root of the project

//codegen.json"generated/generated-types.ts":{"schema":"http://localhost:3000/admin-api","documents":"src/plugins/**/ui/**/*.graphql.ts","plugins":[{"add":{"content":"/* eslint-disable */"}},"typescript","typescript-compatibility","typescript-operations"],"config":{"scalars":{"ID":"string"}}}
Enter fullscreen modeExit fullscreen mode

Now we can start writing the list component

// src/plugins/vendure-plugin-back-in-stock/ui/components/back-in-stock-list.component.tsimport{ChangeDetectionStrategy,Component}from'@angular/core';import{ActivatedRoute,Router}from'@angular/router';import{BaseListComponent,DataService}from'@vendure/admin-ui/core';import{BackInStockSubscriptionStatus,GetBackInStockSubscriptionList,SortOrder,}from'../../../../../generated/generated-types';import{GET_BACKINSTOCK_SUBSCRIPTION_LIST}from'./back-in-stock-list.graphql';// @ts-ignore@Component({selector:'back-in-stock-list',templateUrl:'./back-in-stock-list.component.html',styleUrls:['./back-in-stock-list.component.scss'],changeDetection:ChangeDetectionStrategy.OnPush,})exportclassBackInStockListComponentextendsBaseListComponent<GetBackInStockSubscriptionList.Query,GetBackInStockSubscriptionList.Items,GetBackInStockSubscriptionList.Variables>{filteredStatus:BackInStockSubscriptionStatus|null=BackInStockSubscriptionStatus.Created;constructor(privatedataService:DataService,router:Router,route:ActivatedRoute){super(router,route);super.setQueryFn((...args:any[])=>{returnthis.dataService.query<GetBackInStockSubscriptionList.Query>(GET_BACKINSTOCK_SUBSCRIPTION_LIST,args,);},data=>data.backInStockSubscriptions,(skip,take)=>{return{options:{skip,take,sort:{createdAt:SortOrder.ASC,},...(this.filteredStatus!=null?{filter:{status:{eq:this.filteredStatus,},},}:{}),},};},);}}
Enter fullscreen modeExit fullscreen mode

And add the template for the list component, for adding custom styles useback-in-stock-list.component.scss in the same folder as the html template

// src/plugins/vendure-plugin-back-in-stock/ui/components/back-in-stock-list.component.html<vdr-action-bar><vdr-ab-left><divclass="filter-controls"><selectclrSelectname="status"[(ngModel)]="filteredStatus"(change)="refresh()"><option[ngValue]="null">All Subscriptions</option><optionvalue="Created">Active</option><optionvalue="Notified">Notified</option></select></div></vdr-ab-left><vdr-ab-right></vdr-ab-right></vdr-action-bar><vdr-data-table[items]="items$ | async"[itemsPerPage]="itemsPerPage$ | async"[totalItems]="totalItems$ | async"[currentPage]="currentPage$ | async"(pageChange)="setPageNumber($event)"(itemsPerPageChange)="setItemsPerPage($event)"><vdr-dt-column>ID</vdr-dt-column><vdr-dt-column>Status</vdr-dt-column><vdr-dt-column>Email</vdr-dt-column><vdr-dt-column>Product</vdr-dt-column><vdr-dt-column>Created At</vdr-dt-column><vdr-dt-column>Updated At</vdr-dt-column><ng-templatelet-subscription="item"><tdclass="left align-middle">            {{ subscription.id }}</td><tdclass="left align-middle">            {{ subscription.status }}</td><tdclass="left align-middle"><a*ngIf="subscription.customer !== null; else guestUser"[routerLink]="['/customer', 'customers', subscription.customer.id]">                {{ subscription.email }}</a><ng-template#guestUser>                {{ subscription.email }}</ng-template></td><tdclass="left align-middle"><atitle="{{ 'Stock on hand - ' + subscription.productVariant.stockOnHand }}"[routerLink]="[                    '/catalog',                    'products',                    subscription.productVariant.id,                    { id: subscription.productVariant.id, tab: 'variants' }                ]"><clr-iconshape="link"></clr-icon>                {{ subscription.productVariant.name }}</a></td><tdclass="left align-middle">            {{ subscription.createdAt | date : 'mediumDate' }}</td><tdclass="left align-middle">            {{ subscription.updatedAt | date : 'mediumDate' }}</td></ng-template></vdr-data-table>
Enter fullscreen modeExit fullscreen mode

Let's add the component to it's module

// src/plugins/vendure-plugin-back-in-stock/ui/back-in-stock.module.tsimport{NgModule}from'@angular/core';import{RouterModule}from'@angular/router';import{SharedModule}from'@vendure/admin-ui/core';import{BackInStockListComponent}from'./components/back-in-stock-list.component';// @ts-ignore@NgModule({imports:[SharedModule,RouterModule.forChild([{path:'',pathMatch:'full',component:BackInStockListComponent,data:{breadcrumb:'Back-In-Stock Subscriptions'},},]),],declarations:[BackInStockListComponent],})exportclassBackInStockModule{}
Enter fullscreen modeExit fullscreen mode

And create a shared module for adding a new section to the Admin UI main nav bar containing a link to the extension

// src/plugins/vendure-plugin-back-in-stock/ui/back-in-stock-shared.module.tsimport{NgModule}from'@angular/core';import{SharedModule,addNavMenuSection}from'@vendure/admin-ui/core';// @ts-ignore@NgModule({imports:[SharedModule],providers:[addNavMenuSection({id:'back-in-stock',label:'Custom Plugins',items:[{id:'back-in-stock',label:'Back-In-Stock',routerLink:['/extensions/back-in-stock'],// Icon can be any of https://core.clarity.design/foundation/icons/shapes/icon:'assign-user',},],},// Add this section before the "settings" section'settings',),],})exportclassBackInStockSharedModule{}
Enter fullscreen modeExit fullscreen mode

Add the modules to the plugins array invendure-config.ts

// vendure-config.tsAdminUiPlugin.init({route:'admin',port:3002,adminUiConfig:{apiHost:'http://localhost',apiPort:3000,},app:compileUiExtensions({outputPath:path.join(__dirname,'../admin-ui'),extensions:[{extensionPath:path.join(__dirname,'../src/plugins/back-in-stock-plugin/ui'),ngModules:[{type:'lazy'asconst,route:'back-in-stock',ngModuleFileName:'back-in-stock.module.ts',ngModuleName:'BackInStockModule',},{type:'shared'asconst,ngModuleFileName:'back-in-stock-shared.module.ts',ngModuleName:'BackInStockSharedModule',},],},],devMode:IS_DEV?true:false,}),}),
Enter fullscreen modeExit fullscreen mode

NoticedevMode option set to true which will compile the Admin UI app in development mode, and recompile and auto-refresh the browser on any changes to the extension source files.

Angular uses the concept of modules (NgModules) for organizing related code. These modules can be lazily loaded, which means that the code is not loaded when the app starts, but only when that code is required, keeping the main bundle small to improve performance. Shared modules are loadedeagerly, i.e. code is loaded as soon as the app loads. Modules defining new routes must be set tolazy. Modules defining new navigations items must be set toshared.

Finally, run withyarn dev and see the admin UI extension in action athttp://localhost:4200/admin/

Read myprevious post to learn how the Back-In-Stock plugin was created and join this awesome community of open-source developers onslack to start your own Vendure adventure today!

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

on a qwik vendure adventure
  • Location
    Queens, New York
  • Work
    open-source software developer
  • Joined

More fromprasanna malla

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp