Dec 10 2016
Dec 10

Qwintry team recently started active migration to Vue.js as a frontend framework in all our legacy and new projects:

  • in legacy Drupal system (qwintry.com)
  • in our new, completely rewritten qwintry.com branch
  • in Yii2-powered b2b system (logistics.qwintry.com)
  • in all our smaller internal and external projects (mostly with PHP and Node.js backends)

In terms of project size Qwintry is used by around half a million of customers around the world, we run two warehouses (in US and in Germany) on our own software, and we are one of the biggest mailforwarders in United States in terms of visitor and shipment traffic, focusing on Eastern Europe, and Middle East. Basically, we help people to purchase goods in US online stores and manage packages via our control panel when they were received by our warehouse (consolidate/measure/take pictures) and then ship them internationally while saving significantly on shipping costs. We leverage our own IT systems and logistics chains to get the best quality of delivery services for the best price.
Qwintry box
Our package at customer door - from our happy customer reviews

We have pretty big codebase, mostly PHP&JS.

We decided to use Vue.js after we've completed evaluation of modern frameworks: we've built our customer calculator on React, Vue.js and Angular2.

My thoughts on React.js

React skyrocketed the JS world and it is now probably the default choice for JS devs, when we talk about choosing frontend view framework.
I've built some SPAs and dynamic widgets on React, I've played around React Native (under iOS) and Redux as well. I think that React was a great step forward for JS world in terms of state-awareness, and it showed lots of people the real functional programming in a good, practical way. I think React Native is huge - it just changes the landscape of native development.

Cons of React for me are:

Purity, immutability and ideology over getting things done

Don't get me wrong. I appreciate pure functions and simplistic render() approach - no doubt, that's a great idea which is working great in real life. I am talking about other things.
I guess this level of strictness and purity is something that may be useful when you have 1000 devs in your company - just about the time when you decide to develop your own syntax to go for static types in all the PHP code you write. Or when you are a Haskell developer coming to JS world. But most of companies have far smaller dev teams and other goals than Facebook. I will elaborate more on this below.

JSX sucks

I know, I know! It is "just a plain javascript with special syntax". Our design&html guys who need to focus on making this specific form beautiful by wrapping its elements in various quantities of divs - right now - they don't give a sh*t about purity and plain ES6. Applying designs to React components still sucks big time because JSX lacks readability. Not being able to put plain old IF condition to some block of HTML code sucks, please don't believe React fans that keep telling you that you don't need it when you have ternary operators. Let me assure you - this is still a mess of HTML and JS when you edit it and read it, even though it gets compiled to pure JS.

<ul>  
       {items.map(item =>
         <li key={item.id}>{item.name}</li>
       )}
</ul>  

Lots of developers (including me - but I am not there anymore) are thinking that this specific restriction on syntax will make you stronger will help you write more modular code because you have to put your chunks of code to smaller helper functions and use them inside your render() function like this guy suggested:
http://stackoverflow.com/a/38231866/1132016

JSX is also the reason when you have to keep splitting your 15-lines-of-html-code component to 3 components, 5-lines-of-code-in-each.

Don't think that this is a great workaround which makes you a better developer because you now have to structure your code like this.

Here is the thing:
When you write a relatively complex component - which you are probably not going to put to public github repo tomorrow to showcase it on hackernews - this approach of splitting components into super-dumb components because of JSX restrictions will always put you out of flow when you are solving real business task. No, I am not saying that the idea of smaller components is bad or not working.
You should clearly realize that you need to split your code into components to keep you codebase manageable and reusable. But you should do it only when you think that this specific logical entity in your code should be a separate component with own props, - and not on every two-three IFs that you write via ternary operator! Every time you create a new component here and there it costs you and your flow a penny (probably more) because you need to switch from business-task thinking (when you already remember current component state model, and you just need to add some html here and there to make it running) to "manager thinking" - you go create separate file for your component, start thinking about props of this new component, and how they map to state, and how you are going to pass callbacks inside, etc, etc.
As a result you get reduced speed of writing code by being forced to leverage excessive and premature (potential) modularity of components in places where you don't really need it. In my opinion, premature modularity is very similar to premature optimization.

For me and my team the readability of code is important, but it is still very important that writing code is fun. It is not funny to create 6 components when you are implementing really simple calculator widget. In a lot of cases, it is also bad in terms of maintenance, modifications, or applying visual overhaul to some widget, because you need to jump around multiple files/functions and check each small chunk of HTML separately. Again, I am not suggesting to write monoliths - I suggest to use components instead of microcomponents for day-to-day development. It is just about the common sense.

Working with forms and Redux in React will make you type all day long

React is about pureness and clean one way flow, remember? That's why LinkedStateMixin became a persona non grata and now you have to create 10 functions to get input from 10 inputs. 80% of these functions will contain single line with this.setState() call, or redux action call (then you will probably have to create another 10 constants - one per each input). I guess that would be acceptable if you could generate all this code by thinking about it.. but I am not aware of any IDE that could significantly improve this.
Why do you have to type so much? Because two-way binding is considered dangerous by big guys in big-enterprise apps. I can confirm that two-way data flow code sometimes is not as clean to read, but most of these fears are mixed with overall pain about Angular 1 where two-way binding was bad, and still.. probably it was not the biggest fail even there.

Let me show you the quick-editor set of components I've built recently with Vue.js for our Drupal website (I beg your pardon for the design - it is a backend UI for our operators, and our designers are busy creating frontend interfaces for our customers so this piece is waiting to get design overhaul):

I can't share the code for obvious reasons but writing it in Vue was real fun, and the code is very readable.

And I know for sure that creating a separate function for each input to handle a widget like this in React would certainly not make me happy.

Redux sounds like a synonym of verbosity, as well. And it's easy to find developers that blame that Mobx is turning React into Angular just because because it leverages two-way binding - see my point #1 about purity. It looks like a lot of smart people value purity of their codebase more than getting job done (which is fine if you don't have deadlines, I guess).

Excessive tooling

React was created with Babel in mind. You can't do a step in real-world React app without a bunch of npm packages here, compiler to ES5 going first. Simple app based on official react starting package code has around 75MB of JS code in node_modules.
It is not a critical thing, it's more related to JS world in overall than to React, but it adds up to overall frustration when using React as well.

Angular 1: too much freedom is sometimes bad

Angular 1 was a great frontend framework that is located in the opposite corner (from React) of the imaginary JS map of purity and readability of codebases - it allows you to start quickly, it gives you some real fun for your first 1k lines of code, and then it practically forces you to write shitty code. You will probably get lost in directives, scopes, and two way data flows across all the layers of your app will just be a cherry on the pie of the code that your freshly hired devs won't even want to touch because it won't be manageable.

Why so?
Angular.js was created in 2009 when frontend world looked pretty simple and nobody was even thinking about state hygiene. You can't blame these guys - they were just creating competitor of Backbone with some new concepts and they wanted to do less typing.

Angular2

Just build hello world app and look at the amount of files you got in your repo. You will have to use Typescript (and I am not 100% sure it is something I am going to enjoy doing every day - great writeup from Eric Eliott on this topic) and compilers to start working. It was enough for me.. For me is still too much typing before I start real work. To my mind, Angular 2 guys are trying to build perfect framework which will beat React, instead of trying to build a framework which solves business tasks for average user. May be I am wrong and my mind might change - I don't have a lot of experience building Angular2 apps yet, we've just built demo customer calculator app for our in-house evaluation. Wonderful comparison page on Vue.js website states that Angular2 is a good framework which share a lot of concepts with Vue.

Vue.js

In short, Vue.js is a thing that I've been waiting for a long time ( I will be talking about Vue.js 2 which got quite a few improvements over first version of Vue and this is the current stable framework version). For me, in terms of elegance and conciseness, and focus on getting things done, Vue.js is the biggest change to JS after the day when I was blown out by jQuery in 2007.
If you look to Vue.js popularity graphs you will notice it is not just me: https://www.google.ru/trends/explore?q=vue.js,react.js,angular.js
Vue.js is one of the most rapidly growing JS frameworks in 2016, and I think it's not just another hype based on fans that switch to newer JS framework every 3 months, or authority (and money) of one big company.

Laravel added Vue.js to core, which is a big thing.

Pros of Vue.js

Vue.js hits a sweet spot between readability&maintainability and fun. A spot between React and Angular 1, and if you look at Vue guideline, you will instantly notice how many nice things it got from these frameworks.
From React, it got component-based approach, props, one-way data flow for components hierarchy, performance, virtual rendering ability, and understanding of importance of proper state management of apps.
From Angular, it got similar templates with good syntax, and two-way binding when you need it (inside single component).

Vue.js is very easy to start - I've seen this in our team. It does not enforce any compilers by default, so it's really easy to drop in Vue to your legacy codebase and start improving your jQuery mess with good JS code.

Right amount of Magic

Vue.js is very easy to work with, both in HTML and JS - you can do pretty complex templates without losing your focus on business task and the template usually maintains great readability even when it gets really big - at this moment you've usually made a good progress in terms of business task solved, and you might want to refactor templates and split them into smaller components - at this moment you see the whole "picture" of your app a lot better than at the moment when you've started.

From my experience, this differs vastly from approach that I used to have in React: I saved myself a lot of hours here. In React, you have to split components into micro-components and micro-functions at the time of writing the initial version of your code - or you will literally get buried in the mess of your code. In React you will spend a lot of time polishing the props and refactoring your super-small components (that will never be re-used later) again and again since you don't see clearly if you have to change the flow of your app logic somewhere in the middle of the code writing process.

Working with html forms is a breeze in Vue. This is where two-way binding shines. It does not bring any issues to me even in complex cases, though watchers may remind of Angular 1 at first glance. One-way flow with callback passing is always at your service when you do your components splitting.

If you want some compiler magic, linting, PostCSS and ES6 - you got it. Vue extension seems to become a default way of writing public components in Vue.js 2. By the way, idea of scoped CSS of component, working out of the box, is something that looks really nice and can reduce need in proper css hierarchy naming and technologies like BEM.

