List of Objects with Linkable Attributes

This page demonstrates Hashtag Markup for multiple relational database operations.   A list of all Objects is shown including their Linked Attributes, with options to link or unlink Attributes individually or batched by type.   A list of all potential Attributes is cached and is injected into each Object row, then processed by jQuery to show only the appropriate Unlinked Attributes for each Object (only Attributes not currently Linked).   The Table of Objects, the Attribute Labels, and Form Buttons are all styled using Bootstrap 3 default theme classes.  
Name Linked Attributes Unlinked Attributes Actions
Object 1 1 2 3 4 5 All Positive All Negative
Object 2 1 2 3 4 5 All Positive All Negative
Object 3 1 2 3 4 5 All Positive All Negative
Object 4 1 2 3 4 5 All Positive All Negative
Object 5 1 2 3 4 5 All Positive All Negative
All Objects: All Positive All Negative


Processed this Hashtag Markup:
<#: list all Objects, including options to link and unlink all Attributes :#>
<# start list for objects;
	relate id to object_attributes.object_id;
	relate object_attributes.attribute_id to attributes.id;
	sort by objects.name, objects.id, attributes.type desc, attributes.name as number;
#>

<# start header #>
<table class="table table-striped table-condensed table-hover" id="objects-list">
	<thead>
	<tr>
		<th>Name</th>
		<th>Linked Attributes</th>
		<th>Unlinked Attributes</th>
		<th>Action</th>
	</tr>
	</thead>
<# end header #>

<# start row #>
	<tr data-id="<# id #>">
		<td><# name as html #></td>
		<td class="linked">
			<# start row loop for objects.id; #>
			<a href="object-unlink?object_id=<# id #>&attribute_id=<# attributes.id #>"
			  data-id="<# attributes.id #>" data-type="<# attributes.type #>" 
			  class="label label-action" title="Click to Unlink"><# attributes.name as html #></a>
			<# end row loop #>
		</td>
		<td class="unlinked"></td>
		<td><a href="object-link-all-positive?id=<# id #>" class="label label-success label-action"
			  title="Click to Link All Positive Attributes">+ All Positive</a>
			<a href="object-unlink-all-negative?id=<# id #>" class="label label-danger label-action"
			  title="Click to Unlink All Negative Attributes">- All Negative</a>
		</td>
	</tr>
<# end row #>

<# start footer #>
</table>
<# end footer #>

<# start no results #>
<h4 class="alert alert-warning text-center">No Objects Found</h4>
<# end no results #>

<# end list #>

<#: buttons that apply to all Objects :#>
<div class="text-center">
	<span class="btn-label">All Objects:</span>
	<a href="all-objects-link-all-positive" class="btn btn-success"
	  title="Click to Link All Positive Attributes to All Objects">+ All Positive</a>
	<a href="all-objects-unlink-all-negative" class="btn btn-danger"
	  title="Click to Unlink All Negative Attributes from All Objects">- All Negative</a>
</div>

<#: store a list of all linkable Attributes for use later :#>
<div id="attributes-list">
<# start list for attributes;
	sort by type desc, name as number;
#>
 <a href="object-link?attribute_id=<# id #>" data-id="<# id #>" data-type="<# type #>"
   class="label label-action hidden" title="Click to Link"><# name as html #></a>
<# end list #>
</div>

<#: front-end process to show only the appropriate Unlinked Attributes for each Object :#>
<script type="text/javascript">
  $(document).ready(function(){
    // style all Attribute labels based on their type
    $(".label[data-type='positive']").addClass('label-success');
    $(".label[data-type='negative']").addClass('label-danger');
    // copy the saved list of all potential attributes into every Unlinked Attributes cell
    $(".unlinked").html($("#attributes-list").html());
    // loop through every row in the table, processing each Object
    $("#objects-list tr").each(function(){
      // process the list of all potential Attributes for this Object
      $(this).find(".unlinked [data-id]").each(function(){
        // check each Attribute ID for a match under Linked Attributes for this Object
        if($(this).closest('tr').find(".linked [data-id='"+$(this).data('id')+"']").length==0) {
          // Attribute is not currently linked to this Object, show it under Unlinked Attributes
          $(this).attr('href', $(this).attr('href')+'&object_id='+$(this).closest('tr').data('id'));
          $(this).removeClass('hidden');
        }
      });
    });
  });
</script>
Note:This markup could be rewritten without the use of jQuery, or any front-end process at all, by listing All Attributes related to All Objects, then adding additional logic to the Row Loop Template to create both Linked and Unlinked Attributes; but this example demonstrates the addition of a front-end process to remove that complexity from the back-end, simplifying design integration and decreasing database load and network utilization.   The Saved List of all Attributes could be rewritten to provide a JavaScript array of Attributes IDs, which could later be used by jQuery to dynamically create HTML elements; but this example demonstrates providing complete HTML markup for those elements, again to simplify design integration.   The Alternate Syntax example demonstrates a back-end only solution, but, in some cases, it may be less performant; such as a large list of Objects rarely linked to many, if any, Attributes.
Hashtag Markup for Link Attribute: /attributes/object-link
<#: ensure the requested Object and Attribute both exist :#>
<# apply "objects.<#[url.object_id]#>" or redirect to "/attributes/#objects-list"; #>
<# apply "attributes.<#[url.attribute_id]#>" or redirect to "/attributes/#objects-list"; #>

