apostrophe-schemas
Advanced tools
Comparing version 0.5.20 to 0.5.21
65
index.js
@@ -325,3 +325,3 @@ var async = require('async'); | ||
self.converters.form.joinByOne = function(req, data, name, snippet, field, callback) { | ||
snippet[field.idField] = self._apos.sanitizeId(data[name]); | ||
snippet[field.idField] = self._apos.sanitizeId(data[field.idField]); | ||
return setImmediate(callback); | ||
@@ -336,49 +336,26 @@ }; | ||
self.converters.form.joinByArray = function(req, data, name, snippet, field, callback) { | ||
var input = data[name] || []; | ||
if (!Array.isArray(input)) { | ||
input = []; | ||
} | ||
snippet[field.idsField] = []; | ||
if (field.extras) { | ||
snippet[field.extrasField] = {}; | ||
} | ||
snippet[field.idsField] = self._apos.sanitizeIds(data[field.idsField]); | ||
// Clear old values before we sanitize new, so we don't get orphans | ||
if (field.relationshipsField) { | ||
snippet[field.relationshipsField] = {}; | ||
} | ||
// Each element may be an id or an object with a 'value' property | ||
// containing the id as well as optional extra properties | ||
_.each(input, function(e) { | ||
var id; | ||
if (typeof(e) === 'object') { | ||
id = e.value; | ||
} else { | ||
id = e; | ||
snippet[field.relationshipField] = {}; | ||
_.each(snippet[field.idsField], function(id) { | ||
var e = data[field.relationshipField] && data[field.relationshipField][id]; | ||
if (!e) { | ||
e = {}; | ||
} | ||
id = self._apos.sanitizeId(id); | ||
if (id !== undefined) { | ||
snippet[field.idsField].push(id); | ||
if (field.relationship) { | ||
if (typeof(e) !== 'object') { | ||
// Behave reasonably if we got just ids instead of objects | ||
e = {}; | ||
} | ||
// Validate the relationship (aw) | ||
var validatedRelationship = {}; | ||
_.each(field.relationship, function(attr) { | ||
if (attr.type === 'string') { | ||
validatedRelationship[attr.name] = self._apos.sanitizeString(e[attr.name]); | ||
} else if (attr.type === 'boolean') { | ||
validatedRelationship[attr.name] = self._apos.sanitizeBoolean(e[attr.name]); | ||
} else if (attr.type === 'select') { | ||
// Validate the relationship (aw) | ||
var validatedRelationship = {}; | ||
_.each(field.relationship, function(attr) { | ||
if (attr.type === 'string') { | ||
validatedRelationship[attr.name] = self._apos.sanitizeString(e[attr.name]); | ||
} else if (attr.type === 'boolean') { | ||
validatedRelationship[attr.name] = self._apos.sanitizeBoolean(e[attr.name]); | ||
} else if (attr.type === 'select') { | ||
validatedRelationship[attr.name] = self._apos.sanitizeSelect(e[attr.name], attr.choices); | ||
} else { | ||
console.log(snippet.name + ': unknown type for attr attribute of relationship ' + name + ', ignoring'); | ||
} | ||
}); | ||
snippet[field.relationshipsField][id] = validatedRelationship; | ||
validatedRelationship[attr.name] = self._apos.sanitizeSelect(e[attr.name], attr.choices); | ||
} else { | ||
console.log(snippet.name + ': unknown type for attr attribute of relationship ' + name + ', ignoring'); | ||
} | ||
} | ||
}); | ||
snippet[field.relationshipField][id] = validatedRelationship; | ||
}); | ||
@@ -385,0 +362,0 @@ return setImmediate(callback); |
{ | ||
"version": "0.5.20", | ||
"version": "0.5.21", | ||
"name": "apostrophe-schemas", | ||
@@ -4,0 +4,0 @@ "description": "Schemas for easy editing of properties in Apostrophe objects", |
@@ -222,5 +222,3 @@ function AposSchemas() { | ||
$field = self.findSafe($el, '[data-name="' + name + '"]'); | ||
// The server will do the work of moving it to the idField as needed | ||
data[name] = $field.selective('get', { incomplete: true })[0]; | ||
console.log(data[name]); | ||
data[field.idField] = $field.selective('get', { incomplete: true })[0]; | ||
if (field.required && !data[name]) { | ||
@@ -238,6 +236,15 @@ return apos.afterYield(_.partial(callback, 'required')); | ||
$field = self.findSafe($el, '[data-name="' + name + '"]'); | ||
// The server will do the work of processing it all into | ||
// the relationshipsField and idsField separately for us if needed | ||
data[name] = $field.selective('get', { incomplete: true }); | ||
if (field.required && !data[name].length) { | ||
var info = $field.selective('get', { incomplete: true }); | ||
if (field.relationshipField) { | ||
data[field.idsField] = _.pluck(info, 'value'); | ||
data[field.relationshipField] = {}; | ||
var relationship = {}; | ||
_.each(info, function(e) { | ||
relationship[e.value] = _.omit(e, [ 'value', 'label' ]); | ||
}); | ||
data[field.relationshipField] = relationship; | ||
} else { | ||
data[field.idsField] = info; | ||
} | ||
if (field.required && !data[field.idsField].length) { | ||
return apos.afterYield(function() { | ||
@@ -455,3 +462,6 @@ return apos.afterYield(_.partial(callback, 'required')); | ||
} else { | ||
$field.val(data[name]); | ||
// Always select the first item if no item is selected. | ||
// This is consistent with what most browsers do and works around | ||
// an issue with lister | ||
$field.val(((data[name] === undefined) && field.choices[0]) ? field.choices[0].value : data[name]); | ||
} | ||
@@ -492,2 +502,4 @@ return apos.afterYield(callback); | ||
$field.selective({ limit: 1, data: selectiveData, source: autocomplete }); | ||
self.enhanceSelectiveWithSlugs($field); | ||
return apos.afterYield(callback); | ||
@@ -506,21 +518,3 @@ }, | ||
var selectiveData = []; | ||
_.each(data[field.name] || [], function(friend) { | ||
var datum = {}; | ||
if (field.relationshipsField) { | ||
$.extend(true, datum, friend.relationship); | ||
// Fix booleans to match the select element's options | ||
_.each(field.relationship, function(relField) { | ||
if (relField.type === 'boolean') { | ||
datum[relField.name] = datum[relField.name] ? '1' : '0'; | ||
} | ||
}); | ||
// Present these as jQuery Selective expects us to | ||
datum.label = friend.item.title; | ||
datum.value = friend.item._id; | ||
} else { | ||
datum.label = friend.title; | ||
datum.value = friend._id; | ||
} | ||
selectiveData.push(datum); | ||
}); | ||
// For now this is still correct on the browser side, getManager | ||
@@ -533,3 +527,33 @@ // always returns undefined for an index type | ||
} | ||
$field.selective({ preventDuplicates: true, sortable: field.sortable, extras: !!field.relationship, data: selectiveData, source: autocomplete }); | ||
// The server knows the title of the joined things, while we know | ||
// about our relationship properties. Solve the puzzle by | ||
// passing selective plain old IDs, causing it to call back to its | ||
// source for the corresponding labels. Provide a custom source | ||
// that queries the server and then merges in the relationship fields. | ||
if (field.relationshipField) { | ||
var url = autocomplete; | ||
autocomplete = function(req, callback) { | ||
$.getJSON(url, req, function(results) { | ||
// This gives us "label" and "value", add the | ||
// relationship info and invoke the original callback | ||
_.each(results, function(result) { | ||
var relationship = data[field.relationshipField][result.value]; | ||
if (relationship) { | ||
_.extend(result, relationship); | ||
_.each(field.relationship, function(relField) { | ||
if (relField.type === 'boolean') { | ||
// Fix booleans to work as select elements expect | ||
result[relField.name] = result[relField.name] ? '1' : '0'; | ||
} | ||
}); | ||
} | ||
}); | ||
return callback(results); | ||
}); | ||
}; | ||
} | ||
$field.selective({ preventDuplicates: true, sortable: field.sortable, extras: !!field.relationship, data: data[field.idsField] || [], source: autocomplete }); | ||
self.enhanceSelectiveWithSlugs($field); | ||
return apos.afterYield(callback); | ||
@@ -620,2 +644,20 @@ }, | ||
}; | ||
self.enhanceSelectiveWithSlugs = function($field) { | ||
// Change the presentation to include the slug. | ||
// Based on: http://jqueryui.com/autocomplete/#custom-data | ||
// I stuck with that markup with a minimum of new markup to | ||
// allow styling. -Tom | ||
var $autocomplete = self.findSafe($field, "[data-autocomplete]"); | ||
$autocomplete.data( "ui-autocomplete" )._renderItem = function(ul, item) { | ||
var inner = '<a><div class="apos-autocomplete-label">' + item.label + '</div>'; | ||
if (item.slug) { | ||
inner += '<div class="apos-autocomplete-slug">' + item.slug + '</div>'; | ||
} | ||
inner += '</a>'; | ||
return $('<li class="apos-autocomplete-item">') | ||
.append(inner) | ||
.appendTo(ul); | ||
}; | ||
}; | ||
} | ||
@@ -622,0 +664,0 @@ |
@@ -391,2 +391,14 @@ # apostrophe-schemas | ||
##### Joining With "Regular Pages" | ||
What if you want to allow the user to pick anything at all, as long as it's a "regular page" in the page tree with its own permanent URL? | ||
Just use: | ||
```javascript | ||
withType: 'page' | ||
``` | ||
This special case allows you to easily build navigation menus and the like using [schema widgets](https://github.com/punkave/apostrophe-schema-widgets) and [array fields](#arrays-in-schemas). | ||
##### Reverse Joins | ||
@@ -552,3 +564,3 @@ | ||
If the author's note for every each appearance of each story has to be super-fancy, with rich text and images, then you should make a new module that subclasses snippets in its own right and just join both books and stories to that new module. You can also use [array fields](#arrays-in-schemas) in creative ways to address that problem. | ||
If the author's note for every each appearance of each story has to be super-fancy, with rich text and images, then you should make a new module that subclasses snippets in its own right and just join both books and stories to that new module. You can also use [array fields](#arrays-in-schemas) in creative ways to address this problem, using `joinByOne` as one of the fields of the schema in the array. | ||
@@ -555,0 +567,0 @@ But if the relationship just has a few simple attributes, there is an easier way: |
Sorry, the diff of this file is not supported yet
95949
29
1219
974