Vue.js has pretty simple and useful state and props management in core, via data() and props() methods - they work great in real world. Better separation of concerns available via Vuex (which is to my understanding similar to Mobx in React - with some mutation of state involved).

I think a good percent of Vue.js use cases won't ever require such a state management as Vuex provides, but it is always good to have an option.

Cons of VueJS

  1. The biggest one: not descriptive runtime errors in templates. This is pretty similar to Angular 1. Vue.js manages to give a lot of useful warnings for your JS code, for example there are warnings when you try to mutate props, or using data() method incorrectly - the good influence of React can be very well seen here. But runtime errors in templates are still a weak point of Vue - exception stacktraces in a lot of times are not useful and are leading into Vue.js internal methods.
  2. The framework is young. No stable community components - a lot of them were built for Vue.js 1, and it is sometimes not easy for newcomers to see from github repo which version of Vue the library is built for. This issue is leveled by the fact that you can do huge things in Vue without any additional libraries - you will probably just need some ajax library (vue-resource will be a good choice if you don't care about isomorphic apps, axios otherwise), and probably vue-router which is considered as core library with good support.
  3. Chinese comments in code across most of community libraries - this is not surprising, Vue.js is getting very popular in China (the author speaks Chinese).
  4. Single-guy project? Not exactly a real issue, but something to consider. Evan You is the guy who built Vue, after working at Google and Meteor. Laravel also used to be a single-guy project, it is still a huge success, but you never know..

Vue.js in Drupal

Disclaimer: we do not plan to use Drupal 8 any time soon at Qwintry, since we are switching to faster and simpler PHP&Node.js frameworks, and our legacy codebase is Drupal 7.
Since our legacy system qwintry.com is powered by Drupal, it was very important for us to test this new framework here, in the wild. I am not proud by a lot of code in our legacy codebase, but it works and generates our revenue, so we respect it, improve it, and build a lot of new features here. Here are the list of things I've built in Vue&Drupal already:
In-place node editing for complex order entities. This includes generating invoices for customers, and quick edit of product items. It required building basic JSON api for loading and saving of nodes - nothing too fancy, just a few menu callbacks.
Two REST-powered dashboards for proprietary Saas software we are using so our customer support does not have to login to separate websites to quickly check information related to specific customer - everything is built right inside customer profile in our website now.

I know a lot of backend developers are still stuck in 2010 and Drupal 7 core Ajax system.
I know how complex Drupal might be when you try to build some fancy multi-step ajax interaction form using core features - it's just crazy hard to maintain this code later. Yes, I am looking at you, ctools_wizard_multistep_form() and you, ajax_render!
At the same time, these Drupal developers are pushed forward by modern world UI requirements, but they might be scared by increased complexity of modern JS frameworks. Yep, it's me a year ago. Let me tell you - you won't find a better time and way to improve your interfaces than get Vue.js now, put it in your /sites/all/libraries, add it via drupal_add_js() to your template, and start hacking around. You will be shocked how easier it is to maintain a number of pure JSON callbacks sitting in your hook_menu when the client side, including forms, is completely powered by Vue.

Vue.js in Yii2

Fun fact: Yii was created by a Chinese speaking guy - Qiang Xue. So, you might call Yii+Vue stack not just very difficult to pronounce, but also a Chinese stack :)
For our new version of Qwintry.com (not yet public) we chose Yii2 which I believe is one of the best and fastest PHP frameworks available. It is definitely not as popular as Laravel which is rocking the PHP world now, but we are pretty happy with our Yii2 stack now (though we are looking at Laravel from time to time, the guys are doing a great job).
We are gradually reducing amount of html generated by Yii2 and PHP, and concentrating more on REST backend which generates JSON for our client-side which is powered by Vue.js. It is mostly API-first approach for all our Active Record models.
Here, we are taking API seriously and that's why we are spending a lot of time building good API documentation even though it is only used in-house.
With PHP7&latest MySQL the response times of our Yii2 JSON backend does not differ much from Node.js backends (15-20ms are the numbers I am talking about), so it's more than enough for our needs, and 10-20 times faster than we could imagine to achieve using Drupal. At the same time, it's good&old PHP with all the power of composer libraries and stable codebase at our hands.
So, Yii2&Vue.js responsiveness is huge, and in terms of code it's a pleasure to work with.

We are also using Vue.js in a number of internal projects.

Conclusion

We've been writing Vue.js code every day for around 3 months in various projects, with impressive results. 3 months is nothing in backend world, but it is something in JS world :) We'll see how it goes further.

I expect Vue to become a primary JS framework in 16-24 months if Evan You makes right steps, at least around backenders and smaller teams of frontenders. I still consider React stack to be the primary JS framework of 2017, especially if React Native manages to mature and improve itself with same pace it used to.

UPD: this post got to frontpage of HackerNews and there is useful discussion with 200+ comments there: https://news.ycombinator.com/item?id=13151317
It also got to top posts of Reddit webdev, 70+ comments here: https://www.reddit.com/r/webdev/comments/5ho71i/why_we_chose_vuejs_over_react/

Jan 10 2016
JK
Jan 10

In this article we will describe how we created the calendar in our back-office management application EK (see demo).

Calendar

For this function, we used the FullCalendar plugin and its dependencies.

1) Create the Drupal 8 library

In a file called MyModule.libraries.yml, insert the following css and js configurations:

MyModule.calendar:
  version: VERSION
  css:
    theme:
      css/ek_calendar.css: {}
      js/cal/fullcalendar/fullcalendar.css: {}
      js/jquery.qtip/jquery.qtip.css: {}
  js:
    js/cal/fullcalendar/lib/moment.min.js: {}
    js/cal/fullcalendar/fullcalendar.js: {}
    js/jquery.qtip/jquery.qtip.min.js: {}
    js/cal/calendar_script.js: {}
  dependencies:
    - core/jquery
    - core/drupal
    - core/drupalSettings
    - core/drupal.ajax
    - core/drupal.dialog
    - core/jquery.ui.datepicker

Note: we also used jQuery qtip to display title pop-up in the calendar, but this not an obligation.

The file calendar_script.js is for custom javascript scripts needed in this implementation.

2) create the routes

In MyModule.routing.yml we will need 2 routes. The first one is the main route to the calendar display function in our countroller; the second one is to pull data displayed in the calendar from an ajax call.

MyModule_calendar:
  path: '/path/calendar'
  defaults:
    _title: 'Calendar'
    _controller: '\Drupal\MyModule\Controller\CalendarController::calendar'
  requirements:
    _permission: 'calendar'

MyModule_calendar_view:
  path: '/MyModule/calendar/view/{id}'
  defaults:
    _controller: '\Drupal\ek_projects\Controller\CalendarController::view'
  requirements:
    _permission: 'calendar'

The id in MyModule_calendar_view route is a key used to indentify the type of data to be retrieved. In our case we display different events from dates of projects status and tasks thus we filter base on event type in our controller (submission, start, deadline, etc...). But this can be adapted to your own case.

3) The form to filter content display

This form is very simple and used to filter the events and trigger the calendar display.

filter

Here is the function buildForm into the SelectCalendar Class

public function buildForm(array $form, FormStateInterface $form_state) {

    $options = [ 0 => t('Calendar'), 1 => t('My tasks') , 2 => t('Projects submission'),    3 => t('Projects validation'), 4 => t('Projects start')],

    $form['select'] = array(

      '#type' => 'select',

      '#id' => 'filtercalendar',

      '#options' => $options,

      '#attributes' => array('title' => t('display options'), 'class' => array()),

      );

        return $form;  

  }

validateForm and submitForm are not used as the form is actually never submitted;

4) Create the controller

In our controller CalendarController.php we have 2 functions that match the above routes: calendar() and view() plus a third function to display the calendar in a dialog box: dialog();

calendar() function is very basic as it only call the dialog box.

  /**
   * AJAX callback handler for Ajax Calendar Dialog
   */
  public function calendar() {
    return $this->dialog(TRUE);
  }

So when using route /MyModule/calendar, the actual response happens in the dialog function:

/**
   * Render dialog in ajax callback.
   *
   * @param bool $is_modal
   *   (optional) TRUE if modal, FALSE if plain dialog. Defaults to FALSE.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   An ajax response object.
   */
  protected function dialog($is_modal = FALSE) {
 
    $content = $this->formBuilder->getForm('Drupal\MyModule\Form\SelectCalendar');
    $content['content']['#markup'] = "<div id='calendar'></div>";
                        
    $response = new AjaxResponse();
    $title = t('Calendar');
    $l =  \Drupal::currentUser()->getPreferredLangcode();
    $content['#attached']['drupalSettings'] = array('calendarLang' => $l );
    $content['#attached']['library'] = array('core/drupal.dialog.ajax', 'ek_projects/ek_projects.calendar');
    $options = array('width' => '80%');
    
    if ($is_modal) {
      $dialog = new OpenModalDialogCommand($title, $content, $options);
      $response->addCommand($dialog);
    }
    else {
      $selector = '#ajax-text-dialog-wrapper-1';
      $response->addCommand(new OpenDialogCommand($selector, $title, $html));
    }
    return $response;
  }

Note: first the form to filter data is included with $this->formBuilder->getForm('Drupal\MyModule\Form\SelectCalendar');. Then we add a simple div markup in the content that will hold the calendar display generated by the plugin: $content['content']['#markup'] = "<div id='calendar'></div>"; Finally, the Ajax response is built with necessary parameters. The proper core ajax and dialog references in the controller are needed for the dialog to work as expected:

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\Core\Ajax\OpenDialogCommand;


