You can override pretty much all the HTML generated by ng-admin, at different levels.
Ng-admin CSS is composed of styles from sb-admin, on top of Bootstrap 3. By using your own CSS in addition to the ng-admin CSS, you can customize the look and feel of every displayed component.
To ease styling, ng-admin adds custom classes for the entities and fields currently displayed, to allow a finer styling. For instance, the datagrid in the list view always uses a class named ng-admin-entity-[entityName]
. In the datagrid also, each header in the table uses a class named ng-admin-column-[fieldName]
, which should allow precise sizing of columns.
Don't hesitate to inspect the generated source to look for the right CSS selector.
To ease theming, you can add a custom class to your fields in every view, using the cssClasses()
method.
myEntity.listView().fields([
nga.field('title').cssClasses(['foo', 'bar'])
]);
cssClasses()
can optionally accept a function, to return a class depending on the current entry.
myEntity.listView().fields([
nga.field('title').cssClasses(function(entry) {
if (entry && entry.values && entry.values.publishDate >= Date.now()) {
return 'bg-success';
}
})
]);
The listView
calls cssClasses()
for each datagrid cell, and for the datagrid headers. There is no active 'entry' in headers, that's why you should always check that the entry
parameter is defined in a cssClasses
function.
All field types support the template()
method, which makes it easy to customize the look and feel of a particular field, without sacrificing the native features.
For instance, if you want to customize the appearance of a NumberField
according to its value:
listview.fields([
nga.field('amount', 'number')
.format('$0,000.00')
.template('<span ng-class="{ \'red\': value < 0 }"><ma-number-column field="::field" value="::entry.values[field.name()]"></ma-number-column></span>')
]);
The template scope exposes the following variables:
value
,field
,entry
,entity
, anddatastore
inlistView
andshowView
value
,field
,values
, anddatastore
in filtersvalue
,field
,entry
,entity
,form
, anddatastore
ineditionView
andcreationView
In showView
, editionView
, and creationView
, the template zone covers only the field itself - not the label. To force the template to replace the entire line (including the label), pass true
as second argument to the template()
call. This can be very useful to conditionally hide a field according to a property of the entry:
post.editionView()
.fields([
nga.field('category', 'choice')
.choices([
{ label: 'Tech', value: 'tech' },
{ label: 'Lifestyle', value: 'lifestyle' }
]),
nga.field('subcategory', 'choice')
.choices(function(entry) {
return subCategories.filter(function (c) {
return c.category === entry.values.category;
});
})
// display subcategory only if there is a category
.template('<ma-field ng-if="entry.values.category" field="::field" value="entry.values[field.name()]" entry="entry" entity="::entity" form="formController.form" datastore="::formController.dataStore"></ma-field>', true),
]);
Most of the time, template()
is used to customize the existing ng-admin directives (like ma-number-column>
in the previous example), for instance by decorating them. If you want to learn about these native directives, explore the column, field, and fieldView directories in ng-admin source.
Using Angular's $provide
service, and the ability to decorate another provider, you can customize the template of any directive used by ng-admin for your entire application. Here is an example of customization of the 'text' input field:
var myApp = angular.module('myApp', ['ng-admin']);
myApp.config(function(NgAdminConfigurationProvider, $provide) {
// Override textarea template
$provide.decorator('maTextFieldDirective', ['$delegate', function ($delegate) {
// You can modify directly the template
$delegate[0].template = angular.element($delegate[0].template).addClass('MyClass')[0].outerHTML;
// or use a templateURL (loaded from a file or a <script type="text/ng-template" id="string.html"></script> tag)
$delegate[0].template = '';
$delegate[0].templateUrl = 'string.html';
return $delegate;
}]);
// ...
});
For a given entity, each of the main views (list
, show
, create
, edit
, delete
) can use a custom HTML template instead of the default one. Just pass it to the template()
function on the view:
var myTemplate = require('text!./path/to/list.html');
var myEntity = nga.entity('foo_endpoint');
myEntity.listView().template(myTemplate);
// continue myEntity configuration
// ...
var admin = nga.application('My Application');
admin.addEntity(myEntity);
You can use the app.customTemplate()
method to customize the template of a given view across the entire application (for all entities). This method expects a function argument, which should return the template as a string.
var myTemplate = require('text!./path/to/list.html');
var admin = nga.application('My Application');
admin.customTemplate(function(viewName) {
if (viewName === 'ListView') {
return myTemplate;
}
})
If you want to use a custom template dashboard, you must define a custom dashboard configuration. This will give you access to the dashboard.template()
function:
var myTemplate = require('text!./path/to/dashboard.html');
var admin = nga.application('My Application');
admin.dashboard(nga.dashboard()
.template(mytemplate)
})
If you want to override the application header (for instance to add authentication status on the top bar), use the admin.header()
setter with a valid HTML content. Angular directives are executed in the context of the layout. For instance, to display a custom title and a right aligned link, use the following code:
var customHeaderTemplate =
'<div class="navbar-header">' +
'<a class="navbar-brand" href="#" ng-click="appController.displayHome()">' +
My Custom Title
'</a>' +
'</div>' +
'<p class="navbar-text navbar-right">' +
'<a href="https://github.com/marmelab/ng-admin/blob/master/examples/blog/config.js">' +
'<span class="glyphicon glyphicon-sunglasses"></span> View Source' +
'</a>' +
'</p>';
var admin = nga.application('My Application');
admin.header(customHeaderTemplate);
If the header is not enough, and you need to override the entire application layout use the admin.layout()
setter:
var myLayout = require('text!./path/to/layout.html');
var admin = nga.application('My Application');
admin.layout(myLayout);
The original layout can be found in src/javascripts/ng-admin/Main/view/layout.html.
You can use the app.errorMessage()
, entity.errorMessage()
or view.errorMessage()
methods to customize the message displayed when an error occurred with the API. These methods expect a function argument with the response as parameter, which should return the message as a string.
entity.errorMessage(function (response) {
return 'Global error: ' + response.status + '(' + response.data + ')';
});
If you want to override, patch or extend the way HTTP errors are handled by the ng-admin Http Error Service then you may use an Angular decorator, as per the below example:
Create a decorator, HttpErrorDecorator.js
// Change HTTP 403 error notification to display them as information
// and not errors ('humane-flatty-info' instead of 'humane-flatty-error')
export const HttpErrorDecorator = ($delegate, $translate, notification) => {
$delegate.handle403Error = error => {
$translate('STATE_FORBIDDEN_ERROR', {
message: error.data.message,
}).then(text => notification.log(text, {
addnCls: 'humane-flatty-info',
}));
throw error;
};
return $delegate;
}
HttpErrorDecorator.$inject = ['$delegate', '$translate', 'notification'];
export default HttpErrorDecorator;
Bind the decorator to your application:
myApp.decorator('HttpErrorService',require('HttpErrorDecorator'));