List of Objects with Linkable Attributes

This page lists all Objects including their Linked Attributes, with options to link or unlink any Attribute individually or batched by type.   A list of all Attributes is generated and saved to a Variable that is reused in each row, and later processed by jQuery to show only the appropriate Unlinked Attributes for each Object.   The Table is 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:
<#: create a list of all linkable Attributes, and save the results for use later :#>
<# start list for attributes;
	sort by type desc, name as number;
	save to attributes_list;
#>
<# if "<# type #>"=="positive" #>
<a href="object-link?attribute_id=<# id #>" class="label label-success label-action hidden"
  data-id="<# id #>" title="Click to Link"><# name as html #></a>
<# elseif "<# type #>"=="negative" #>
<a href="object-link?attribute_id=<# id #>" class="label label-danger label-action hidden"
  data-id="<# id #>" title="Click to Link"><# name as html #></a>
<# end if #>
<# end list #>

<#: 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; #>
			<# if "<# attributes.type #>"=="positive" #>
			<a href="object-unlink?object_id=<# id #>&attribute_id=<# attributes.id #>"
			  data-id="<# attributes.id #>" class="label label-success label-action"
			  title="Click to Unlink"><# attributes.name as html #></a>
			<# elseif "<# attributes.type #>"=="negative" #>
			<a href="object-unlink?object_id=<# id #>&attribute_id=<# attributes.id #>"
			  data-id="<# attributes.id #>" class="label label-danger label-action"
			  title="Click to Unlink"><# attributes.name as html #></a>
			<# end if #>
			<# end row loop #>
		</td>
		<td class="unlinked">
			<#: inject the saved list of all available Attributes for each row :#>
			<#[attributes_list]#>
		</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 #>

<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>

<#: front-end process to show only the appropriate Unlinked Attributes for each Object :#>
<script type="text/javascript">
  $(document).ready(function(){
    // 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 a front-end process to remove that complexity from the back-end, simplifying design integration.   The Saved List of all Attributes could be rewritten to provide a JavaScript array of Attributes IDs, which could then be used by jQuery to dynamically create HTML elements later, but this example demonstrates providing complete HTML markup for those elements to simplify design integration.  
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.  
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.  
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 serves to demonstrate an alternate method, explicitly showing the logic being executed to filter records, in an extensible way.  

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 translated to an SQL INNER JOIN clause.   The Relate directive comparing multiple fields with logical and grouping is translated to an SQL LEFT OUTER JOIN clause.   The Include When directive comparing a related ID field to an empty string "" is translated to 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.