The view() function is where the customisation part is the most relevant. This function collect data to display in a database and send them back in appropriate format. Thus we will only show the basic structure as it may apply to multiple data sources or formats.

  /**
   * AJAX callback handler for task and event display in calendar
   */
  public function view($id) {
      $color_array = array('#CEE3F6','#CEEFF6','#CEF6F0','#CEE3F6','#CEF6D4');

// this array will hold the data to be send back for dispay
      $events=array();
 
      switch($id) {
          
          case 1 :
     
          //1 My tasks
          // query data here and format it
          // sample event value format required by fullCalendar:
          //        
          //      $values = array(
          //          'id' => 'entry id',
          //          'title' => 'string',
          //          'description' => 'string',
          //          'start' => 'date',
          //          'end' => 'date',
          //          'url' => "MyModule/path",
          //          'allDay' => 'True/False',
          //          'className' => "",
          //          'color' => $color_array[$n],  
          //          'textColor' => 'black',
          //      );
          //      array_push($events, $values);
            
              break;     
            case 2 :
              // query data here
              break;
            case 3 :
              // query data here      
              break;
            
            //etc.
        
    }
      return new JsonResponse($events);        
   }

5) Javascript

Here is the code in js/cal/calendar_script.js:

(function ($, Drupal, drupalSettings) {

  Drupal.behaviors.MyModule_calendar = {
    attach: function (context, settings) {
      
        jQuery( "#filtercalendar" )
          .bind( "change", function( event ) {
              jQuery('#loading').show();
              var h = screen.height;
              var w = screen.width;
              var dh = h*0.8;
              var dw = dh;
              var top = (h-dh)/3;
              var left = (w-dw)/2;
              jQuery('.ui-dialog').css({top: top});
              var option = jQuery(this).val();
              display_calendar(option,settings.calendarLang);
          });

    }
  };
 
    
  function display_calendar(e,calendarLang) {
        jQuery('#calendar').fullCalendar( 'destroy' );
        jQuery('#calendar').fullCalendar({
            header: {
                left: 'prev,next today',
                center: 'title',
                right: 'month,agendaWeek,agendaDay'
            },
            eventMouseover:true,
            lang: calendarLang,
            events: {
                url: drupalSettings.path.baseUrl + "MyModule/calendar/view/" + e,
                error: function() {
                    jQuery('#calendar-warning').show();
                }
            },
                        aspectRatio:  1.8,
                        timeFormat: 'H(:mm)',
                        agenda: 'h:mm{ - h:mm}',
                        loading: function(bool) {
                            jQuery('#loading').toggle(bool);
                        },
                        eventRender: function(event, element) {
                                element.qtip({
                                    content: event.description,
                                    target: 'mouse',
                                    adjust: { x: 5, y: 5 }
                                });
                            }
            });
  }  

})(jQuery, Drupal, drupalSettings);

What happen here is that the function display_calendar() which actually trigger the fullCalendar plugin action is bind to the form that filters the data and identified by its id '#filtercalendar'. This function simply call  fullCalendar with necessary options (including the events that are pulled from view()) and display it into the html div markup identified by its id  '#calendar'.

Feel free to comment or suggest other ways of creating a calendar.

May 20 2015
May 20

Boost the performance of your Drupal 7 website, improve usability and help with SEO by making content load via AJAX in just a few steps.

Drupal AJAX is a cinch to implement—as it should be since core is loaded with it. In just a few of steps I’ll show you how to use jQuery and Drupal’s hook_menu() function to quickly build a AJAX function to return anything from HTML to a JSON array.

Simple Drupal AJAX JSON Implementation

/**
 * Implementation of hook_menu().
 * @see https://api.drupal.org/api/drupal/modules%21system%21system.api.php/function/hook_menu/7
 */
function moduleName_menu() {

  // Setup a URL to retrieve the JSON array.
  $items['node/%/moduleName/pageviews'] = array(
    'page callback'     =&gt; 'moduleName_get_pageviews',
    'page arguments'    =&gt; array(3),
    'type'              =&gt; MENU_CALLBACK,
    'access arguments'  =&gt; array('access content'),
    'delivery callback' =&gt; 'moduleName_ajax'
  );

  return $items;
}

/**
 * Returns the number of pageviews for a node ID.
 */
function moduleName_get_pageviews($nid) {

  // Some fancy function that returns pageviews.
  return fancyPageviews($nid);
}

/**
 * Renders a JSON array.
 */
function moduleName_ajax($pageviews) {

  // Tell the browser to expect JSON data.
  // @see https://api.drupal.org/api/drupal/includes!bootstrap.inc/function/drupal_add_http_header/7
  drupal_add_http_header('Content-Type', 'application/json');

  // Output the JSON result
  // @see https://api.drupal.org/api/drupal/includes!common.inc/function/drupal_json_output/7
  print drupal_json_output(array('pageviews', $pageviews));

  // Perform end-of-request tasks.
  // @see https://api.drupal.org/api/drupal/includes!common.inc/function/drupal_page_footer/7
  drupal_page_footer();
}

With the above code (after clearing cache), visit yoursite.com/node/55/moduleName/pageviews to return a JSON array with the number of pageviews for node ID 55. More information on the hooks and functions used can be found below:

You can then use this URL and jQuery.getJSON() to return the data via AJAX.

