Drupal 7 Het gebruik van ctools' modal frames en field collection forms om een betere gebruikerservaring te creëren

Posted by: 
Dominique De Cooman

We willen ctools' modal frames en field collection forms gebruiken om een betere gebruikerservaring te creëren.

Zoals we weten komt ctools met een hoop nuttige apis en tools om in onze eigen modules te gebruiken. Een van hen is de "modal frames".

De field collection module wat een minder bekende module is, maar niettemin erg nuttig, stelt je in staat om een verzamelingen van velden toe te voegen aan een eenheid. In ons voorbeeld hebben we bijvoorbeeld een curriculum vitae en we willen er meerdere ervaringen aan toevoegen. Een ervaring bestaat uit meerdere velden: bedrijf, periode, functie en beschrijving. Dus de "field collection" groepeert ze.

Om nieuwe ervaringen toe te voegen kunnen we de node bewerken of we kunnen de field collection modules link gebruiken, die je naar een aparte pagina verwijst om een apart formulier in te vullen.

Wat we willen is om dit formulier te laten verschijnen in een ctools modal frame zodat we easy in place editing hebben wat een uitstekende gebruikerservaring biedt.

Het voorbeeld kan gebruikt worden om elk formulier in een ctools modal frame weer te geven.

Hoe?
Installeer ctools en field collection
Creëer een node type curriculum en voeg alle velden toe. Gebruik de field collection module om hier nieuwe verzamelingen toe te voegen admin/structure/field-collections. Je zult merken dat het tevens een entity is, dus voeg velden toe zoals je velden zou toevoegen aan andere entiteiten zoals nodes. Nu kun je de verzamelingen toevoegen die je hebt gemaakt als een veld op je node type cv. Nu zou je de links moeten zien wanneer je een node creëert.

Vervolgens nemen we deze links over. We zullen een hook menu maken in onze custom module om onze juiste links te creëren. We prefix en suffix de bestaande paden omdat we nog steeds de parameters nodig hebben.

<?php
/**
 * Implements hook_menu().
 */
function glue_menu() {
  
$items = array();
   
  
$items['modal/field-collection/%/%/%/%/%ctools_js/go'] = array(
    
'page callback' => 'glue_modal_operator_callback',
    
'page arguments' => array(2,3,4,5,6),
    
'access arguments' => array('access content'),
  );
  
  return 
$items;
}
?>

