Rendering a Drupal 8 Link with Anchor and Destination
I’m working on a site that contains several complicated views (many exposed filters). The views render entities using a couple different view modes. These view modes use Display Suite. Within these view modes I’ve leveraged DS custom fields for several needs.
I needed to render a node “Edit” link within some of these view modes. The user would click the edit link, edit the node, then click “Save”. The system would then bring the user to the exact position of the page they were on before, filters and settings intact.
This type of solution would work without Display Suite (leveraging other methods). You’re getting a DS-based example because that’s what the site called for.
In my particular case I needed to separate the anchor from the edit link. If you’re okay having them in the same place you could skip Step 1 and just include the anchor within the markup for the edit link.
Step 1: Create a DS field for the anchor link
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php namespace Drupal\mymodule\Plugin\DsField; use Drupal\ds\Plugin\DsField\DsFieldBase; /** * Add an anchor link of node-NID (e.g., <a name="node-1234"></a>). * * @DsField( * id = "mymodule_node_id_anchor", * title = @Translation("Node ID anchor (mymodule)"), * entity_type = "node", * provider = "mymodule", * ui_limit = {"*|*"} * ) */ class NodeIdAnchor extends DsFieldBase { /** * {@inheritdoc} */ public function build() { /** @var \Drupal\node\Entity\Node $entity */ $entity = $this->getConfiguration()['entity']; $markup = '<a name="node-' . $entity->id() . '"></a>'; return [ '#type' => 'markup', '#markup' => $markup, ]; } } |
Step 2: Create a DS field for the edit link
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
<?php namespace Drupal\mymodule\Plugin\DsField; use Drupal\Core\Link; use Drupal\Core\Url; use Drupal\ds\Plugin\DsField\DsFieldBase; /** * Add edit link with a destination of the current page with a node id anchor. * * @DsField( * id = "mymodule_edit_destination_node_id_anchor", * title = @Translation("Edit link with destination node ID anchor (mymodule)"), * entity_type = "node", * provider = "mymodule", * ui_limit = {"*|*"} * ) */ class EditLinkDestinationNodeAnchor extends DsFieldBase { /** * {@inheritdoc} */ public function build() { /** @var \Drupal\node\Entity\Node $entity */ $entity = $this->getConfiguration()['entity']; $node_edit_url = '/node/' . $entity->id() . '/edit'; $node_edit_url_options = [ 'query' => [ 'destination' => $this->currentUrlWithParamsAndAnchor($entity->id()), ], ]; $markup = Link::fromTextAndUrl($this->t('Edit'), Url::fromUserInput($node_edit_url, $node_edit_url_options))->toString(); // NOTE: You could render the <a name="node-NID"> here if the positioning // works for you. In my case I need the anchor and the edit link to be // in different locations in the markup. return [ '#type' => 'markup', '#markup' => $markup, '#cache' => [ 'contexts' => ['url.query_args'], ] ]; } /** * Returns the current URL with its current parameters and a node-NID anchor. * * @param int $node_id * Node ID for the anchor. * * @return string * URL as a string. */ public function currentUrlWithParamsAndAnchor($node_id) { $current_url_options = [ 'query' => \Drupal::request()->query->all(), 'fragment' => 'node-' . $node_id, ]; $current_url = Url::fromRoute( '<current>', [], $current_url_options)->toString(); return $current_url; } } |
Step 3: Add the new DS fields to the appropriate view modes
This is a matter of drag-and-drop in the “Manage Display” screens.
Step 4: Test
To test I made sure the “Edit” links were working properly in every context. Some scenarios to test:
- Do I end up back on the same page within the View I was in?
- Do I end up at the exact spot on the page (where the anchor is)?
- Do all of the other exposed filters stay the same when I return to the Views page?