diff --git a/UPGRADE-0.9.md b/UPGRADE-0.9.md new file mode 100644 index 00000000..83b25554 --- /dev/null +++ b/UPGRADE-0.9.md @@ -0,0 +1,26 @@ +# Upgrade to 0.9 + +## All fields have `.template()` + +All field types now 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: + +```js +listview.fields([ + nga.field('amount', 'number') + .format('$0,000.00') + .template('') +]); +``` + +This removes the need for [custom types](doc/Custom-types.md) in a vast majority of cases. + +The `template` *field type* is deprecated and will be removed in future versions. Simply use the default field type together with the `template()` method instead: + +```js +// before +nga.field('name', 'template').template('{{ entry.values.name }}') +// now +nga.field('name').template('{{ entry.values.name }}') +``` diff --git a/doc/Configuration-reference.md b/doc/Configuration-reference.md index 4cc5e55e..ba12ab07 100644 --- a/doc/Configuration-reference.md +++ b/doc/Configuration-reference.md @@ -48,6 +48,7 @@ application |-transform |-attributes |-cssClasses + |-template |-validation |-editable ``` @@ -283,10 +284,10 @@ Add filters to the list. Each field maps a property in the API endpoint result. nga.field('q').label('Search').pinned(true) ]); - Filter fields can be of any type, including `reference` and `template`. This allows to define custom filters with ease. + Filter fields can be of any type, including `reference`. This allows to define custom filters with ease. listView.filters([ - nga.field('q', 'template').label('') + nga.field('q').label('') .template('
'), ]); @@ -379,12 +380,11 @@ A field is the representation of a property of an entity. * [`reference` Field Type](#reference-field-type) * [`referenced_list` Field Type](#referenced_list-field-type) * [`reference_many` Field Type](#reference_many-field-type) -* [`template` Field Type](#template-field-type) ### General Field Settings * `nga.field(name, type)` -Create a new field of the given type. Default type is 'string', so you can omit it. Bundled types include `string`, `text`, `wysiwyg`, `password`, `email`, `date`, `datetime`, `number`, `float`, `boolean`, `choice`, `choices`, `json`, `file`, `reference`, `reference_list`, `reference_many`, and `template`. +Create a new field of the given type. Default type is 'string', so you can omit it. Bundled types include `string`, `text`, `wysiwyg`, `password`, `email`, `date`, `datetime`, `number`, `float`, `boolean`, `choice`, `choices`, `json`, `file`, `reference`, `reference_list`, and `reference_many`. * `label(string label)` Define the label of the field. Defaults to the uppercased field name. @@ -466,6 +466,25 @@ A list of CSS classes to be added to the corresponding field. If you provide a f return 'my-custom-css-class-for-list-header'; }); +* `template(String|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('') + ]); + + The template scope exposes the following variables: + + - `value`, `field`, `entry`, `entity`, and `datastore` in `listView` and `showView` + - `value`, `field`, `values`, and `datastore` in filters + - `value`, `field`, `entry`, `entity`, `form`, and `datastore` in `editionView` and `creationView` + + 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](../src/javascripts/ng-admin/crud/column), [field](../src/javascripts/ng-admin/crud/field), and [fieldView](../src/javascripts/ng-admin/crud/fieldView) directories in ng-admin source. + * `defaultValue(*)` Define the default value of the field in the creation form. @@ -765,8 +784,3 @@ If set to false, all references (in the limit of `perPage` parameter) would be r }) .perPage(10) // limit the number of results to 10 ]); - -### `template` Field Type - -* `template(*)` -Define the template to be displayed for fields of type `template` (can be a string or a function). diff --git a/doc/Custom-pages.md b/doc/Custom-pages.md index f5a35393..36684872 100644 --- a/doc/Custom-pages.md +++ b/doc/Custom-pages.md @@ -72,12 +72,12 @@ myApp.directive('sendEmail', ['$location', function ($location) { ## Including the Custom Directive in a Template Field -Now the new directive is ready to be used inside a ng-admin field of type 'template': +Now the new directive is ready to be used inside a ng-admin field using 'template()': ```js post.showView().fields([ // ... - nga.field('custom_action', 'template') + nga.field('custom_action') .label('') .template('') ]); diff --git a/doc/Getting-started.md b/doc/Getting-started.md index 7dd85af2..b3df2c78 100644 --- a/doc/Getting-started.md +++ b/doc/Getting-started.md @@ -504,10 +504,10 @@ Browse to the posts list, and you will see the full-text filter displayed on the The full-text search isn't looking very good. Usually, a full-text filter widget has no label, a placeholder simply saying "Search", and a magnifying glass icon. How can you turn the current full-text input into that UI? -Fortunately, ng-admin fields can benefit from the power of Angular.Js directives. Using the 'template' field type, you can specify the HTML template to use for rendering the field. And you can use any directive already registered in that HTML. Update the `nga.field('q')` definition as follows: +Fortunately, ng-admin fields can benefit from the power of Angular.js directives. Using the `template()` field method, you can specify the HTML template to use for rendering the field. And you can use any directive already registered in that HTML. Update the `nga.field('q')` definition as follows: ```js - nga.field('q', 'template') + nga.field('q') .label('') .pinned(true) .template('
'), @@ -653,7 +653,7 @@ myApp.config(['NgAdminConfigurationProvider', function (nga) { .listActions(['show']) .batchActions([]) .filters([ - nga.field('q', 'template') + nga.field('q') .label('') .pinned(true) .template('
'), diff --git a/doc/Theming.md b/doc/Theming.md index c5db8320..7f66ab2b 100644 --- a/doc/Theming.md +++ b/doc/Theming.md @@ -32,9 +32,31 @@ myEntity.listView().fields([ ]); ``` +## Customizing The Template For A Given Field + +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: + +```js +listview.fields([ + nga.field('amount', 'number') + .format('$0,000.00') + .template('') +]); +``` + +The template scope exposes the following variables: + +* `value`, `field`, `entry`, `entity`, and `datastore` in `listView` and `showView` +* `value`, `field`, `values`, and `datastore` in filters +* `value`, `field`, `entry`, `entity`, `form`, and `datastore` in `editionView` and `creationView` + +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](../src/javascripts/ng-admin/crud/column), [field](../src/javascripts/ng-admin/crud/field), and [fieldView](../src/javascripts/ng-admin/crud/fieldView) directories in ng-admin source. + ## Customizing Directives Templates -Using Angular's [`$provide`](https://docs.angularjs.org/api/auto/service/$provide) service, and the ability to decorate another provider, you can customize the templates of all the directives used by ng-admin. Here is an example of customization of the 'text' input field customization: +Using Angular's [`$provide`](https://docs.angularjs.org/api/auto/service/$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: ```js var myApp = angular.module('myApp', ['ng-admin']); diff --git a/examples/blog/config.js b/examples/blog/config.js index d5e5e25a..11488953 100644 --- a/examples/blog/config.js +++ b/examples/blog/config.js @@ -163,7 +163,7 @@ .sortField('created_at') .sortDir('DESC') .listActions(['edit']), - nga.field('', 'template').label('') + nga.field('').label('') .template('') ]); @@ -171,8 +171,7 @@ .fields([ nga.field('id'), post.editionView().fields(), // reuse fields from another view in another order - nga.field('custom_action', 'template') - .label('') + nga.field('custom_action').label('') .template('') ]); @@ -197,7 +196,7 @@ .singleApiCall(ids => { return {'id': ids }}) ]) .filters([ - nga.field('q', 'template') + nga.field('q') .label('') .pinned(true) .template('
'), @@ -238,9 +237,9 @@ comment.editionView() .fields(comment.creationView().fields()) - .fields([nga.field(null, 'template') - .label('') - .template('') // template() can take a function or a string + .fields([ + nga.field('').label('') + .template('') // template() can take a function or a string ]); comment.deletionView() @@ -260,14 +259,15 @@ return 'bg-warning text-center'; } }), - nga.field('custom', 'template') + nga.field('custom') .label('Upper name') .template('{{ entry.values.name.toUpperCase() }}') .cssClasses('hidden-xs') ]) .filters([ - nga.field('published', 'template') + nga.field('published') .label('Not yet published') + .template(' ') .defaultValue(false) ]) .batchActions([]) // disable checkbox column and batch delete diff --git a/package.json b/package.json index 475e9037..f4526ec3 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "url": "git://github.com/marmelab/ng-admin.git" }, "devDependencies": { - "admin-config": "^0.3.2", + "admin-config": "^0.4.0", "angular": "~1.3.15", "angular-bootstrap": "^0.12.0", "angular-mocks": "1.3.14", diff --git a/src/javascripts/ng-admin/Crud/CrudModule.js b/src/javascripts/ng-admin/Crud/CrudModule.js index c45d0729..1f4db9b9 100644 --- a/src/javascripts/ng-admin/Crud/CrudModule.js +++ b/src/javascripts/ng-admin/Crud/CrudModule.js @@ -46,6 +46,7 @@ CrudModule.directive('maDatagridPagination', require('./list/maDatagridPaginatio CrudModule.directive('maDatagridInfinitePagination', require('./list/maDatagridInfinitePagination')); CrudModule.directive('maDatagridItemSelector', require('./list/maDatagridItemSelector')); CrudModule.directive('maDatagridMultiSelector', require('./list/maDatagridMultiSelector')); +CrudModule.directive('maFilterForm', require('./filter/maFilterForm')); CrudModule.directive('maFilter', require('./filter/maFilter')); CrudModule.directive('maFilterButton', require('./filter/maFilterButton')); diff --git a/src/javascripts/ng-admin/Crud/column/maColumn.js b/src/javascripts/ng-admin/Crud/column/maColumn.js index 18efefb4..7423cc5c 100644 --- a/src/javascripts/ng-admin/Crud/column/maColumn.js +++ b/src/javascripts/ng-admin/Crud/column/maColumn.js @@ -1,77 +1,77 @@ -/*global define*/ +function maColumn($state, $anchorScroll, $compile, Configuration, FieldViewConfiguration) { -define(function (require) { - 'use strict'; - - function maColumn($state, $anchorScroll, $compile, Configuration, FieldViewConfiguration) { - - function getDetailLinkRouteName(field, entity) { - if (entity.isReadOnly) { - return entity.showView().enabled ? 'show' : false; - } - if (field.detailLinkRoute() == 'edit' && entity.editionView().enabled) { - return 'edit'; - } + function getDetailLinkRouteName(field, entity) { + if (entity.isReadOnly) { return entity.showView().enabled ? 'show' : false; } + if (field.detailLinkRoute() == 'edit' && entity.editionView().enabled) { + return 'edit'; + } + return entity.showView().enabled ? 'show' : false; + } - function isDetailLink(field, entity) { - if (field.isDetailLink() === false) { + function isDetailLink(field, entity) { + if (field.isDetailLink() === false) { + return false; + } + if (field.type() == 'reference' || field.type() == 'reference_many') { + var relatedEntity = Configuration().getEntity(field.targetEntity().name()); + if (!relatedEntity) { return false; } - if (field.type() == 'reference' || field.type() == 'reference_many') { - var relatedEntity = Configuration().getEntity(field.targetEntity().name()); - if (!relatedEntity) { - return false; - } - return getDetailLinkRouteName(field, relatedEntity) !== false; - } - return getDetailLinkRouteName(field, entity) !== false; + return getDetailLinkRouteName(field, relatedEntity) !== false; } + return getDetailLinkRouteName(field, entity) !== false; + } - return { - restrict: 'E', - scope: { - field: '&', - entry: '&', - entity: '&', - datastore: '&' - }, - link: function(scope, element) { - scope.datastore = scope.datastore(); - scope.field = scope.field(); - scope.entry = scope.entry(); - scope.entity = scope.entity(); - var type = scope.field.type(); + return { + restrict: 'E', + scope: { + field: '&', + entry: '&', + entity: '&', + datastore: '&' + }, + link: function(scope, element) { + scope.datastore = scope.datastore(); + scope.field = scope.field(); + scope.entry = scope.entry(); + scope.value = scope.entry.values[scope.field.name()]; + scope.entity = scope.entity(); + let customTemplate = scope.field.getTemplateValue(scope.entry); + if (customTemplate) { + element.append(customTemplate); + } else { + let type = scope.field.type(); if (isDetailLink(scope.field, scope.entity)) { element.append(FieldViewConfiguration[type].getLinkWidget()); } else { element.append(FieldViewConfiguration[type].getReadWidget()); } - $compile(element.contents())(scope); - scope.gotoDetail = function () { - var route = getDetailLinkRouteName(scope.field, scope.entity); - $state.go($state.get(route), - angular.extend({}, $state.params, { - entity: scope.entry.entityName, - id: scope.entry.identifierValue - })); - }; - scope.gotoReference = function () { - var referenceEntity = scope.field.targetEntity().name(); - var relatedEntity = Configuration().getEntity(referenceEntity); - var referenceId = scope.entry.values[scope.field.name()]; - var route = getDetailLinkRouteName(scope.field, relatedEntity); - $state.go($state.get(route), { - entity: referenceEntity, - id: referenceId - }); - }; } - }; - } + $compile(element.contents())(scope); + scope.gotoDetail = function () { + var route = getDetailLinkRouteName(scope.field, scope.entity); + $state.go($state.get(route), + angular.extend({}, $state.params, { + entity: scope.entry.entityName, + id: scope.entry.identifierValue + })); + }; + scope.gotoReference = function () { + var referenceEntity = scope.field.targetEntity().name(); + var relatedEntity = Configuration().getEntity(referenceEntity); + var referenceId = scope.entry.values[scope.field.name()]; + var route = getDetailLinkRouteName(scope.field, relatedEntity); + $state.go($state.get(route), { + entity: referenceEntity, + id: referenceId + }); + }; + } + }; +} - maColumn.$inject = ['$state', '$anchorScroll', '$compile', 'NgAdminConfiguration', 'FieldViewConfiguration']; +maColumn.$inject = ['$state', '$anchorScroll', '$compile', 'NgAdminConfiguration', 'FieldViewConfiguration']; - return maColumn; -}); +module.exports = maColumn; diff --git a/src/javascripts/ng-admin/Crud/field/maField.js b/src/javascripts/ng-admin/Crud/field/maField.js index 4f6fdc11..6f32477e 100644 --- a/src/javascripts/ng-admin/Crud/field/maField.js +++ b/src/javascripts/ng-admin/Crud/field/maField.js @@ -1,41 +1,20 @@ -var _ = require('lodash'); - -function maField(FieldViewConfiguration) { - var writeWidgetTypes = _(FieldViewConfiguration) - .map(function(fieldView, field) { - return '' + fieldView.getWriteWidget() +''; - }).join(''); - var template = -'
' + -'' + -'
' + - writeWidgetTypes + - '' + -'
' + -'
' + - '

' + - '' + - '

' + -'
' + -'
'; +function maField(FieldViewConfiguration, $compile) { return { restrict: 'E', scope: { field: '&', entry: '=', + value: '=', entity: '&', form: '&', datastore: '&' }, - link: function(scope) { + link: function(scope, element) { scope.field = scope.field(); scope.type = scope.field.type(); scope.entity = scope.entity(); scope.form = scope.form(); scope.datastore = scope.datastore(); - scope.getClassesForField = function(field, entry) { return 'ng-admin-field-' + field.name().replace('.', '_') + ' ' + 'ng-admin-type-' + field.type() + ' ' + (field.getCssClasses(entry) || 'col-sm-10 col-md-8 col-lg-7'); }; @@ -67,11 +46,36 @@ function maField(FieldViewConfiguration) { } }; - }, - template: template + var fieldTemplate; + if (scope.field.editable()) { + fieldTemplate = +`
+ ${scope.field.getTemplateValue(scope.entry) || FieldViewConfiguration[scope.type].getWriteWidget()} + +
`; + } else { + fieldTemplate = +`
+

+ +

+
`; + } + + const template = +`
+ + ${fieldTemplate} +
`; + + element.append(template); + $compile(element.contents())(scope); + } }; } -maField.$inject = ['FieldViewConfiguration']; +maField.$inject = ['FieldViewConfiguration', '$compile']; module.exports = maField; diff --git a/src/javascripts/ng-admin/Crud/field/maTemplateField.js b/src/javascripts/ng-admin/Crud/field/maTemplateField.js index 0c3cbaba..5b3123de 100644 --- a/src/javascripts/ng-admin/Crud/field/maTemplateField.js +++ b/src/javascripts/ng-admin/Crud/field/maTemplateField.js @@ -10,7 +10,8 @@ define(function (require) { field: '&', entry: '&', entity: '&', - value: '=' + value: '=', + values: '=' }, link: function(scope) { scope.field = scope.field(); diff --git a/src/javascripts/ng-admin/Crud/fieldView/BooleanFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/BooleanFieldView.js index f00071d8..9089098b 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/BooleanFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/BooleanFieldView.js @@ -1,9 +1,9 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => ``, + getFilterWidget: () => ``, getWriteWidget: () => `
- - + +
` } diff --git a/src/javascripts/ng-admin/Crud/fieldView/ChoiceFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/ChoiceFieldView.js index 3ec5bbfc..9bf3cca3 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/ChoiceFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/ChoiceFieldView.js @@ -1,6 +1,6 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => '', - getWriteWidget: () => '' + getFilterWidget: () => '', + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/ChoicesFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/ChoicesFieldView.js index 113d17a8..634a0bc3 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/ChoicesFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/ChoicesFieldView.js @@ -1,6 +1,6 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => '', - getWriteWidget: () => '' + getFilterWidget: () => '', + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/DateFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/DateFieldView.js index 9a2c0bbe..5b1932db 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/DateFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/DateFieldView.js @@ -1,6 +1,6 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => '', - getWriteWidget: () => '
' + getFilterWidget: () => '', + getWriteWidget: () => '
' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/DateTimeFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/DateTimeFieldView.js index 7140a06a..552367f6 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/DateTimeFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/DateTimeFieldView.js @@ -1,6 +1,6 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => '', - getWriteWidget: () => '
' + getFilterWidget: () => '', + getWriteWidget: () => '
' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/EmailFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/EmailFieldView.js index f9c9dcdd..46137fba 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/EmailFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/EmailFieldView.js @@ -1,6 +1,6 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => '', - getWriteWidget: () => '' + getFilterWidget: () => '', + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/FileFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/FileFieldView.js index 455789d9..55bdf518 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/FileFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/FileFieldView.js @@ -2,5 +2,5 @@ module.exports = { getReadWidget: () => 'error: cannot display file field as readable', getLinkWidget: () => 'error: cannot display file field as linkable', getFilterWidget: () => 'error: cannot display file field as filter', - getWriteWidget: () => '' + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/FloatFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/FloatFieldView.js index fb41ef92..23a1f350 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/FloatFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/FloatFieldView.js @@ -1,6 +1,6 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => '', - getWriteWidget: () => '' + getFilterWidget: () => '', + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/JsonFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/JsonFieldView.js index 7b076748..c6bd01c8 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/JsonFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/JsonFieldView.js @@ -1,6 +1,6 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => 'error: cannot display a json field as linkable', - getFilterWidget: () => '', - getWriteWidget: () => '' + getFilterWidget: () => '', + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/NumberFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/NumberFieldView.js index 02a7162f..c8c0a919 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/NumberFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/NumberFieldView.js @@ -1,6 +1,6 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => '', - getWriteWidget: () => '' + getFilterWidget: () => '', + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/PasswordFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/PasswordFieldView.js index 58c2d439..9240604a 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/PasswordFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/PasswordFieldView.js @@ -2,5 +2,5 @@ module.exports = { getReadWidget: () => 'error: cannot display password field as readable', getLinkWidget: () => 'error: cannot display password field as linkable', getFilterWidget: () => 'error: cannot display password field as filter', - getWriteWidget: () => '' + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/ReferenceFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/ReferenceFieldView.js index 0985c743..dd023cd8 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/ReferenceFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/ReferenceFieldView.js @@ -1,6 +1,6 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => '', - getWriteWidget: () => '' + getFilterWidget: () => '', + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/ReferenceManyFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/ReferenceManyFieldView.js index 81a79a5b..a34aa266 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/ReferenceManyFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/ReferenceManyFieldView.js @@ -1,6 +1,6 @@ module.exports = { getReadWidget: () => '', - getLinkWidget: () => '', - getFilterWidget: () => '', - getWriteWidget: () => '' + getLinkWidget: () => '', + getFilterWidget: () => '', + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/StringFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/StringFieldView.js index e53818d7..194e0e2e 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/StringFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/StringFieldView.js @@ -1,6 +1,6 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => '', - getWriteWidget: () => '' + getFilterWidget: () => '', + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/TemplateFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/TemplateFieldView.js index 78fc0357..5ee3647d 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/TemplateFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/TemplateFieldView.js @@ -1,6 +1,6 @@ module.exports = { getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => '', - getWriteWidget: () => '' + getFilterWidget: () => '', + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/TextFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/TextFieldView.js index 24036046..730b6d9e 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/TextFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/TextFieldView.js @@ -1,6 +1,6 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => '', - getWriteWidget: () => '' + getFilterWidget: () => '', + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/fieldView/WysiwygFieldView.js b/src/javascripts/ng-admin/Crud/fieldView/WysiwygFieldView.js index 97fbe5f6..8b099fd8 100644 --- a/src/javascripts/ng-admin/Crud/fieldView/WysiwygFieldView.js +++ b/src/javascripts/ng-admin/Crud/fieldView/WysiwygFieldView.js @@ -1,6 +1,6 @@ module.exports = { - getReadWidget: () => '', + getReadWidget: () => '', getLinkWidget: () => '' + module.exports.getReadWidget() + '', - getFilterWidget: () => '', - getWriteWidget: () => '' + getFilterWidget: () => '', + getWriteWidget: () => '' }; diff --git a/src/javascripts/ng-admin/Crud/filter/maFilter.js b/src/javascripts/ng-admin/Crud/filter/maFilter.js index dd6ddea2..981dbf44 100644 --- a/src/javascripts/ng-admin/Crud/filter/maFilter.js +++ b/src/javascripts/ng-admin/Crud/filter/maFilter.js @@ -1,48 +1,20 @@ -var _ = require('lodash'); - -function maFilterDirective(FieldViewConfiguration) { - 'use strict'; - - var filterWidgetTypes = _(FieldViewConfiguration) - .map(function(fieldView, field) { - return '' + fieldView.getFilterWidget() +''; - }).join(''); - - var template = ` -
-
-
-
- -
- -
- ${filterWidgetTypes} -
-
-
-
- `; - +function maFilterDirective(FieldViewConfiguration, $compile) { return { restrict: 'E', - template: template, scope: { - filters: '=', + field: '=', datastore: '&', values: '=', - removeFilter: '&' + value: '=' }, - link: function(scope) { + link: function(scope, element) { scope.datastore = scope.datastore(); - scope.removeFilter = scope.removeFilter(); - scope.shouldFilter = () => Object.keys(scope.filters).length; + element.append(scope.field.getTemplateValue(scope.values) || FieldViewConfiguration[scope.field.type()].getFilterWidget()); + $compile(element.contents())(scope); } }; } -maFilterDirective.$inject = ['FieldViewConfiguration']; +maFilterDirective.$inject = ['FieldViewConfiguration', '$compile']; module.exports = maFilterDirective; diff --git a/src/javascripts/ng-admin/Crud/filter/maFilterForm.js b/src/javascripts/ng-admin/Crud/filter/maFilterForm.js new file mode 100644 index 00000000..207447f1 --- /dev/null +++ b/src/javascripts/ng-admin/Crud/filter/maFilterForm.js @@ -0,0 +1,36 @@ +function maFilterFormDirective() { + return { + restrict: 'E', + scope: { + filters: '=', + datastore: '&', + values: '=', + removeFilter: '&' + }, + link: function(scope, element) { + scope.datastore = scope.datastore(); + scope.removeFilter = scope.removeFilter(); + scope.shouldFilter = () => Object.keys(scope.filters).length; + }, + template: +`
+
+
+
+ +
+ +
+ +
+
+
+
` + }; +} + +maFilterFormDirective.$inject = []; + +module.exports = maFilterFormDirective; diff --git a/src/javascripts/ng-admin/Crud/form/create.html b/src/javascripts/ng-admin/Crud/form/create.html index 6d734503..88c53705 100644 --- a/src/javascripts/ng-admin/Crud/form/create.html +++ b/src/javascripts/ng-admin/Crud/form/create.html @@ -15,7 +15,7 @@

- +
diff --git a/src/javascripts/ng-admin/Crud/form/edit.html b/src/javascripts/ng-admin/Crud/form/edit.html index e3bdab5d..fc8a0a93 100644 --- a/src/javascripts/ng-admin/Crud/form/edit.html +++ b/src/javascripts/ng-admin/Crud/form/edit.html @@ -16,7 +16,7 @@

- +
diff --git a/src/javascripts/ng-admin/Crud/list/listLayout.html b/src/javascripts/ng-admin/Crud/list/listLayout.html index bb56236a..d4cfd471 100644 --- a/src/javascripts/ng-admin/Crud/list/listLayout.html +++ b/src/javascripts/ng-admin/Crud/list/listLayout.html @@ -15,7 +15,7 @@

{{ ::llCtrl.view.description() }}

- +

diff --git a/src/javascripts/test/e2e/validationSpec.js b/src/javascripts/test/e2e/validationSpec.js index 19219296..4e3e85c2 100644 --- a/src/javascripts/test/e2e/validationSpec.js +++ b/src/javascripts/test/e2e/validationSpec.js @@ -9,16 +9,16 @@ describe('Form validation', function () { it('should not display any validation status before entering data', function () { var enclosingDiv = element.all(by.css('.has-feedback')).first(); - expect(enclosingDiv.getAttribute('class')).toBe('has-feedback'); + expect(enclosingDiv.getAttribute('class')).toBe('has-feedback ng-scope'); }); it('should display correct validation status once data is entered', function () { var input = element.all(by.css('input')).first(); var enclosingDiv = element.all(by.css('.has-feedback')).first(); input.sendKeys('ra'); - expect(enclosingDiv.getAttribute('class')).toBe('has-feedback has-error'); + expect(enclosingDiv.getAttribute('class')).toBe('has-feedback ng-scope has-error'); input.sendKeys('rabisudbfij'); - expect(enclosingDiv.getAttribute('class')).toBe('has-feedback has-success'); + expect(enclosingDiv.getAttribute('class')).toBe('has-feedback ng-scope has-success'); }); }); @@ -29,7 +29,7 @@ describe('Form validation', function () { it('should not display any validation status before entering data', function () { var enclosingDiv = element.all(by.css('.has-feedback')).first(); - expect(enclosingDiv.getAttribute('class')).toBe('has-feedback'); + expect(enclosingDiv.getAttribute('class')).toBe('has-feedback ng-scope'); }); it('should display correct validation status once data is entered', function () { @@ -37,9 +37,9 @@ describe('Form validation', function () { var enclosingDiv = element.all(by.css('.has-feedback')).first(); input.clear(); input.sendKeys('ra'); - expect(enclosingDiv.getAttribute('class')).toBe('has-feedback has-error'); + expect(enclosingDiv.getAttribute('class')).toBe('has-feedback ng-scope has-error'); input.sendKeys('rabisudbfij'); - expect(enclosingDiv.getAttribute('class')).toBe('has-feedback has-success'); + expect(enclosingDiv.getAttribute('class')).toBe('has-feedback ng-scope has-success'); }); }); diff --git a/src/javascripts/test/unit/Crud/column/maColumnSpec.js b/src/javascripts/test/unit/Crud/column/maColumnSpec.js new file mode 100644 index 00000000..7ddddc07 --- /dev/null +++ b/src/javascripts/test/unit/Crud/column/maColumnSpec.js @@ -0,0 +1,42 @@ +/*global angular,inject,describe,it,expect,beforeEach*/ +describe('directive: ma-column', function () { + 'use strict'; + + var directive = require('../../../../ng-admin/Crud/column/maColumn'); + var Field = require('admin-config/lib/Field/Field'); + angular.module('testapp_Column', []) + .directive('maColumn', directive) + .service('FieldViewConfiguration', () => ({ string: { getReadWidget: () => 'DUMMY' } })) + .service('$state', () => ({})) + .service('$anchorScroll', () => ({})) + .service('NgAdminConfiguration', () => ({})); + + var $compile, + scope, + directiveUsage = ''; + + beforeEach(angular.mock.module('testapp_Column')); + + beforeEach(inject(function (_$compile_, _$rootScope_) { + $compile = _$compile_; + scope = _$rootScope_; + })); + + it("should render the ReadWidget from the fieldView Configuration for that type", function () { + scope.field = new Field('foo'); + scope.entry = { values: { foo: null } }; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + expect(element.html()).toContain('DUMMY'); + }); + + it("should render the Field template instead of the ReadWidget when set", function () { + scope.field = new Field('foo').template('YOPLA'); + scope.entry = { values: { foo: null } }; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + expect(element.html()).not.toContain('DUMMY'); + expect(element.html()).toContain('YOPLA'); + }); + +}); diff --git a/src/javascripts/test/unit/Crud/field/maFieldSpec.js b/src/javascripts/test/unit/Crud/field/maFieldSpec.js new file mode 100644 index 00000000..2cb2902e --- /dev/null +++ b/src/javascripts/test/unit/Crud/field/maFieldSpec.js @@ -0,0 +1,39 @@ +/*global angular,inject,describe,it,expect,beforeEach*/ +describe('directive: ma-field', function () { + 'use strict'; + + var directive = require('../../../../ng-admin/Crud/field/maField'); + var Field = require('admin-config/lib/Field/Field'); + angular.module('testapp_Field', []) + .directive('maField', directive) + .service('FieldViewConfiguration', () => ({ string: { getWriteWidget: () => 'DUMMY' } })); + + var $compile, + scope, + directiveUsage = ''; + + beforeEach(angular.mock.module('testapp_Field')); + + beforeEach(inject(function (_$compile_, _$rootScope_) { + $compile = _$compile_; + scope = _$rootScope_; + })); + + it("should render the WriteWidget from the fieldView Configuration for that type", function () { + scope.field = new Field('foo'); + scope.form = { foo: {} }; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + expect(element.html()).toContain('DUMMY'); + }); + + it("should render the Field template instead of the WriteWidget when set", function () { + scope.field = new Field('foo').template('YOPLA'); + scope.form = { foo: {} }; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + expect(element.html()).not.toContain('DUMMY'); + expect(element.html()).toContain('YOPLA'); + }); + +}); diff --git a/src/javascripts/test/unit/Crud/filter/maFilterSpec.js b/src/javascripts/test/unit/Crud/filter/maFilterSpec.js new file mode 100644 index 00000000..ffda8210 --- /dev/null +++ b/src/javascripts/test/unit/Crud/filter/maFilterSpec.js @@ -0,0 +1,37 @@ +/*global angular,inject,describe,it,expect,beforeEach*/ +describe('directive: ma-filter', function () { + 'use strict'; + + var directive = require('../../../../ng-admin/Crud/filter/maFilter'); + var Field = require('admin-config/lib/Field/Field'); + angular.module('testapp_Filter', []) + .directive('maFilter', directive) + .service('FieldViewConfiguration', () => ({ string: { getFilterWidget: () => 'DUMMY' } })); + + var $compile, + scope, + directiveUsage = ''; + + beforeEach(angular.mock.module('testapp_Filter')); + + beforeEach(inject(function (_$compile_, _$rootScope_) { + $compile = _$compile_; + scope = _$rootScope_; + })); + + it("should render the FilterWidget from the fieldView Configuration for that type", function () { + scope.field = new Field('foo'); + var element = $compile(directiveUsage)(scope); + scope.$digest(); + expect(element.html()).toContain('DUMMY'); + }); + + it("should render the Field template instead of the FilterWidget when set", function () { + scope.field = new Field('foo').template('YOPLA'); + var element = $compile(directiveUsage)(scope); + scope.$digest(); + expect(element.html()).not.toContain('DUMMY'); + expect(element.html()).toContain('YOPLA'); + }); + +});