Laten we nu de hoofdfuncties creëren die de arguments zal nemen en de "voeg toe", "bewerk" of "verwijder" formulier om onze field collection items in een ctools modal frame te beheren. (lees de opmerkingen in code voro meer uitleg0

<?php
/**
 *  Modal callback
 */
function glue_modal_operator_callback($field_name$nid$operator$id 0$js FALSE) {
  
//We need a function to load an argument to use in our form state for a file in the field collection which is not loaded in this context
  
module_load_include('pages.inc''field_collection');  
  
  
//Access checks to make sure the user has access to the field collections
  
switch ($opertor) {
    case 
'add':
      
$result field_collection_item_add($field_name'node'$nid);
      if (
$result == MENU_NOT_FOUND || $result == MENU_ACCESS_DENIED) {
        exit();
      }     
      break;
    case 
'edit':           
    case 
'delete':
      if (!
field_collection_item_access($opertor$id)) {
        exit();
      }
      break;
  }  
  
  
//Check if js is enabled, this parameter will be loaded by ctools
  
if ($js) {
    
//Include ctools ajax and modal, dont forget to set ajax TRUE
    
ctools_include('ajax');
    
ctools_include('modal');
    
$form_state = array(
      
'ajax' => TRUE,
      
'title' => t('Experiences'),
    );
    
    if (
$operator == 'add') {
      
//Arguments need to be loaded directly onto the build_info args array because ctools_modal_form_wrapper will call drupal_build_form() directly see from API for more
      
$arg glue_field_collection_item_add(str_replace('-''_'$field_name), 'node'$nid);
      if (
$arg == MENU_NOT_FOUND || $arg == MENU_ACCESS_DENIED) {
        exit();
      }
      
$form_state['build_info']['args'][] = $arg
      
//The modal form wrapper is needed to make sure the form will allow validating, you cannot use drupal_get_form directly it wont work.
      
$output ctools_modal_form_wrapper('field_collection_item_form'$form_state);
    }
    else {
      
//The id is the collection entity id
      
$form_state['build_info']['args'][] = field_collection_item_load($id);
      if (
$operator == 'edit') {
        
$output ctools_modal_form_wrapper('field_collection_item_form'$form_state);
      }
      elseif (
$operator == 'delete') {
        
$output ctools_modal_form_wrapper('field_collection_item_delete_confirm'$form_state);
      }
      else {
        exit();
      }
    }
        
    
//If the form is executed will need to dismiss the form and reload the page
    
if ($form_state['executed']) {      
      
$commands = array();
      
      
//Load the new output
      
$node node_load($nidNULLfalse);       
      
//Render the newly saved field collection set       
      //Here is how to render a single field:<a href="http://dominiquedecooman.com/blog/drupal-7-tip-theming-render-only-single-field-your-entities
">http://dominiquedecooman.com/blog/drupal-7-tip-theming-render-only-singl...</a>      $field_to_render = field_view_field('node', $node, str_replace('-', '_', $field_name), 'full');     

      // Remove the prefix and suffix, which contain unneeded div's and actions links.
      unset(
$field_to_render['#prefix']);
      unset(
$field_to_render['#suffix']);

      
$output = render($field_to_render);
      
      //We will replace the fieldcollection with the new output
      
$commands[] = ajax_command_html('.field-name-field-cv-experience', $output);
      //close the frame
      
$commands[] = ctools_modal_command_dismiss();
      
      
$output = $commands;
    }
    //Render the output
    print ajax_render(
$output);
    exit();        
  }
  else {
    //No js found lets go to the default page
    return drupal_get_form('field_collection_item_form', field_collection_item_load(
$id));
  }
}

/**
 * Add a new field-collection item.
 * 
 * We copied this function from the field collection module but instead of returning a form we return the object
 */
function glue_field_collection_item_add(
$field_name$entity_type$entity_id$revision_id = NULL, $langcode = NULL) {
  
$info = entity_get_info();
  if (!isset(
$info[$entity_type])) {
    return MENU_NOT_FOUND;
  }
  
$result = entity_load($entity_type, array($entity_id));

  
$entity = reset($result);  

  if (!
$entity) {
    return MENU_NOT_FOUND;
  }
  // Ensure the given entity is of a bundle that has an instance of the field.
  list(
$id$rev_id$bundle) = entity_extract_ids($entity_type$entity);
  
$instance = field_info_instance($entity_type$field_name$bundle);

  if (!
$instance) {
    return MENU_NOT_FOUND;
  }

  // Check field cardinality.
  
$field = field_info_field($field_name);
  
$langcode = LANGUAGE_NONE;
  if (!(
$field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || !isset($entity->{$field_name}[$langcode]) || count($entity->{$field_name}[$langcode]) < $field['cardinality'])) {
    drupal_set_message(t('Too many items.'), 'error');
    return '';
  }

  
$title = ($field['cardinality'] == 1) ? $instance['label'] : t('Add new !instance_label', array('!instance_label' => drupal_strtolower($instance['label'])));

  drupal_set_title(
$title);

  
$field_collection_item = entity_create('field_collection_item', array('field_name' => $field_name));
  // Do not link the field-collection item with the host entity at this point,
  // as during the form-workflow we have multiple field-collection item entity
  // instances, which we don't want link all with the host.
  // That way the link is going to be created when the item is saved.
  
$field_collection_item->setHostEntity($entity_type$entity, LANGUAGE_NONE, FALSE);

  // Make sure the current user has access to create a field collection item.
  if (!field_collection_item_access('create', 
$field_collection_item)) {
    return MENU_ACCESS_DENIED;
  }
  return 
$field_collection_item;
}
?>

Het enige wat we nu moeten doen is de links overnemen op de pagina. Om dit te doen zullen we de render array moeten veranderen. De hook om dit te doen is hook_field_attach_view_alter().

Door het toevoegen van de ctools-use-modal class zal onze modal functionaliteit worden getriggerd.

<?php
/**
 * Implements hook_field_attach_view_alter
 */
function synergie_field_attach_view_alter(&$output$context) {
  if (
$output['field_cv_experience']) {
    
ctools_include('modal');
    
ctools_modal_add_js();
    
    
$field 'field_cv_experience';
    
//Add
     
$output[$field]['#suffix'] = 
    
'<div class="description field-collection-description"></div>
       <ul class="action-links action-links-field-collection-add">
         <li>'
           
l(t('Add'), 'modal/field-collection/field-cv-experience/' $context['entity']->nid '/add/0/nojs/go'
               array(
'attributes' => array('class' => 'ctools-use-modal'))) .
         
'</li>
        </ul>
     </div>'
;
    
    
//Edit & delete        
    
foreach ($output[$field] as $key => $value) {      
      if (
is_numeric($key)) {          

        
$output[$field][$key]['links']['#links']['edit']['href'] = 'modal/field-collection/' str_replace('_''-'$field) . '/' $context['entity']->nid   '/edit/' $output[$field]['#items'][$key]['value'] . '/nojs/go';
        
$output[$field][$key]['links']['#links']['edit']['attributes'] = array('class' => 'ctools-use-modal');

        
$output[$field][$key]['links']['#links']['delete']['href'] = 'modal/field-collection/' str_replace('_''-'$field) . '/' $context['entity']->nid '/delete/' $output[$field]['#items'][$key]['value'] . '/nojs/go';
        
$output[$field][$key]['links']['#links']['delete']['attributes'] = array('class' => 'ctools-use-modal');
      }      
    }
  }
}
?>

Hier zie je hoe je het moet doen en zo moet het resultaat eruit zien:

Je zou in staat moeten zijn om te valideren na het indienen, dankzij de ctools_modal_form_wrapper functie. Na het indienen zou het modal frame moeten sluiten en de pagina zou de veranderingen moeten weergeven die waren ingevoerd door de ajax_command_html().

Meer meer informatie over hoe je het modal frame kunt thematiseren en vele andere dingen, bekijk ctools_ajax_sample.module, welke uitgerust is met ctools.

Reactie toevoegen