<#: only link the Attribute to the Object if it isn't currently linked :#>
<# start list for object_attributes;
	include when object_id is "<#[url.object_id]#>" and attribute_id is "<#[url.attribute_id]#>";
	limit 1;
#>
<# start no results #>
<# create record for "object_attributes";
	set object_id to "<#[url.object_id]#>";
	set attribute_id to "<#[url.attribute_id]#>";
#>
<# end no results #>
<# end list #>

<# redirect to "/attributes/#objects-list" #>
Note:This markup for a No Results template could be rewritten as a create unique record commmand, but this example serves to explicitly show the logic being executed, which can be extended to include logic that the unique keyword doesn't provide natively.   The Alternate Syntax example demonstrates a create unique record commmand, eliminating the need for most of this markup, but removing some extensibility.
Hashtag Markup for Unlink Attribute: /attributes/object-unlink
<# delete record list for object_attributes;
	filter object_id=="<#[url.object_id]#>" and attribute_id=="<#[url.attribute_id]#>";
#>

<# redirect to "/attributes/#objects-list" #>
Note:Alternate Keywords for List Directives were used in this example, such as filter instead of delete when, and the comparison operator == instead of is.   The Alternate Syntax example demonstrates those keywords.
Hashtag Markup for Link All Positive Attributes to Object: /attributes/object-link-all-positive
<#: ensure the Object exists :#>
<# apply "objects.<#[url.id]#>" or redirect to "/attributes/#objects-list"; #>

<#: build a CSV list of all "positive" Attribute IDs currently linked to this Object :#>
<# start list for object_attributes;
	relate attribute_id to attributes.id;
	include when object_id is "<#[url.id]#>" and attributes.type is "positive";
#>
<# append already_linked with "<# attributes.id #>" using ","; #>
<# end list #>

<#: query for all "positive" Attributes not currently linked to this Object, then create links :#>
<# start list for attributes;
	include when type is "positive" and id not in "<#[already_linked]#>";
#>
<# create record for "object_attributes"; 
	set object_id to "<#[url.id]#>";
	set attribute_id to "<# id #>";
#>
<# end list #>

<# redirect to "/attributes/#objects-list" #>
Note:The markup for this example could be rewritten similar to the Link All Positive Attributes to All Objects example below, but this example serves to demonstrate an alternate method that explicitly shows the logic being executed to filter records in an extensible way.   The Alternate Syntax example eliminates most of this markup, but removes some extensibility.

Hashtag Markup for Unlink All Negative Attributes from Object: /attributes/object-unlink-all-negative
<# delete record list for object_attributes;
	relate attribute_id to attributes.id;
	delete when object_id is "<#[url.id]#>" and attributes.type is "negative";
#>

<# redirect to "/attributes/#objects-list" #>
Hashtag Markup for Link All Positive Attributes to All Objects: /attributes/all-objects-link-all-positive
<#: query for all "positive" Attributes not currently linked to each Object, then create links :#>
<# start list for attributes;
	relate all objects;
	relate id to object_attributes.attribute_id and object_attributes.object_id to objects.id;
	include when type is "positive" and object_attributes.id is "";
#>
<# create record for "object_attributes"; 
	set object_id to "<# objects.id #>";
	set attribute_id to "<# attributes.id #>";
#>
<# end list #>

<# redirect to "/attributes/#objects-list" #>
Note:The Hashtag Markup for this example demonstrates many methods for relating, filtering, and processing records in a List targeting an SQL database.   The Relate All directive is treated as an SQL INNER JOIN clause.   The Relate directive comparing multiple fields with logical and grouping is treated as an SQL LEFT OUTER JOIN clause.   The Include When directive comparing a related ID field to an empty string "" is also treated as an SQL IS NULL comparison, implying a related record did not exist.  

Hashtag Markup for Unlink All Negative Attributes from All Objects: /attributes/all-objects-unlink-all-negative
<# delete record list for object_attributes;
	relate attribute_id to attributes.id;
	delete when attributes.type is "negative";
#>

<# redirect to "/attributes/#objects-list" #>

The Attribute Batching Mini-App demonstrates many methods for batch processing records using Hashtag Markup.

In these examples, Objects can be linked to 2 different types of Attributes, either positive or negative.   Features are provided to link all positive attributes that are currently unlinked,  or unlink all negative attributes that are currently linked.   Those features can be applied to either a single Object, or all Objects.

All records are stored in an SQL Database.   jQuery was used to demonstrate front-end processing.