;( function( $ ) {
  &quot;use strict&quot;;

  // When document is ready...
  $( function() {

    // Get the JSON result.
    $.getJSON( &quot;node/55/moduleName/pageviews&quot;, function( data ) {

      // Add the number of pageviews to the page.
      $( &quot;#pageviews&quot; ).text( data.pageviews );
    });
  });

})( jQuery );

Or if you need to pass data to the AJAX script:

;( function( $ ) {
  &quot;use strict&quot;;

  // When ready...
  // @see http://www.benmarshall.me/drupal-behaviors/
  Drupal.behaviors.myModule = {

    // Arguments to pass to the AJAX script.
    vars arguments = { status: 'awesome' };

    // Send the arguments &amp; return the JSON data.
    $.getJSON( &quot;node/55/moduleName/pageviews&quot;, arguments ).done( function( data ) {

      // Add the number of pageviews to the page.
      $( &quot;#pageviews&quot; ).text( data.pageviews );
    });
  };

})( jQuery );

For more information on $.getJSON(), see http://api.jquery.com/jquery.getjson/.

You’ll also notice it’s using something called Drupal.behaviors. This is essentially a document ready for all content, including new content that get’s added to the page after load. For instance, if a modal is added to the page via AJAX and a function that’s already been initialized needs to run on the modal, normally you’d have to force event delegation. For a quick introduction to Drupal behaviors, see http://www.benmarshall.me/drupal-behaviors/.

A Closer Look

Like with everything in Drupal, there’s a number of ways to implement AJAX, but which is best? Let’s dive a little deeper into Drupal AJAX and take a look at how, why and when you can use this powerful feature.

AJAX outside the Form API is new in Drupal 7. It allows back and front-end developers the ability to leverage high performance and solid JSON and HTML responses. You can use this feature to update blocks, nodes, pages and any other element with better performance on the client and server-side vs. loading AHAH page fragments.

Drupal AJAX with jQuery

jQuery provides a few different AJAX commands, depending on your exact requirements. Here is the simplest Ajax call you can make with jQuery:

$( &quot;#someDiv&quot; ).load( url );

What this is saying is “Find the div with the id of ‘someDiv’ and load the html that you find at the location ‘url’ into this div”.

The jQuery utility functions below provide for the extra flexibility you need when dealing with data coming back from the server that needs to be parsed.

// https://api.jquery.com/jquery.get/
$.get( url, parameters, callback );

// http://api.jquery.com/jquery.post/
$.post( url, parameters, callback );

// http://api.jquery.com/jquery.ajax/
$.ajax( options, settings );

// http://api.jquery.com/jquery.getjson/
$.getJSON( url, data, success );

// http://api.jquery.com/load/
$.load( url, data, complete )

The only difference between $.get and $.post is the HTTP request method used to send your parameters (an array passed as the second argument) to the server. Very often in Drupal, you won’t need to send any parameters because the url you will be calling will be a menu callback you have set up as, for example, ‘ajax/get/node_details’ taking one argument, ‘nid’ and so you would simply make the call to ‘ajax/get/node_details/123’ and not need to send your nid parameter as a parameter in the second argument.

Additional Resources:

Like this:

Like Loading...

Author: Ben Marshall

Red Bull Addict, Self-Proclaimed Grill Master, Entrepreneur, Workaholic, Front End Engineer, SEO/SM Strategist, Web Developer, Blogger

Apr 20 2015
Apr 20

In this article, we are going to look at how we can create a Drupal module which will allow your users to like your posts. The implementation will use jQuery to make AJAX calls and save this data asynchronously.

logo_drupal

Creating your Drupal like module

Let’s start by creating the new Drupal module. To do that we should first create a folder called likepost in the sites\all\modules\custom directory of your Drupal installation as shown below:

Initial folder structure

Inside this folder, you should create a file called likepost.info with the following contents:

name = likepost
description = This module allows the user to like posts in Drupal.
core = 7.x

This file is responsible for providing metadata about your module. This allows Drupal to detect and load its contents.

Next, you should create a file called as likepost.module in the same directory. After creating the file, add the following code to it:

/**
 * @file
 * This is the main module file.
 */

 /**
 * Implements hook_help().
 */
function likepost_help($path, $arg) {

    if ($path == 'admin/help#likepost') {
        $output = '<h3>' . t('About') . '</h3>';
        $output .= '<p>' . t('This module allows the user to like posts in Drupal.') . '</p>';
        return $output;
    }
}

Once you have completed this you can go to the modules section in your Drupal administration and should be able to see the new module. Do not enable the module yet, as we will do so after adding some more functionality.

Creating the schema

Once you have created the module file, you can create a likepost.install file inside the module root folder. Inside, you will define a table schema which is needed to store the likes on each post for each user. Add the following code to the file:

<?php

/**
* Implements hook_schema().
*/
function likepost_schema() {
    $schema['likepost_table_for_likes'] = array(
        'description' => t('Add the likes of the user for a post.'),
        'fields' => array(
            'userid' => array(
                'type' => 'int',
                'not null' => TRUE,
                'default' => 0,
                'description' => t('The user id.'),
            ),

            'nodeid' => array(
                'type' => 'int',
                'unsigned' => TRUE,
                'not null' => TRUE,
                'default' => 0,
                'description' => t('The id of the node.'),
                ),

        ),

        'primary key' => array('userid', 'nodeid'),
    );
    return $schema;
}

In the above code we are are implementing the hook_schema(), in order to define the schema for our table. The tables which are defined within this hook are created during the installation of the module and are removed during the uninstallation.

We defined a table called likepost_table_for_likes with two fields: userid and nodeid. They are both integers and will store one entry per userid – nodeid combination when the user likes a post.

Once you have added this file, you can install the module. If everything has gone correctly, your module should be enabled without any errors and the table likepost_table_for_likes should be created in your database. You should also see the help link enabled in the module list next to your likepost module. If you click on that you should be able to see the help message you defined in the hook_help() implementation.

Help Message

Creating a menu callback to handle likes

Once we have enabled the module, we can add a menu callback which will handle the AJAX request to add or delete the like. To do that, add the following code to your likepost.module file

/**
* Implements hook_menu().
*/
function likepost_menu() {
    $items['likepost/like/%'] = array(
        'title' => 'Like',
        'page callback' => 'likepost_like',
        'page arguments' => array(2),
        'access arguments' => array('access content'),
        'type' => MENU_SUGGESTED_ITEM,
    );
    return $items;
}


function likepost_like($nodeid) {
    $nodeid = (int)$nodeid;
    global $user;

    $like = likepost_get_like($nodeid, $user->uid);

    if ($like !== 0) {
        db_delete('likepost_table_for_likes')
        ->condition('userid', $user->uid)
        ->condition('nodeid', $nodeid)
        ->execute();
        //Update the like value , which will be sent as response
        $like = 0;
    } else {
        db_insert('likepost_table_for_likes')
        ->fields(array(
        'userid' => $user->uid,
        'nodeid' => $nodeid
        ))
        ->execute();
        //Update the like value , which will be sent as response
        $like = 1;
    }

    $total_count = likepost_get_total_like($nodeid);
    drupal_json_output(array(
        'like_status' => $like,
        'total_count' => $total_count
        )
    );

}

/**
* Return the total like count for a node.
*/
function likepost_get_total_like($nid) {
    $total_count = db_query('SELECT count(*) from {likepost_table_for_likes} where nodeid = :nodeid',
    array(':nodeid' => $nid))->fetchField();
    return (int)$total_count;
}

/**
* Return whether the current user has liked the node.
*/
function likepost_get_like($nodeid, $userid) {
    $like = db_query('SELECT count(*) FROM {likepost_table_for_likes} WHERE
    nodeid = :nodeid AND userid = :userid', array(':nodeid' => $nodeid, ':userid' => $userid))->fetchField();
    return (int)$like;
}

In the above code, we are implementing hook_menu() so that whenever the path likepost/like is accessed with the node ID, it will call the function likepost_like().

Inside of likepost_like() we get the node ID and the logged in user’s ID and pass them to the function likepost_get_like(). In the function likepost_get_like() we check our table likepost_table_for_likes to see if this user has already liked this post. In case he has, we will delete that like, otherwise we will insert an entry. Once that is done, we call likepost_get_total_like() with the node ID as a parameter, which calculates the total number of likes from all users on this post. These values are then returned as JSON using the drupal_json_output() API function.

This menu callback will be called from our JQuery AJAX call and will update the UI with the JSON it receives.

Displaying the Like button on the node

Once we have created the callback, we need to show the like link on each of the posts. We can do so by implementing hook_node_view() as below:

/**
 * Implementation of hook_node_view
 */
function likepost_node_view($node, $view_mode) {
    if ($view_mode == 'full'){
        $node->content['likepost_display'] =  array('#markup' => display_like_post_details($node->nid),'#weight' => 100);

        $node->content['#attached']['js'][] = array('data' => drupal_get_path('module', 'likepost') .'/likepost.js');
        $node->content['#attached']['css'][] = array('data' => drupal_get_path('module', 'likepost') .'/likepost.css');
    } 

}

/**
* Displays the Like post details.
*/
function display_like_post_details($nid) {

    global $user;
    $totalLike =  likepost_get_total_like($nid);
    $hasCurrentUserLiked = likepost_get_like($nid , $user->uid);

    return theme('like_post',array('nid' =>$nid, 'totalLike' =>$totalLike, 'hasCurrentUserLiked' => $hasCurrentUserLiked));
    
}
/**
* Implements hook_theme().
*/
function likepost_theme() {
    $themes = array (
        'like_post' => array(
            'arguments' => array('nid','totalLike','hasCurrentUserLiked'),
        ),
    );
    return $themes;
}

function theme_like_post($arguments) {
    $nid = $arguments['nid'];
    $totalLike = $arguments['totalLike'];
    $hasCurrentUserLiked = $arguments['hasCurrentUserLiked'];
    global $base_url;
    $output = '<div class="likepost">';
    $output .= 'Total number of likes on the post are ';
    $output .= '<div class="total_count">'.$totalLike.'</div>';

    if($hasCurrentUserLiked == 0) {
        $linkText = 'Like';
    } else {
        $linkText = 'Delete Like';
    }

    $output .= l($linkText, $base_url.'/likepost/like/'.$nid, array('attributes' => array('class' => 'like-link')));

    $output .= '</div>'; 
    return $output;
    
}

Inside likepost_node_view() we check for when the node is in the full view mode and we add the markup returned by the function display_like_post_details(). We also attached our custom JS and CSS file when the view is rendered using the attached property on the node content. In function display_like_post_details() we get the total number of likes for the post and whether or not the current user has liked the post. Then we call the theme function which will call the function theme_like_post() which we have declared in the implementation of ‘hook_theme’ but will allow the designers to override if required. In theme_like_post(), we create the HTML output accordingly. The href on the link is the $base_url and the path to our callback appended to it. The node ID is also attached to the URL which will be passed as a parameter to the callback.

Once this is done, add a file likepost.css to the module root folder with the following contents:

.likepost {
    border-style: dotted;
    border-color: #98bf21;
    padding: 10px;
}

.total_count {
    font-weight: bold;
}

.like-link {
    color:red;
}

.like-link:hover {
    color: red;
}

Now if you go to the complete page of a post you will see the Like post count as shown below.

Adding the jQuery logic

Now that we see the like link displayed, we will just have to create the likepost.js file with the following contents:

jQuery(document).ready(function () {

    jQuery('a.like-link').click(function () {
        jQuery.ajax({
            type: 'POST', 
            url: this.href,
            dataType: 'json',
            success: function (data) {
                if(data.like_status == 0) {
                    jQuery('a.like-link').html('Like');
                }
                else {
                    jQuery('a.like-link').html('Delete Like');
                }

                jQuery('.total_count').html(data.total_count);
            },
            data: 'js=1' 
        });

        return false;
    });
});

The above code binds the click event to the like link and makes an AJAX request to the URL of our callback menu function. The latter will update the like post count accordingly and then return the new total count and like status, which is used in the success function of the AJAX call to update the UI.

Updated UI with Like count

Conclusion

jQuery and AJAX are powerful tools to create dynamic and responsive websites. You can easily use them in your Drupal modules to add functionality to your Drupal site, since Drupal already leverages jQuery for its interface.

Have feedback? Let us know in the comments!

Jan 08 2014
Jan 08

A client recently asked us for help with a very specific issue. The node edit page was hanging up, but only in Internet Explorer 10, and not in Firefox or Chrome. The client had WYSIWYG editor enabled.

This automatically pointed to a front end issue, not a server issue.

So, we investigated more, and found that the underlying issue is between Internet Explorer and JQuery with a large number of items to be parsed.

Internet Explorer was not able to parse the high number of token items listed (around 220). This causes the browse to hang when rendering the WYSIWYG page, with the following error message:

A script on this page is causing Internet Explorer to run slowly. If it continues to run, you computer might become responsive.

With the option to stop the script.

The real problem is with the critical issue described in #1334456, which is as yet not committed to the repository of the token module.

Fortunately there is an easy workaround, the steps are:

  • Install the Token Tweaks module.
  • Go to /admin/config/system/tokens.
  • Change the Maximum Depth limit from the default of 4 to 1
  • Save the changes.

Now the edit form for the node should work normally, and the browser, whichever it is, will not hang anymore.

Note: Thanks to Dave Reid for this workaround.

Mar 26 2013
Mar 26

Episode Number: 

131

The Drupal 7 Floating Block module makes it easy to float a Drupal block along the sidebar as you scroll down the page. It uses a simple CSS selector to determine which blocks to float.

In this episode you will learn:

  • How to install and configure the Drupal 7 Floating Block module
  • How to determine the CSS selector to use for your block
  • How to get a Drupal block to float as you scroll down the page

Thanks to OSTraining for sponsoring this episode of the Daily Dose of Drupal.

DDoD Video: 

Feb 28 2013
Feb 28

Episode Number: 

117

The Drupal 7 jQuery SelectBox module is a simple module that makes HTML select boxes easier to style. It replaces the HTML form select element with easier to style HTML markup.

In this episode you will learn:

  • How to download and install the jQuery SelectBox module
  • What the jQuery SelectBox module is doing in the background to make the select boxes on the site continue to work correctly

Thanks to Drupalize.me for sponsoring this episode.

DDoD Video: 

Nov 28 2012
Nov 28
Lullabot logo

Lullabot has trained thousands of Drupal developers & guided the development of some of the largest Drupal websites.

Nov 27 2012
Nov 27

Hello there everyone and welcome to another exciting episode of the Daily Dose of Drupal. As always; I am Shane, you can follow me on Twitter @smthomas3 or you can go to codekarate.com and sign up for the newsletter which I encourage you to do if you have not already. I will be releasing another issue here the next few days.

So today we’re going to talk about the Ajax Framework in Drupal 7. The documentation is listed here from api.drupal.org and it talks about how you can use Ajax … the Ajax Framework to help you more easily make Ajax Calls to your Drupal website. And this is for Drupal 7 so it’s relatively new in Drupal 7 or it’s quite a bit different than Drupal 6. I found a lot of examples on how to use Ajax within a form, however there was not just a simple straight forward example for how to use it with other types of HTML.

So I wanted to create a simple example of how you use Ajax which is just a simple link, basically you click a link, it makes an Ajax Call out to the server, it does a little bit of processing and sends you back something to put on your HTML page or your Drupal page.

So a few days ago and this is episode 54, that will be coming out shortly but a few days ago I created a blog post called Drupal 7, Java Script, Ajax Framework example from a link and this is basically what I’m going to be going over today but I wanted to put in a video format so I could explain it a little bit better and show a demo. But all the code is there so you can definitely take a look in code if you don’t want to follow along with this video but this video is going to show you how it works and walk through how the code is actually doing what it’s doing.
So here’s my example; just a very simple page, all it does is that when you click this link it goes out to the server, it returns the current time as you can see after a little bit it goes away and it replaces the link back again so can notice that the time has changed, it’ll go away in a few seconds and basically; what that’s doing is just making a very simple Ajax Callout to the server, it’s returning a response and then it’s doing some processing. You can go ahead and take a look at the network tab here and you can see it’s making a call to ajax/myajaxtest or myajaxtest/ajax actually, if you hover over this link you’ll look down here and you’ll notice it’s making a call to my-ajax-test/nojs.

So what this does is when you click this link it actually replaces the nojs portion of this path, you can notice if I open this in a new window it actually gives me a formatted page, notice the title is different where it has nojs in the URL but if you use the Ajax format which when you click this link, the Java Script replaces this nojs with Ajax, it gives you a different result.

You’ll notice it’s Jason data and then that will get processed. So let’s go ahead and take a look at the code and see how it’s doing that and what is it doing and how it works. Go ahead and start … it’s a very simple module, it’s called Ajax Link, it has three files; the first one is ajaxlink.info which is just your simple Drupal 7 module info file.

You’ll notice it does include a scripts file for ajax_link.jas. Let’s go ahead and take a look at the ajaxlink.module file; so it’s only about 79 lines so it’s not too long.

The first is it implements Hook Menu and the first item in Hook Menu is just ajax-test and this is just our ajax-test page. All it has in this page is just an Ajax link. So it calls this Ajax Link Page Callback which this returns a renderable array which if you’re not sure what a renderable array is, look at some of my previous Daily Dose of Drupal videos, I talked about them briefly and it returns a … basically what happens is this gets rendered and created into a link.

So it’s a type of link, the title is Ajax Link which is the title right here, the H Ref is what you see when you hover over it is my-ajax-test/nojs as you can see down here, I also add a prefix and a suffix onto this and you’ll notice I wrap the link inside in ajax-linkdiv but I also put a div after this and it’s just set up to be empty.

So if I look at this you’ll notice … let me go ahead and refresh this page, there is a link right here; ajax-display. So I have my ajax-link div which wraps this link and I also have this ajax-display div which is empty right now. You’ll notice when I click on the link this ajax-display gets the data that’s pass back from the server and it gets put inside that div. So that’s all put into the prefix and suffix, I mean this is a renderable arrays or somewhat similar to Form Arrays if you’re familiar with Drupal 6 Form Arrays. The important part is this part right here; this Ajax property of this array.

All it’s telling you to do is use Ajax which is going to make sure it switches this nojs to Ajax when it makes the Ajax Call, I’m using an effect fade, you can look inside the Ajax Framework documentation for a whole bunch of other options for what you can do with this, a lot of these are use within Drupal forms like I said so they’re not all needed and this was basically what I can stripped it down to and still have it work.

Now let’s look at this other menu item; my-ajax-test, you’ll notice there’s a % sign here and this is so we can take argument or either it’s going to be nojs or it’s going to be /ajax. So it’s going to pass in that page argument to this Ajax link Callback function which if we go down here you’ll notice I have the function and I just pass the variable that get’s called in, this is either going to be set to like I said, nojs or Ajax and it’s going to be a string.

So the first thing I do and this is where you do any of your database queries, your processing, anything like that, I just go ahead and get the current time using the PHP date function then I do the IF statement, I basically say if it’s Ajax that was passed in here I’m going to run some Ajax commands otherwise I’m just going to return a renderable array with the mark-up of this time variable that we previously created. So let’s look at this Ajax piece and unpack it a little bit; I basically set up a commands array, I run one command that’s going to do a replace so it’s going to look for this ID of ajax/display, it’s going to replace that entire div with this HTML right here.

So this is going to be a div with the same ID but it’s going to have this time variable that I created up above. The second one we’re going to do is its Ajax command to change, that is going to just mark this ajax-display with a change indicator and I’ll show you what that does in a second.

So we’ll go ahead and take a look … you’ll notice when I come to the page originally and I go to Ajax link you’ll notice there’s just div ID, ajax –display. As soon as I click that you’ll notice that there’s a new class added; ajax-change and what that allows you to do is it allows you to react on items that have actually been changed.

Now what I also needed to do I needed to run some Java Script after this Ajax Call went through and the easiest way I could find to do that was to create a very simple J query plug-in.

So what this does is it calls Ajax command invoke and it calls my J query plug-in which we’ll show you in a second, that’s just called Ajax Link and it executes that code. And then what it does is it wraps it all into renderable array type format and returns t using this Ajax delivers function and that’s how it works. Let’s go ahead and look at the Java Script which is just a few simple lines of code.

Basically this right here creates a new J query plug-in called ajax_link, all this does is it hides this ajax-link div which is a wrap around this link so you can see it hides it that’s why it goes away.

The Java Script or the Ajax from here, these two lines or this line specifically is what adds this to the page and then in the Java Script I proceed to set a timeout of 5 seconds or 5,000 mili seconds which basically will fade out this ajax-display, set the HTML to nothing so we’ll get rid of the contents and then re-show it so it’s there for the next time then I’ll also fade in this Ajax link so it comes back. And that’s all there is to it to making this happen, see it disappears, this is going to fade out and then be removed and it’s going to fade this one back in.

Very simple, it took me a while to figure it out and get it working because like I said there wasn’t a lot of examples of Ajax … using the Ajax Framework for simple links and there are probably multiple ways to do this, this is just one way, you can always add just a use-ajax class to a link and it will start using Ajax, there’s a whole bunch of different things you can read up on on the Ajax Framework, I just wanted to show you one example and explain how it worked so you can start using Ajax in your own modules and on your own Drupal 7 websites.

That’s it for this time on the Daily Dose of Drupal, check back in again for another exciting episode and we’ll be back again soon. Thanks for watching.

Sep 20 2012
Sep 20

Book cover: Sakai CLE Courseware Management: The Official GuidePackt, publisher of many worthy books about technology topics that have helped me know what I'm doing, is about to publish their 1000th book.

Many Packt titles, such as Sakai CLE Courseware Management: The Official Guide, books on Drupal, and jQuery have been my guides to the open-source technologies I use every day.

To celebrate, Packt is giving away gifts to their readers who register before 30 September 2012 over at Packt.com.

Thank you Packt, and congratulations!

May 31 2012
May 31

Posted May 31, 2012 // 0 comments

Wouldn't it be great if there was an easy way to access php.net or other documentation offline or on a plane?

UPDATE: Sadly, as this blog post went to press, two important updates came out that change the usefulness of this blog post. Dash is now ad-supported, and secondly, it ships with a Drupal DocSet available for download, so that's one fewer step you have to perform to have all the docs that matter to you in Dash.

There's a free as in beer application called Dash (available on the Mac App Store at http://itunes.apple.com/us/app/dash/id458034879?ls=1&mt=12) available for Mac OS X. Dash is a nice-looking documentation browser featuring several useful features, such as the ability to query it with a custom URL string (dash://YOURQUERY), which lends itself for use in tools like Alfred.

Dash can also download additional documentation sets for many open source technologies, including MySQL, PHP, and jQuery. It can be handy to search through the latest PHP API documentation no matter what kind of connection you're on, like so:

Dash - Documentation

In addition, Dash also has the ability to browse any API documentation that you have installed through XCode onto your system. (In fact, any files in DocSet format that are located inside the ~/Library/Developer/Shared/Documentation/DocSets directory can be read by Dash.)

In addition to the freely available DocSets that are available for major open-source technologies, it's easy to make your own DocSets using doxygen. I went ahead and made a DocSet for Drupal 7.x using doxygen. Not every method that's available at api.drupal.org is here, but it's a great start, especially if you want a single offline app where you can query offline documentation.

  1. Unzip the file
  2. Move org.drupal.docset to ~/Library/Developer/Shared/Documentation/DocSets/
  3. Launch Dash and start searching, like so.
Dash - Documentation

As Director of Engineering with Phase2, Steven Merrill is instrumental in propelling into its position as a leader in Drupal architecture and performance. His work in cloud-based hosting architecture, sophisticated caching structures, and ...

Mar 26 2012
Mar 26

Oh, multiselect form elements. When you need to display a long list of items and users can pick from several of them, they're the only way to go. Why can't they be prettier, though? Why can't they be easier to use? Why can't users select things in them without endlessly scrolling and holding down the control key? The standard HTML multiple-selection element leaves a lot to be desired, if you're looking to spruce it up, check out the new Improved Multi Select module.

Screenshot of an enhanced multiselect form element

Using Improved Multi Select is straightforward: install it, configure it, and any multiselect form elements on your site can be replaced by a handy two-pane "mover" interface that lets users keep track of what they've selected. In addition, it provides a simple search/filter mechanism: type a letter or two, and only the entries containing those letters will be shown. It's nothing fancy, but it can really speed up the process of picking entries from a long list. Since the entire widget is powered by jQuery and CSS, tweaking its appearance to match your site is relatively straightforward.

Setting up the custom widget is a bit of a change if you're used to FieldAPI widgets. Rather than providing a custom widget or form element, this module implements its picker using only jQuery and CSS. On its configuration screen, administrators can choose the URL paths or CSS selectors it should use to determine which multiselect form elements should be enhanced.

Screenshot of the module's configuration screen

The Lullabots are divided on this approach: On the one hand, it means that you can't explicitly choose the enhanced selection widget when building a new form or content type the way some site builders did with the venerable Multiselect module. On the other hand, the technique used by Improved Multi Select can easily be layered on top of any form or FieldAPI widget, even if it wasn't initially designed to take advantage of the swanky jQuery enhancements. Check out both modules if you're uncertain: either one will give some extra polish to your site's administrative forms.

*/
Jan 13 2012
Jan 13

We are now developing big Drupal 7 project where users have profiles with lots of fields.
They also have a node tied to them ("My story") which is created during registration.

Initially, these were "edit my profile" and "edit my story" links which were leading to a huge forms where user was scared by amount of elements.

And that is the result of UI improvements:
Modal 1

and after clicking "Edit" near "Hobby" item...
Modal 1

So, basically, user has separate small form for photo upload, separate form for about field, and separate form for small details.

I can tell you that filling in the profile in this way feels more natural, and is much more easy and convenient . And another cool thing is that all form validation errors appear without form reload, via ajax - and you don't have to do anything to achieve that - you just need to add "use-ajax-submit" class to form submit button! To avoid misunderstanding: ajax submit is the core functionality of Drupal 7, not directly related to modal forms, but it really shines when used with them!

Research

When we realized that we need some sort of dialogs, I decided to use Colorbox module since it provides similar user experience.
But after some evaluation I realized that forms integration is not working in the right way in Colorbox. (That's also what the project page says: "The attempt to support opening various forms in a Colorbox was in hindsight never a good idea. It will most likely be completely removed from the module. Form error handling and form redirects are complicated, a lot of code would be needed to do it correctly.") - it suggests to use Modal Forms module instead.

After some issue queues reading it was clear to me that ctools dialogs are not so mature as jquery.ui.dialogs library which is included (surprise, huh?) in Drupal 7 core.
After more research I found "PHP API" for jQuery dialogs - Dialog API module.
Interesting thing is that Dialog API project page mentions CTools, too - but I couldn't find any code that uses CTools in dialog module (tell me if you find it).

So, I decided to use Dialog API module in this project. It's not very stable so I had to apply some patches from issue queue to make example modules work.

Implementation

The basic idea of making some form appear in ajax is that we create new menu item (using hook_menu) which is "ajax-aware" menu item, and then create a special link to that menu item.
So, you need your own module, and you need to know how forms are working in Drupal, to create something interesting.

Example:
Let's imagine that we want to open part of node edit form (e.g. only node body field) in dialog.

Here is the demo (I've allowed anonymous to use the link):
http://analytic7.pixeljets.com/node/11

So, in our module, we write:

Dec 23 2011
Dec 23

Working with Columns

Posted on: Friday, December 23rd 2011 by Sonja Farrell

While we all wait for multi-column layouts with CSS3 to become standard across all browsers, we have to put our faith in js to help us solve our column problems. Here are two things I like to use.

Columnizer
Columnizer is great for when you have a lot of content that you want to make columns out of. It lets you set the width, height, number of columns, define which areas of content you DON'T want to split, and then will set up divs that allow it to flow properly. You can resize the page or add new content and the columns will stay fluid and reposition themselves properly - you don't have to worry about setting up your css all over again.

Equal Height Columns
Another problem I sometimes face is making sure that pre-defined columns (view teasers, for example) can often be of varying heights, depending on the content that is inside them. If the columns have some kind of background or other styling on them, this can look visually unappealing. I use this little snippet of code, which I found at http://abcoder.com/css/css-equal-height-columns/, to help me keep them all the same height:

$(function(){
var H = 0;
$(".equalheight").each(function(i){
var h = $(".equalheight").eq(i).height();
if(h > H) H = h;
});
$(".equalheight").height(H);
});

Voila! Two frustrating column-related problems solved, just in time for the holidays!

Dec 19 2011
Dec 19

Keeping visitors to your site reading and clicking (and -- let's be honest, viewing ads) once they've finished an article can be challenging. Often, lists of related articles or hot news are embedded at the end of each article to provide readers with an easy "next step," but these can easily be lost in the noise of a dense footer or link-heavy site. Enter Slidebox, a svelte module that uses interesting jQuery tricks to prompt users who've finished an article with a smoothly animated link to another one.

Screenshot of the Slidebox module in action

Setting up Slidebox is a snap: just turn it on, tell it what node types its "Next article" box should appear on, and you're ready to go. While it's possible to tweak the values that control how long the Slidebox animations should take, the defaults it ships with are sensible. The magic happens once you pop open a node and scroll to the bottom of its content: a tidy little box slides out from the right side of the window, prompting you with the title of the "Next Article." It's simple, attractive, and easy to dismiss. Also, because it appears when the user scrolls to the end of the node content, not the bottom of the page, it doesn't require that readers wade deep into a crowded site's footer or other "below the fold" content to see the link.

Screenshot of Slidebox settings

Slidebox's settings are currently pretty sparse: there's no way to exercise more control over what list of nodes should be used to find the next article, and you can only customize the contents of the "Next Article" box by cracking open theme template files. There's a lot of interest in the issue queue around turning the module into a more API-driven tool that supports Views and other entities, however, so keep a close eye on how it evolves. For now, it's a clean and simple tool that does one thing and does it well!

*/
Nov 13 2011
Nov 13

In Drupal 7, it's easy to show calendar (ui.datepicker) date selector to user. (it's not hard in Drupal 6, too - but in d6 you need to enable http://drupal.org/project/jquery_ui to get the calendar, I think)

You just write several lines of js code, and you get a calendar.
But things are quickly getting more complicated when you need to allow visitors to choose from dates that have some nodes "attached" to them only. E.g. if you have news view, and you don't have news for 13 Nov 2011, there is no sense in allowing user to click on this date in calendar (I'm talking about the case when clicking on calendar redirects user to views page /news/2011/11/02 , where 2011/11/02 is a context filter for news nodes)

Here is the screenshot illustrating the desired functionality:

Calendar with available dates

(so, user can click on Nov 3 - but not on Nov 4)

I'm not sure if there is a module to do the stuff I've described in this post, but I couldn't find one, and since I was asked 3 times to implement such date-restricting feature - I hope it can be useful for someone.

Okay, so the overall idea is:

  1. Use jquery.ui.datepicker to show calendar to user
  2. Use several lines of js code to hook into calendar days rendering, and allow/disallow date selection if the date has/doesn't have available nodes.
  3. Implement corresponding PHP method to query database and prepare data for code in step#2.

And here is the code (I recommend to put it to /yourmodule/js/calimprove.js):


(function ($) {

Drupal.behaviors.calrestrictselection = {
attach: function(context) { if (!$('body', context)) return; // don't run the processing if it's an ajax call

{gfm-js-extract-pre-1}

// just call mywebsite.com/dateloadevents which returns JSON array of available dates (see php code of this page below) // and enable calendar for textfield afterwards $.getJSON(Drupal.settings.basePath + 'dateloadevents', {}, function(data, textStatus, jqXHR) { eventdays = data; $('#views-exposed-form-events-page #edit-start-value-datepicker-popup-1').attr('disabled', false); });

{gfm-js-extract-pre-2}

} } })(jQuery);

PHP code for /dateload_events callback (defined in yourmodule/yourmodule.module):

Oct 24 2011
Oct 24

Revamped Captions with jQuery, CKEditor and IMCE

Posted on: Monday, October 24th 2011 by Sonja Farrell

(Consider this a re-vamping of Scott's original post, located here.)

One of my recent projects was for a client who wanted to place inline images - and potentially captions - into the body text of a node, without any HTML knowledge. I decided to use Scott's solution, reworked to suit my client's needs.

One thing I felt was missing in the original script was the ability to set margins based on which way the image was floating: I only want to set margins on the right side if the image is floating left, and on the left side if it is floating to the right, otherwise my image will not be flush with the margins of the article itself.

I also wanted to add imagecaching ability so that the image could be clicked on and enlarged, since it would most likely be scaled down to fit in the article. So, if you are going to follow my lead, be sure to create an imagecache preset, unless you want the image to open to its original (and potentially too large) size.

.caption { 
	margin-top: 0;
	color: #935423;
	font: italic 14px Cambria, Georgia, serif;
}

Styling the captions that would be appearing underneath the image. Setting 0 as a top margin enables the caption to stay flush to the bottom of the image. Make sure to do this, otherwise you might get margin rules from somewhere else that cause the caption to get pushed down.

Upload the image, make sure to type out the caption in the "alt" attribute textbox, and float your image either right or left using the "alignment" dropdown (FCKEditor and CKEditor provide this option, I'm not sure about other WYSIWYG modules) .

On to the fun part - adding the caption, float and imagecache with js.

Drupal.behaviors.ArticleCaptions = function(context) {  
  
  //Find all images in the body and run a function on each of them
  $('.article img').each(function(i) {
    
    //Grab the alt attribute from the current image.
    var caption = $(this).attr('alt');

    //Grab the src attribute from the current image.
    var imgSrc = $(this).attr('src');

   //Grab the width of the image.
 		var imgWidth = $(this).width();

		//Find out which way the image is floating so that you can set proper margins
   	var img_float = $(this).css("float");

		//Setting the path for imagecache
    var fileDirectoryPath = $('#file_directory_path').val();
    var presetPath = imgSrc.replace(fileDirectoryPath, fileDirectoryPath + '/imagecache/article-inline-image');
    
		//Apply the float, margins and width
		$(this).wrap("")
    
		//Pop a div tag with class caption after the current img tag. Make sure to provide an if, in case the image doesn't have a caption.
    if(caption) {
      $(this).after(""+caption+"")
    }

		//Wrap the image in a lightbox or other modal for the enlargement.
    $(this).wrap(""); 
  });
};

Include this js file in your theme's .info file or on the page(s) where you want it to fire.

drupal_add_js(drupal_get_path('theme', 'yourtheme') . '/js/caption.js');

Now you should have an image with a caption, proper padding, and clickable to open a larger version.

Modules used were: CKEditor, IMCE.

Oct 13 2011
Oct 13

Theming forms is always a little bit of a handful; designers like to create slick looking forms with rounded corners, drop shadows and custom looking form elements. Unfortunately it's not always an easy task to bring the design to life, but with a bit of css and jquery it is very easy to get your drupal forms looking a little less Drupal-ey.

Overview

We have previously gone over theming the markup of your Drupal forms for easier theming, now let's look at what tools are out there to add a bit of pizzaz to those boring select list and checkboxes. Select lists and checkboxes are incredibly annoying to deal with because unlike text fields you just can do much with css. Without any theming you end up with a very disjointed looking form. I this case we have to look to jquery to save the day. We are going to be using two lightweight jquery scripts to transform those stubborn form element into slick custom checkboxes and select lists.

Preparations

We are going to need these two scripts.

The select box script comes with a .css file as well, you can drop that in your theme's css folder and we will load it like any other css file.

and lastly you will need an empty file to supply the jquery to the theme, I usualy go with the incredibly creative scripts.js.

How-to

First things first you'll want to create a folder in the root of your theme called "scripts" if there isn't one already. Once you have that done you can toss those three files in there. Then we want to load those files through the theme. The way we do that is declare them in your themes .info file like this.

stylesheets[all][] = css/jquery.selectBox.css scripts[] = scripts/jquery.selectBox.js scripts[] = scripts/jquery.checkbox.js scripts[] = scripts/scripts.js

Save your file and give your site's cache a clear at admin/config/development/performance or if you use Administration menu you can flush it through the link they have. Your theme should now be loading your scripts so we should probably start applying them to something.

First we can apply the select box script to all select boxes on the site with this

(function ($) { $(document).ready(function(){ $(function() { /** * Fancy select boxes */ $("SELECT").selectBox(['default']); }); }); })(jQuery);

If you would like something a little more targeted just throw in the css selector you'd like it to apply to.

Under that you will add a similar line for your checkboxes again if you want to target specific ones just add a css selector.

(function ($) { $(document).ready(function(){ $(function() { /** * Fancy select boxes */ $("SELECT").selectBox(['default']); /** * Fancy checkboxes */ $('input:checkbox:not([safari])').checkbox(); }); }); })(jQuery);

What you will get is some nice markup to theme away.

For select boxes you will get this.

<a class="selectBox selectBox-dropdown"> <span class="selectBox-label">Item 15</span> </a>

The ul with all your options will ouput at the bottom of your page and be absolutely positioned below the selectbox.

For checkboxes you will get this.

<label><input style="position: absolute; z-index: -1; visibility: hidden; " type="radio" /> <span class="jquery-checkbox"> <span class="mark"> <img src="empty.png" /> </span> </span> 1st radio button </label>

With these 2 scripts you should be able to get your form looking quite unique and a lot less drupal-ey. I will offer a little disclaimer, it is very easy to get carried away with adding jquery to forms. Having used full jquery form suites in the past I urge you to steer clear of them. I've only created more headaches with them and prefer to bring jquery in to help with only the most stubborn of form elements.

Aug 23 2011
Aug 23

In D7, drupal_add_js can handle external javascript files, but in D6 it cannot handle external js in a nice way.

Most people instead include external js with a preprocess_page function, or hook_footer. However, in both cases, the external js is added before the scripts added by drupal_add_js (the $scripts page template variable) which can be a performance problem.

I've seen one kind of nasty workaround that (ab)uses drupal_add_js to make a call to document.write:

http://www.wootenswebdesign.com/load-external-js-file-drupal-6

Here's another technique to include an external script with drupal_add_js in Drupal 6 that I find a little more readable.

Create a local js file (in your module or theme) and include that with drupal_add_js:

drupal_add_js(drupal_get_path('module', 'my_module') . '/my_module.js', 'module');

In your local js file, create a new script element and append it to the body:

(function ($) {
  Drupal.behaviors.myModule = function(context) {
    var externalScript = $('<script></script>').attr('type','text/javascript').attr('src', 'http://example.org/example.js');
    $('body').append(externalScript);
  }
}(jQuery));

Jul 21 2011
rmo
Jul 21

I have something really, really cool I wanted to share, and I'm sure you won't be disappointed. In scenarios where displays of graphs/charts are needed, either a client wanted to showcase some statistics to their users, or an administrator wanted a summary of activities of what is currently going on within the site. If the graphs/charts are simple, you can just use the Google Charts API, but for something more advanced or simply for the "looks", Google Charts doesn't do a good job. While coding these manually using pure Javascript and jQuery is possible, it is a nightmare to do so...

read more

May 08 2011
May 08

I have a client who wants an exposed filter but, instead of having a select drop down, would like to replace it with a set of links.

This is not an uncommon use case, and there are some existing examples on how to do this. It's pretty easy, since views will read a value for the exposed filter from a query string.

But what if your exposed filter form is in a block? Exposed filter forms in blocks only work if you enable AJAX for the exposed filter, and the ajax dynamically replaces the contents of the block with the results of your selection.

In this case, in addition to replacing the select with links, you need to add a javascript event handler to the links that will duplicate (or invoke) the ajax.

Step 1. my_theme_preprocess_views_exposed_form

You could also use hook_form_alter, for some reason this seemed more straightforward to me.

We're going to go through the select key/value pairs, and create a set of links that we add to the $variables array. We're also going to include a javascript file that will be created in step 2.


function my_theme_preprocess_views_exposed_form(&$variables) {
// return an array of links that we can display in the form
$filter_links = array();
foreach ($variables['form']['tid']['#options'] as $key => $value) {
$filter_links[] = l($value, '', array(
'attributes' => array(
'class' => 'filter-link',
'data-value' => $key,
),
'fragment' => 'filter',
));
}
$variables['filter_links'] = $filter_links;


drupal_add_js(drupal_get_path('theme', 'my_theme') . '/js/exposed_filter.js', 'theme');
}

Step 2. exposed-filter.js

Add js/exposed-filter.js to your theme.

We're going to add a click event handler to each of the links we created in step 1 that will set the value of the existing submit input, and will submit the form. Once the form is submitted, the jQuery behavior supplied by Views takes over.


Drupal.behaviors.my_theme_exposed_filter = function(context) {
$('a.filter-link', context).bind('click', function() {
$('select#edit-tid').val($(this).attr('data-value'));
$('div.view-filters form').submit();
});
}

Step 3. views-exposed-form--VIEW-NAME--block-DELTA.tpl.php

Copy views/themes/views-exposed.form.tpl.php to your theme so that you can print the links you added in the preprocess function.


<div class="links">
<?php
foreach ($filter_links as $link) {
print "$link\n";
}
?>
</div>

Optionally, you can remove the submit button here if you want.

Step 4. Hide the select input in CSS

There's more than one way to do this. However, it's required that you hide the select instead of omitting it, since Views will read from the select when the form is submitted.


form#views-exposed-form-VIEW-NAME-block-DELTA div.views-widget {
display: none;
}

This is, admittedly, an ugly way to do it, but it works. An extension to the better exposed filters project would be much nicer.

Also, *why* is it that exposed filters only work in blocks via ajax? Shouldn't it be possible to reload the page that a block is on with a query string that the block view can recognize? You could use the block ID to disambiguate in the case that multiple blocks with exposed filter forms were on the same page...


Apr 17 2011
Apr 17

That's right! All you Drupalers out there know how frustrating it can be to use jQuery 1.3.2 with Drupal 6 and the lack of an "official" contrib solution out there to fix this only makes matters worse.

I've tried a couple methods in the past but have had no luck. However, I finally came across an (apparently) 100% working method to use jQuery 1.5.2 and jQuery UI 1.8.11 Drupal 6!

The solution

Based on a couple discussion threads and patches over on drupal.org I've managed to piece together alternative versions of both jquery_update and jquery_ui that just work!

What are you waiting for? Check out the projects on github, download & enjoy!

Here are the links:

Disclaimer

There is no warranty of any kind for this and you're using these modules at your own risk! I've tested them extensively on javascript-heavy websites and have had no issues whatsoever with vanilla Drupal, Pressflow, Advanced Aggregation (a patch is needed if you want to use Google's CDN), Boost, Date's Popup, Views, Quicktabs, GMAP and a bunch of other modules I can't remember at the moment. If find any issues please use the issue queues, thanks!

Peace!

Apr 05 2011
Apr 05

Shortly after Drupal 7 launched the version of jQuery shipped with Drupal became outdated. Drupal 7 ships with jQuery 1.4.4 but the jQuery community has shipped jQuery 1.5.2. When we are developing the JavaScript in our shinny new Drupal 7 we are already using an outdated version of jQuery. But, there is a solution. The jQuery Update module provides an update for core to jQuery 1.5.2 and jQuery UI 1.8.11.

If you are writing a Drupal 7 module that uses jQuery please consider depending on jQuery Update or at least make sure the script works with jQuery 1.5.2.

CDN Support

As an added bonus, a new feature in jQuery Update 7.x-2.2 makes it worth using for all sites. It provides integration with the Google and Microsoft CDNs for both jQuery and jQuery UI. In the performance admin settings page there is a selector to choose where to get jQuery from.

Providing Minimized and Full Source Script Versions

The jQuery Update module stores if the JavaScript should be served in a Development (full source) or Production (minified) version. When Development is chosen the full scripts are served which makes debugging and developing easier.

This feature is something other modules can tap into. This state of the JavaScript is stored in the Drupal variable jquery_update_compression_type. The possible values are min and none. That means modules can provide both a production version and a development version of their JavaScript with easy access to switch between them. An example with the script versions script.js and script.min.js:

$min = variable_get('jquery_update_compression_type', 'min') == 'none' ? '' : '.min';
drupal_add_js('path/to/script' . $min . '.js');

Two modules already tapping into this are the bibly and underscore modules.

As as we crank out Drupal 7 websites lets create some awesome JavaScript.

––
Posted in:

Mar 21 2011
Mar 21
Anton Sidashinsenior developer, Pixeljets co-founder

I'm a web developer specializing in PHP and Javascript, and Drupal, of course. I'm building Drupal projects since 2005, and I was working as full-time senior engineer in CS-Cart for a while, building revolutionary e-commerce software. In my free time, I enjoy playing soccer, building my body in gym, and playing guitar.

Drupal.org ID: restyler
Jan 04 2011
Jan 04

When doing development work, from time to time it is handy to be able to look up documentation. Bookmarking manuals is handy, but often you still need to search for the function you're after. Firefox, and possibly other browsers (not Chrome or Chromium), allows you to setup a keyword bookmark linked to a search.

I've setup a few search bookmarks for development resources. This is how I've done it:

  1. Select Bookmarks > Organise Bookmarks... from the menu.
  2. Right click on the bookmarks menu (or any folder) on the left pane
  3. Select New Bookmark... from the context menu
  4. Drupal bookmark example
    Fill in the information for the bookmark, the import piece is the keyword, that will allow us to search.
  5. Click save and return to the browser

Now when we want to search the Drupal 7 API, we can just type "dapi ",>

Example Drupal API search in location bar

Now we should see the appropriate page from the Drupal API documentation.

Example Drupal API page

The same method can be used for other PHP web app developer resources, here are some which I'm using.

  • I've found Google to be the best resource for getting help with javascript

I could have implemented this using OpenSearch plugins, but that requires changing the search provider everytime I want to look for something. By using keyword bookmarks I just type the keyword and the search term into the location bar.

Feel free to share your keyword bookmarks in the comments.

Dec 10 2010
Dec 10

Facebook-like name filtering in linear time.

More and more of Facebook is becoming driven by AJAX reducing the actual page refreshes and making the experience smooth and quick (of course when it works 100%). Ever noticed when you view all friends in a dialog box and start typing a friends name? The results are nearly instant and match depending on first name, last name or full name. For example, typing “Jo” would display results for John Doe, Jane Jones, etc.


read more

Nov 24 2010
Nov 24

In my always-continuing quest to find the perfect online calendar display/management solution, I have found the next level of calendar display/management bliss.

Previously, I was pinning all my hopes on Drupal's very robust, but often complex and confusing, Calendar.module (in use by almost 50,000 websites—and for good reason—it's extremely adaptable). The module provides many different displays, and gives you the ability to link directly to a specific day/month/week... but it (a) is relatively slow to allow switching from month to month, (b) requires a rather complex view, with arguments, which can be confusing for first-time users, and (c) it takes patience to theme it well.

I love the Calendar module, and I still use it on a few sites where necessary, but I've found a new contender that has nothing to do but improve; that condender is the FullCalendar module, which is based on the great fullcalendar.js jQuery-based calendar library by Adam Shaw.

Fullcalendar Display
This is IE. It's easy enough, though, to add better styling to a fullcalendar.

FullCalendar is simply a views display that takes a list of event nodes (as long as your node has a date/time attached, it will work), and displays them in a beautiful calendar display that works across all modern browsers, and even most mobile browsers (I've tested Android, iOS 4, FF, Chrome, Safari, and IE so far).

I had a little trouble getting the calendar to display in IE6/7, but I supplied a quick patch to fix that issue.

One thing I have yet to test is the performance of fullcalendar when displaying large batches of calendar items (in this case, calendar.module might be better—if you need to show thousands of events on a calendar from many years prior). The biggest calendar I have right now displays about 200 items. As time goes on, I could either simply let the list build to the point where fullcalendar slows a bit, or limit the date range so events from only the past few months show.

Oct 08 2010
Oct 08

DrupalSN posted a nice guide to styling exposed View dropdown menus with a JQuery plugin and some stylish CSS.  The guide there worked perfectly except for the step where he hid the submit button.  I changed this:

.views-exposed-form label,
.jquery_dropdown_page .views-exposed-form .form-submit {
  display: none;
}

to this:

.views-exposed-form label,
.views-exposed-widget .form-submit {
  display: none;
}

Also, I wanted a fancy hover effect over the dropdown menu itself, so I added the following style and created a hover version of the background image for the dropdown menu:

div.jquery_dropdown_header:hover {
  background:url(images/jquery_dropdown-header-hover.png) left top no-repeat;
}

Why the module?  The module turns your select box into an unordered list which is much easier to style.  My only gripe is that it fails to show the "any" option on the exposed dropdown once you navigate away from it (it shows up fine on the initial page load).

Jun 01 2010
Jun 01

No. Neil Drumm and I looked at collaborating, but Neil had already implemented most of the jQuery side of the d.o dashboard, I didn't have much time, and it had different requirements to jQuery.dashboard(), especially with regards to the version of jQuery and jQuery UI it was dependent on.

Apr 27 2010
Apr 27
my vertical bar graphs from google charts

For my recent post comparing compression methods for database dumps I had some very simple data, and wanted to present some very simple charts. None of the many charting modules for drupal seemed to be simple enough for me, so I borrowed some code I found on the web and made my own drupal module.

There are lots of charting solutions for Drupal - several of them described and compared in two very comprehensive posts linked to from the Comparison of Charting Modules page on drupal.org. However, most of these modules seem to cater for situations where data is coming from the Views module and / or stored in CCK fields. My requirements were simpler still; I had two very small tables of data which I wanted to chart, and when I say tables I mean little HTML tables, not tables in the database.

My web searches soon threw up a couple of ways to do this using javascript on the client side. The most interesting of which included auto-table-to-chart which uses open flash charts, more than one blog post giving examples of HTML tables to Open Flash Chart using jQuery, and variations on a fairly old (2008) post by Chris Heilmann titled Generating charts from accessible data tables and vice versa using the Google Charts API.

I decided to settle on the simple, lightweight approach of using javascript to examine the HTML table, and generate the request URL to generate a chart using google chart api. 90% of the work had been done for me by Martin Hawksey in a post he made building on Chris Heilmann's original. The javascript in the example given there dealt with more than one column of data, and added line graphs to the original's support for pie charts.

I wanted vertical bar graphs, and to build the javascript into the drupal framework. This was pretty easy. First I made some simple (and pretty crude) changes to the JS. I made it a little more jquery-friendly using document.ready:

< (table2graph = function(){

> $(document).ready(function(){

... then I made a couple of tiny changes to the JS to accommodate vertical bar graphs (bvg); to this section:

if (cht=='lc'){

charturl += '|1:|'+yMarks.join('|')+'&chxp=1,'+yMarks.join(',');

charturl += addDataPoints(tData,'o',dpt);

charturl += '&chco='+niceCol.slice(0,tData[0].length).join(',');

charturl += '&chg=0,16.666667';

}

var chart = document.createElement('img');

... I added the following (using a line of code which was already there to give my bar graphs nice colours like the line charts which were already supported):

if (cht=='lc'){

charturl += '|1:|'+yMarks.join('|')+'&chxp=1,'+yMarks.join(',');

charturl += addDataPoints(tData,'o',dpt);

charturl += '&chco='+niceCol.slice(0,tData[0].length).join(',');

charturl += '&chg=0,16.666667';

}

if (cht=='bvg'){

charturl += '&chco='+niceCol.slice(0,tData[0].length).join(',');

}

var chart = document.createElement('img');

I also needed to add my bvg charts to an if statement which was dealing with the axis for line charts differently than for pie graphs, like so:

< if (cType=="lc"){ stringOut += tArray[0].join('|').replace(/ /gi, '+');}

> if (cType=="lc" || cType=="bvg"){ stringOut += tArray[0].join('|').replace(/ /gi, '+');}

else{

Now all that remained was getting my revised JS code into my drupal site. One easy way would be using drupal_add_js from within the theme. However I decided, as this is (arguably) more about functionality than themeing, to create a very simple drupal module to add the JS. I called the module chartfromhtml and pretty much all it does is this:

/**

 * Implementation of hook_init().

 */

function chartfromhtml_init() {

  drupal_add_js(drupal_get_path('module', 'chartfromhtml') . '/chartfromhtml.js', 'module', 'footer');

}

With this module enabled, I could use the google chart functionality from anywhere in my drupal site the same way as in the demos this is all based on. So to get charts generated from my small HTML tables, I simply added the following self-explanatory classes to the markup:

<table class="tochart typebvg size700x250">

If you think you might find this useful, you're welcome to download the chartfromhtml module (as a simple tarball for the moment). I believe Chris Heilmann's original code was creative commons, and I don't see why this couldn't become a proper GPL'ed drupal module if there's any demand for it.

My list of to do's would include rewriting the JS to be more jquery-esque, and adding support for more of the many chart types that google's API supports. I suppose there's no reason why open flash charts couldn't be an option as well.

AttachmentSize 2.61 KB
Apr 14 2010
Apr 14

Drupal's template files (*.tpl.php) are not really templates. This is what my DrupalCon core developer summit submission is about. The slides briefly explain why tpl.phps are not real templates, what real templates are, why this is a problem for the Drupal project and community, and mentions some possible solutions to the problem. It also provides some basic guidelines as a starting point for tpl.php standards, should that be pursued.

Download the slides here.

Attachment Size Drupal tpl.phps are not templates.pdf 294.24 KB
Apr 12 2010
Apr 12

jQuery for Designers and Themers is a fun interactive session at DrupalCon San francisco on getting started with jQuery. It is targeted at designers and themers but is suitable for anyone with a decent understanding of HTML and CSS — no programming experience is necessary. It doesn't include any PHP, and only basic programming concepts are introduced.

The session is early on Tuesday 20 April in room 307 (Commerce guys) at DrupalCon SF at 8:30am.

The sample code is available at Drupal.org/Project/jQ4DaT and slides are available at TinyURL.com/jQuery-Designers (Google Docs).

Some other related or similar sessions include;

Feb 26 2010
Feb 26

jQueryjQuery for Designers and Themers is a fun interactive session on getting started with jQuery. It is targeted at designers and themers but is suitable for anyone with a decent understanding of HTML and CSS — no programming experience is necessary. It doesn't include any PHP, and only basic programming concepts are introduced.

If you want to see this session at DrupalCon San Francisco you'll need to vote on it here it is at 8:30am on Tuesday 20 April in room 307 (Commerce guys) at DrupalCon SF.

I've presented sessions like this one twice before. The first time at DrupalCon Paris September 2009, and the second time at DrupalSouth Wellington January 2010, where it was successful and well received and both times.

Sample code is available at Drupal.org/Project/jQ4DaT and slides are available at TinyURL.com/jQuery-Designers (Google Docs). (They will be updated.)

Some other related or similar sessions include;

Pages

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web