Skip to main content

use-mvc-javascript-apis

Use ABP's browser JavaScript APIs in an MVC / Razor Pages app: generated JS service proxies + the DataTables adapter, plus the client-side abp.* surface (auth, localization, settings/features, messages, busy/block, events). USE FOR: calling an app service from client JS (acme.bookStore.books.book.getList(...)) via the dynamic proxy (/Abp/ServiceProxyScript) or static proxies (abp generate-proxy -t js), feeding a proxy into abp.libs.datatables.createAjax with normalizeConfiguration / rowAction / dataFormat, and the page abp.* APIs — abp.auth.isGranted, abp.localization.localize, abp.setting/abp.features, abp.message/abp.notify, abp.ui.block/setBusy, abp.event, abp.ajax. DO NOT USE FOR: C# typed HTTP client proxies (use consume-remote-services), running the proxy-generation CLI itself / its flags (use abp-cli-commands), exposing app services as HTTP APIs on the server (use expose-http-apis), general MVC page/modal/tag-helper/bundling work (use mvc-razor-ui).

الانتقال إلى التثبيت

معلومات المصدر

المستودع
abpframework/abp-skills
آخر نشاط في المصدر
٢٠ يوليو ٢٠٢٦ في ٠٣:٢٤
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
١٨
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
use-mvc-javascript-apis
description
Use ABP's browser JavaScript APIs in an MVC / Razor Pages app: generated JS service proxies + the DataTables adapter, plus the client-side abp.* surface (auth, localization, settings/features, messages, busy/block, events). USE FOR: calling an app service from client JS (acme.bookStore.books.book.getList(...)) via the dynamic proxy (/Abp/ServiceProxyScript) or static proxies (abp generate-proxy -t js), feeding a proxy into abp.libs.datatables.createAjax with normalizeConfiguration / rowAction / dataFormat, and the page abp.* APIs — abp.auth.isGranted, abp.localization.localize, abp.setting/abp.features, abp.message/abp.notify, abp.ui.block/setBusy, abp.event, abp.ajax. DO NOT USE FOR: C# typed HTTP client proxies (use consume-remote-services), running the proxy-generation CLI itself / its flags (use abp-cli-commands), exposing app services as HTTP APIs on the server (use expose-http-apis), general MVC page/modal/tag-helper/bundling work (use mvc-razor-ui).
license
MIT
# Use ABP JavaScript APIs in MVC / Razor Pages Call your server-side application services from browser JavaScript through ABP's **JavaScript API Client Proxies** instead of hand-writing `$.ajax`. A proxy function **corresponds to** the C# method — ABP drops the `Async` suffix and camelCases the name, DTOs are serialized to JSON, and the call returns a jQuery Deferred/Promise (with a trailing `ajaxParams` option), so `acme.bookStore.authors.author.getList({ maxResultCount: 10 })` maps to `IAuthorAppService.GetListAsync(...)`. This skill also covers wiring a proxy into ABP's DataTables adapter, and the rest of the client-side `abp.*` API surface (auth, localization, settings/features, messages, busy/block, events) that page code uses alongside the proxies. ## When to Use - You need to call an app service (`getList`, `get`, `create`, `update`, `delete`, …) from page JS. - You want a server-bound data grid using `abp-table` + DataTables. - You are deciding between the runtime (dynamic) and build-time (static) proxy. ## Two proxy systems Both give the identical `acme.bookStore.authors.author.get(...)` calling surface; they differ in *when* the proxy JS is produced. - **Dynamic (default):** generated at runtime. The layout automatically pulls the script from the `/Abp/ServiceProxyScript` endpoint. Easiest dev experience — nothing to regenerate when the API changes. Visit `/Abp/ServiceProxyScript` in the browser to see the emitted functions. - **Static:** generated at development time with `abp generate-proxy -t js -u https://localhost:53929/` (server must be running). Slight runtime perf gain because the API definition isn't fetched at runtime, but you must **re-generate whenever the API changes**. Output lands under `ClientProxies` (e.g. `app-proxy.js`); import it with `<abp-script src="/client-proxies/app-proxy.js"/>`. ### Static requires disabling the dynamic proxy for that module Otherwise both proxies register and you get duplicate/racing definitions. In the module's `ConfigureServices`: ```csharp Configure<DynamicJavaScriptProxyOptions>(options => { options.DisableModule("app"); }); ``` `"app"` is the main application; for a module, pass that module's name. `abp generate-proxy` also assumes `app` as the module name unless you pass `-m` / `--module`. ## Calling a proxy ```js acme.bookStore.authors.author.getList({ maxResultCount: 10 }) .then(function (result) { console.log(result.items); }); acme.bookStore.authors.author.delete('7245a066-5457-4941-8aa7-3004778775f0') .then(function () { abp.notify.info('Successfully deleted!'); }); ``` - Proxy functions run on `abp.ajax` under the hood, so you get **automatic error handling** for free. - **Return value is a jQuery Deferred object** — chain `.then` (result), `.catch` (error), `.always` (finally). It is not a raw value. - **The LAST argument is always `ajaxParams`, not a business parameter.** It's an object that overrides the underlying AJAX options — easy to mistake for a real method arg: ```js acme.bookStore.authors.author .delete('7245a066-5457-4941-8aa7-3004778775f0', { timeout: 10000, headers: { 'X-Demo-Header': 'value' } }) .then(function () { abp.notify.info('Successfully deleted!'); }); ``` `ajaxParams` accepts [jQuery.ajax](https://api.jquery.com/jQuery.ajax/) options — but don't override the HTTP method, URL, content type, or data type the proxy generates (e.g. don't turn a `DELETE` into a `POST` or force an `xml` response); use it for `timeout`, extra headers, `beforeSend`, and similar. ## DataTables integration The startup templates ship [DataTables.Net](https://datatables.net/) pre-installed and bundled. Add a table and call `.DataTable(...)` on it, wrapping the config in ABP's normalizer and feeding the proxy through ABP's AJAX adapter: ```html <abp-table striped-rows="true" id="BooksTable"></abp-table> ``` ```js var dataTable = $('#BooksTable').DataTable( abp.libs.datatables.normalizeConfiguration({ serverSide: true, paging: true, order: [[1, "asc"]], searching: false, ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList), columnDefs: [ { title: l('Actions'), rowAction: { items: [ { text: l('Edit'), action: function (data) { /* ... */ } } ] } }, { title: l('Name'), data: "name" }, { title: l('CreationTime'), data: "creationTime", dataFormat: 'datetime' }, { title: l('Price'), data: "price" } ] }) ); ``` ### `createAjax` — the adapter (do not skip it) DataTables and ABP use **different request/response shapes** for paging and sorting. `abp.libs.datatables.createAjax(proxyFn)` translates between them and works with the proxy system. **Passing the proxy function straight into DataTables' `ajax` is NOT equivalent** — the paging/sorting params and the `{ items, totalCount }` response won't line up. Customize request params and/or response with the optional 2nd/3rd args: ```js var inputAction = function (requestData, dataTableSettings) { return { id: $('#Id').val(), name: $('#Name').val() }; }; var responseCallback = function (result) { return { recordsTotal: result.totalCount, recordsFiltered: result.totalCount, data: result.items }; }; ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList, inputAction, responseCallback) ``` If you only need to add fixed request params, pass a plain object as the 2nd arg: ```js ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList, { id: $('#Id').val() }) ``` ### ABP extensions to the config (not native DataTables) - **`normalizeConfiguration`** — sets `scrollX` (default `true` here), fills in column `targets` indexes, and sets `language` to localize the table. Defaults come from `abp.libs.datatables.defaultConfigurations` (`scrollX`, `dom`, `language`), which you can override. - **`rowAction`** — a column-def option that renders a per-row actions dropdown. Each `items[]` entry supports: `text`, `action` (receives `data` with `data.record` = row object, `data.table` = DataTables instance), `confirmMessage` (function returning a confirmation string), `visible` (bool or function → bool; commonly `abp.auth.isGranted('BookStore.Books.Delete')`), `enabled` (function → bool), `iconClass`, `displayNameHtml`. If no item is visible, the actions column isn't rendered. - **`dataFormat`** — column option for built-in rendering without a custom `render`: `boolean` (check/times icon), `date`, `datetime`. Register new formats via `abp.libs.datatables.defaultRenderers['name'] = function(value){ ... }`. Reload after a mutating action: ```js action: function (data) { acme.bookStore.books.book.delete(data.record.id) .then(function () { abp.notify.info("Successfully deleted!"); data.table.ajax.reload(); }); } ``` ## Browser JS APIs (beyond proxies) Alongside the service proxies, ABP injects a client-side `abp.*` object into every page. These values **mirror server-side configuration** — permissions, localization, settings, features, and the current user are resolved on the server and serialized into the page (via the application configuration), so on the client you read them **synchronously, no request needed**. **Auth (permissions/policies)** — against the current user's granted policies: ```js if (abp.auth.isGranted('BookStore.Books.Delete')) { /* ... */ } abp.auth.isAnyGranted('A', 'B'); // any granted abp.auth.areAllGranted('A', 'B'); // all granted ``` **Current user** — a plain object; check `isAuthenticated` first (anonymous → fields `null`, `roles` `[]`): ```js if (abp.currentUser.isAuthenticated) { console.log(abp.currentUser.userName); } // id, tenantId, userName, name, surName, email, roles, ... ``` **Localization** — reuses the server-side resources; an unlocalized key returns the key itself: ```js var res = abp.localization.getResource('BookStore'); res('HelloWorld'); // localize res('WelcomeMessage', 'John'); // with {0} args abp.localization.localize('HelloWorld', 'BookStore'); // shortcut (key, resource) ``` **Settings & features** — only those whose server definition allows client visibility are present: ```js abp.setting.get('...'); abp.setting.getInt('...'); abp.setting.getBoolean('...'); abp.features.isEnabled('...'); abp.features.get('...'); abp.globalFeatures.isEnabled('...'); ``` **AJAX (low-level)** — prefer proxies; `abp.ajax` wraps `$.ajax`, returns a promise, auto-handles+localizes errors, and auto-adds the anti-forgery (CSRF) token. Pass `abpHandleError: false` to opt out of the automatic error UI; a 401 auto-redirects to login. **Messages (blocking) & notifications (toasts)** — `confirm`/`prompt` return **promises**: ```js abp.message.confirm('Delete the "admin" role?') .then(function (confirmed) { if (confirmed) { /* ... */ } }); abp.message.success(msg, title); // + info / warn / error abp.notify.info(msg, title); // auto-dismissing toast (+ success / warn / error) ``` **UI busy / block**, **events**, **DOM hooks**: ```js abp.ui.block('#MySection'); abp.ui.unblock('#MySection'); abp.ui.setBusy('#MySection'); abp.event.on('basketUpdated', fn); abp.event.trigger('basketUpdated', basket); abp.event.off('basketUpdated', fn); abp.dom.onNodeAdded(function (args) { /* init elements added after page load, e.g. via AJAX */ }); ``` `abp.event` is **browser-only** pub/sub (unrelated to the server local/distributed event bus). `abp.dom` (backed by `MutationObserver`) fires for nodes added later — use it to initialize AJAX-inserted HTML. `abp.ResourceLoader.loadScript(url)` / `loadStyle(url)` fetch a file **once**. ## Validation - **Dynamic proxy present:** open `/Abp/ServiceProxyScript` in the browser — your service's proxy functions appear there, and the object path (e.g. `acme.bookStore.books.book`) resolves in the JS console. - **Static proxy present:** the `ClientProxies` / `app-proxy.js` file exists, is imported via `<abp-script>`, and — after `DisableModule` — the console shows exactly one definition of the proxy object (no dynamic one racing it). - **Table works:** the grid pages/sorts server-side (network requests carry the translated paging/sort params) and the response `items` render; row actions run their `action` and `data.table.ajax.reload()` refreshes the grid. ## Common Pitfalls - **Treating `ajaxParams` as a method parameter** — the trailing object overrides AJAX options; passing a business value there silently does nothing useful. - **Awaiting the return as a plain value** — it's a jQuery Deferred; use `.then` / `.catch` / `.always`. - **Enabling static proxies without `DisableModule`** — leaves the dynamic proxy live too, so both register. Disable the module in `DynamicJavaScriptProxyOptions`. - **Forgetting to re-run `abp generate-proxy` after changing the API** — static proxies are frozen at generation time; dynamic ones update automatically. - **Passing the proxy straight into DataTables `ajax`** — skips `createAjax`, so ABP's paging/sorting/response shape (`items`/`totalCount`) never gets adapted. Always wrap with `abp.libs.datatables.createAjax`. - **Assuming `rowAction` / `dataFormat` / `normalizeConfiguration` are DataTables features** — they are ABP additions; they exist only when you go through ABP's adapter.
عرض على GitHub