Stand With Ukraine. Stop Putin. Stop War.

Some time ago @tlyczko suggested I write a how-to on using MIGX with image Galleries or Sliders. As I'm using MIGX all over the place, I guess it can't hurt to spread the love with this How-To! We'll go over the basics of MIGX and work towards the new slider I recently set up for use in my blog.

Try MoreGallery!

While you can still use MIGX for image galleries, I have recently released MoreGallery, a custom Gallery resource for MODX, as part of my new business modmore. The user experience is significantly better compared to using MIGX, so check it out.

The end-result of what we're doing will be the following slider which you may have seen in my MODX Meetup post recently:

  • Cute Kitty (that may or may not kill you if you stroke it)

    Cute Kitty (that may or may not kill you if you stroke it)

  • Sleepy Dog

    Sleepy Dog

  • Awesome Shirt

    Awesome Shirt

  • Some Light Reading

    Some Light Reading

Requirements

  1. A MODX Revolution site. This one is on 2.1.5, but MODX Revolution 2.2 will work fine..
  2. The MIGX custom TV input type installed (available via package management or from here).
  3. A slider or gallery script we'll want to integrate. I went for FlexSlider by WooThemes as it adjusts itself to its containers' width (which is quite important with a responsive design!). It's also quite configurable, supports swiping on smart phones and it looks pretty good out of the box.
  4. The phpthumbof snippet installed via the Package Manager for autocropping images.
  5. Some images!

Getting Started

There's a few parts involved. There's the back-end MIGX TV we'll need to set up, and we need to take that data to our front-end to put our slider to work. As this tutorial assumes WooThemes' FlexSlider, we will make sure the markup is exactly what that needs - but it should be easy enough to adapt for other sliders as well.

Step 1: Setting up the MIGX TV to manage Images

Let's start with setting up the TV. The beauty of MIGX is that you can define the fields you want. You should basically see it as a table, and you get to define the headers. In this case, I will want three different fields:

  1. An image input field to select an image from the file system (or in 2.2, from your media source).
  2. A simple text input to give a caption / tag-line / description. FlexSlider handles captions quite nicely, so we'll be using that.
  3. I'm also adding a text input for "Set". As we'll see later, I'm using this to manage an infinite amount of individual sliders which could be added at any point in the resource.
You can use different fields if you want, though the rest of the tutorial will assume the fields above.

Now let's start creating the TV. Here's a quick slider with an overview of the things you should be seeing (in 2.1-UI style), in case the blurb of text catches you off-guard.

  • Creating the MIGX TV: General Information

    Creating the MIGX TV: General Information

  • Creating the MIGX TV: Input Options (p1)

    Creating the MIGX TV: Input Options (p1)

  • Creating the MIGX TV: Input Options (2)

    Creating the MIGX TV: Input Options (2)

  • Desired end result

    Desired end result

  • Desired end result, popup window to select the options

    Desired end result, popup window to select the options

Create a new Template Variable via the left-hand Elements tab, and give it a proper name. I called it mh.images, so if you gave it a different name make sure to replace [[*mh.images]] with the right one further on in the tutorial. On the Input Options tab, choose the "migx" input type drop down. If you're not seeing it there, make sure you have installed the MIGX Package through Package Manager. You will notice that a couple of options have been added to the bottom of the screen, including Form Tabs and Grid Columns. If you're not seeing this and running MODX 2.0, sorry - it's slightly different there and you really should consider updating soon.

Note: Both the Form Tabs and Grid Columns field expect to be fed a valid JSON string. JSON is basically a way of showing objects, arrays or just key -> value combinations in a way that is supported in pretty much any programming language available. There's a technical specification and some examples available on the official JSON website.

Setting the Form Tabs

As the name implies, the form allows different tabs to be used. Every tab has a caption and a number of fields. There are some advanced things you can do with this, though most usages will only use a single tab so we'll keep it at that for now. Here's the Form Tabs JSON I am using:

[{"caption":"Image", "fields": [
    {"field":"set","caption":"Set"},
    {"field":"description","caption":"Description"}, 
    {"field":"image","caption":"Image","inputTV":"image"}
  ]
}] 

Let's just briefly go through that per line:

  1. Line one shows us opening the array of tabs with a square bracket ( [ ), and opening the tab object with the curly bracket ( { ). After that we define our caption option with a value of Image. Note that it is important to use double quotes ( " " ) for both the properties and its values, as single quotes are not according to the spec and can have unexpected results. If you have any double quotes inside any properties or values, escape it with a slash ( \ ). After we defined our caption, we go ahead and start defining the "fields" array by opening it with the square bracket ( [ ).
  2. We define our first field, the set in this case. The "field" property defines we want to be able of accessing it in the code as "set" - this would be the name part of a regular text input as well. Next we give it a caption of "Set", which will be shown in the form as field label.
  3. Next we define the description field in the same way.
  4. Line 4 defines the image. What's special here is that it uses another TV (with the name "image") as input type. This is a very powerful feature of MIGX that allows you to use other TVs to build up your form. In this case, the "image" template variable is really simple. Input type is an image, and in 2.2 you would assign it to the right media source as well. You don't need to link it to a template to make this work. Another way you could use this (which I use in another MIGX powered TV) is to build radio boxes or select boxes.
  5. Line five closes the fields array. Also note that the last field definition line (image) does not end with a comma - this is important! If you leave a trailing comma in an array it will not be able to process your JSON, and your form would not work.
  6. Line six closes the tab object as well as the tabs array.
Having put that in place (and having created the "image" TV!) you should have a working form, though there's no grid to show its values yet..

Setting the Grid Columns

The Grid Columns are what builds up the actual table (grid) in the resource's TV panel. You get to define the headers for that grid with the Grid Columns input option.

Here's the (again, JSON) definition I'm use:

[{
  "header": "Set", "sortable": "true", "dataIndex": "set"
},{
  "header": "Description", "sortable": "true", "dataIndex": "description"
},{
  "header": "Image", "sortable": "false", "dataIndex": "image","renderer": "this.renderImage"
}]

Per line:

  1. Open the array of headers with the square bracket ( [ ), and the first column header object with the curly bracket ( { ).
  2. We give the header a title of "Set", say it can be sorted (optional), and tell it the dataIndex is "set", which corresponds with the Set Form Tabs definition which had "field":"set".
  3. End Set / start Description
  4. Give the header a title of "Description", sortable, and match it with the Descriptions' field with dataIndex.
  5. End Description / start Image
  6. Give the header a title of "Image", not sortable, and match it with the Images' field with dataIndex. We also define a renderer which is an ExtJS thing that allows you to change how the value is displayed. In this case, the "this.renderImage" renderer will automatically take the selected image URL, and show a thumbnail for it instead of the url itself.
  7. End Image object, end headers array.
Now that's done, you can assign it to your template and check out the result. No grid or form showing up? Go back to your tab fields and make sure you have all properties and their values enclosed in double quotes, not single ones, and if there are any trailing or missing commas. Can't find anything wrong? It's probably something easy to miss. Use JSONLint to validate the JSON.

Step 1a: Fill in some data!

Find some kitty or dog pictures and put them in through the new magical MIGX TV.

Step 2: Take the data to the Front End

Now you could do something funny to your visitors, and just throw your [[*mh.images]] TV somewhere in the template and telling them to imagine it's all images.If I would have done that to you, the example slider would have looked something like this (without the nice indentation I threw in for readability) instead:

[
    {
        "set": "example1",
        "description": "Cute Kitty (that may or may not kill you if you stroke it)",
        "image": "assets/content/filler/2012-04-10 11.32.16.jpg"
    },
    {
        "set": "example1",
        "description": "Sleepy Dog",
        "image": "assets/content/filler/2012-03-22 10.07.09.jpg"
    },
    {
        "set": "example1",
        "description": "Awesome Shirt",
        "image": "assets/content/filler/2012-03-13 14.23.31.jpg"
    },
    {
        "set": "example1",
        "description": "Some Light Reading",
        "image": "assets/content/filler/2011-11-08 13.43.53.jpg"
    }
]

Correct, that's a JSON array with our fields and values!

Now, luckily, MIGX comes with a snippet called getImageList which can help you make sense of that JSON. As I'm a developer and it's easier for me to write an 8 line piece of PHP to parse it, I tend to write that 8 line php myself and not use getImageList at all. If you do want to use getImageList, you can find the documation for it here.

Here's the snippet (which I called mh.parseMIGXToGallery) I'm using to generate the markup (from a &tpl chunk) based on the TV data:

$input = $modx->fromJSON($input);
$output = array();
if (!$input || empty($tpl)) return 'no stuff';
foreach ($input as $row) {
  if (isset($set) && !empty($set) && ($set != $row['set'])) continue;
  $output[] = $modx->getChunk($tpl, $row);
}
return implode("\n", $output);

That looks easy enough don't you think? Let's just go through it line by line again.

  1. We take the $input variable (which is populated by the data passed in by &input on the snippet call, more on that later), and parse the JSON into a php array.
  2. We set up an empty array to hold our output.
  3. We make sure the $input variable isn't NULL or false, which would mean there was either no JSON passed or it was not valid, as well as we check the $tpl variable (passed from &tpl in the snippet call) isn't empty. If either condition is true we'll return an error "no stuff' which is a hint for you, as content manager, that something's not set up right.
  4. Next we loop over each item in the $input array as $row.
  5. We check if the $set variable (which is set by calling &set on the snippet) is set, and if it's not empty we match it against what the value of "set" in the current row is. If it's not the same as what we want, we continue to the next item in the array.
  6. We get the chunk by the name of $tpl, and inject the values of the current row into it as placeholders.
  7. We close the foreach loop.
  8. Finally we glue together the $output array, separating every entry with a simple line break, and return it to the page.
So to sum that up, we take our input, loop over the individual rows and if they belong to the set we defined it gets the chunk we defined and adds that to the output.

Putting the Snippet to work

Now all we need is a snippet call where we want our images to appear and a chunk that matches the markup required for FlexSlider. Here's how we'd call the snippet:

[[mh.parseMIGXToGallery? 
  &tpl=`mh.images.gallery.tpl` 
  &input=`[[*mh.images]]` 
  &set=`name of the set`
]]

and the mh.images.gallery.tpl chunk to match what FlexSlider is looking for is as follows:

<li>
  <img src="[[+image:phpthumbof=`w=750&h=400&zc=1`]]" alt="[[+description:htmlentities]]" />
  <p class="flex-caption">[[+description]]</p>
</li>

and we will also need to wrap the entire snippet output in a div with an unordered list, according to the FlexSlider documentation. To make that easier (and a helluva lot more client friendly), is to put all that in a chunk. My chunk is called mh.slider and has the following contents:

<div class="flexslider">
   <ul class="slides">
       [[mh.parseMIGXToGallery? &tpl=`mh.images.gallery.tpl` &input=`[[*mh.images]]` &set=`[[+set]]`]]
   </ul>
</div>

With this chunk in place, I can simply reference the following in the content which is much easier to remember or to put on a cheat sheet.

[[$mh.slider?set=`name of the set`]]

We could also use a TV to have it type in the name of the set (gallery or collection) in a TV which, when filled in, puts that chunk in a predefined place in the template.

Now we've got our raw markup as FlexSlider wants.. but it's not doing much yet. Off to hook up FlexSlider!

Step 3: Hooking up FlexSlider

FlexSlider is relatively easy to use (download its files here), and there's a number of examples on its site to look at if you need help. You will need to include jQuery on the page, as well as its CSS file (found in the zip) and the FlexSlider plugin (also found in the zip). After having done that you will need to initiate it on the right element, using a code like the following:

<script type="text/javascript" charset="utf-8">
  $(window).load(function() {
    $('.flexslider').flexslider();
  });
</script>

The website advises to put all of it in the head of the file, though I personally put it in the bottom. If your slider will appear near the top of the page (above the fold), it can be better to put it in the <head> so it loads before the page shows, though that's just a preference.

And that sums it up for this very long article! I hope this how-to can help people getting started with MIGX and galleries like FlexSlider!

DESIGNfromWITHIN

Great tut Mark,

I was planning to make similar tutorial, but you didam it first! Great job.

Hansek

Hi, thanks for tutorial!

Btw. what about parsing media sources in Snippet. I have set media source to folder /media but output snippet don't respect this path and everything try to load from root. Have you any idea?

Thx for reply ;-)

Mark Hamstra

You'll most likely want to set it to media/ instead of /media.

Did you also set the base url for the media source to media/?

Hansek

In other uses (etc. in snippet getResources with parameter "processTVs") it's going well.

In selected Media source are both base_url and base_path set to media/.

Maybe it's a question more on Bruno.

Hansek

I just look into the MIGX native snippet getImageList it works well. Now there is a new problem with open_basedir restriction.

So, it's look your snippet doesn't threat Media source path, in native snippet is this done by method $migx->getFieldSource();

Mark Hamstra

AFAIK there shouldn't be any special processing needed in order to make media sources work with image TVs, though I have to admit I haven't done much testing with them & MIGX yet in 2.2. While I love MIGX, some things it does seem a bit shady and it's been on my list for a while to dive in and see if I can improve it.

Thanks for your report and looking into it, when I have some free time I'll see if I can port this over to 2.2 and see what's up. While I still prefer an 8 line snippet over one with over 250 lines and a 1000 line class using it, you may wish to change the custom snippet call in the tutorial to getImageList if you're having issues like that.

[[getImageList?
&tvname=`mh.image`
&tpl=`mh.images.gallery.tpl`
&where=`{"set":"[[+set]]"}`]]

alyainec

Hi Hansek,
I got the same issue while using on mediasources. Thumbnail won't appear. =(
Hope mark hamstra will fix this soon..

Bruno

@alyainec:

As Mark suggested. Try to use the getImageList-snippet.
This should respect the mediasource of your image-TV.

There are also two other ways to add mediasources to MIGX.
You can use a global mediasource for MIGX.
Or you can use the MIGX-config-property inputTVtype:image and use the MIGX-field-configuration mediasources where you can set up the mediasource for that field.

alyainec

Hi bruno,

Please take a look on my settings on my template & chunks


TV: mh.image
***********************************************************************************************
Form tabs:
[{"caption":"ImageGallery", "fields": [
{"field":"set","caption":"Set"},
{"field":"description","caption":"Description"},
{"field":"image","caption":"Image","inputTV":"imageTV"}
]
}]

Grid Columns:
[{
"header": "Set", "sortable": "true", "dataIndex": "set"
},{
"header": "Description", "sortable": "true", "dataIndex": "description"
},{
"header": "Image", "sortable": "false", "dataIndex": "image","renderer": "this.renderImage"
}]

(I point the mh.images TV to mediasources path: assets/gallery/)

***********************************************************************************************
My Chunk:



***********************************************************************************************
On my template



[[++site_name]] - [[*pagetitle]]










[[*content]]

[[mh.parseMIGXToGallery? &tpl=`mh.images.gallery.tpl` &input=`[[*mh.images]]` &set=`[[+set]]`]]

[^q^] queries, querytime [^qt^], phptime [^p^], totaltime [^t^], source [^s^]





$(document).ready(function() {
/*
* Simple image gallery. Uses default settings
*/

$('.fancybox').fancybox();

/*
* Different effects
*/

// Change title type, overlay opening speed and opacity
$(".fancybox-buttons").fancybox({
helpers: {
title : {
type : 'outside'
},
overlay : {
speedIn : 500,
opacity : 0.95
}
}
});


});




Even i use this:
[[getImageList?
&tvname=`mh.image`
&tpl=`mh.images.gallery.tpl`
&where=`{"set":"[[+set]]"}`]]

No thumbnail or any images display on my template.

I got this error on my modx:
[2012-07-06 03:05:45] (ERROR @ /connectors/source/index.php) Could not load class: GalleryAlbumsMediaSource from mysql.galleryalbumsmediasource.

Any idea how to fix this problem?

Mark Hamstra

Really appreciate that you leave a comment here, but if you need support it is probably better to start a new topic on the forums.. comments don't really work too well for that ;)

ae

The scaling has the page jump.. bad for the reading experience.

Mark Hamstra

Whoops! I had only added that second gallery right before publishing, and never noticed the jump. Those images were hosted of a different domain which phpthumb refused to scale down, causing the images to be different heights, causing the slider to jump.

All fixed now, thanks for letting me know!

Dimmy

Mark, Nice tutturial!!


Mediasource used in a image tv needs processing when using it in a snippet, the reason is because the path that is saved in the database is only that what you see, e.g. if you have an image folder in assets/images/product1/ but your mediasource is set to start in images/ then all the is in the database is /product1/yourimage.jpg. MODX add the path to the medisource when used as a placeholder [[*... but when you use it in a chunk for example in getresources then modx does not add the complete path, unless you say &processTVs = `1`, but that made getresources realy slow. So I made a small modfier that ads the path (assets/images/) to the output, in that way I could use phpthumbof like this: [[+tv.scrollerimage:addPath:phpthumbof=`&w=740&h=270&zc=1&q=100`]]

Mark Hamstra

Thanks Dimmy! I know it's stored without the base url/path, but I still would've figured it'd be bright enough to prepend that automatically and not needing processing or special treatment.. anyway I'll add it to the list to check out. :P

As an addition to your addition, if you need processing in getResources you could use &processTVList=`name, of, tvs` as well to have it only process the TVs you specify, but yeah processing TVs in a loop still stays expensive.

Serghei

Hi Dimmy,
could you please share the code of your "addPath" modifier?

I have problem, when I try to to list the images with getResources. The paths to images are right only the first time, I think when its not taked from cache, and the secont time I refresh the page the "assets/images/" part of url is disapeared.

Dimmy

Here it is (no idea if it this script strips tags...)

Bruno

Mark,

great Tutorial! Thanks for working this out and posting it!
With the latest MIGX, there comes a MIGX-Configurator, where you can put together Columns, Tabs, Fields and all that for MIGX and also for MIGXdb (TV and CMP) and its no longer required to create a TV. Its enough to fill the field 'inputTVtype'.

Regarding the mediasources.
Its possible to get the mediasource either from TV or from configuration. Thats the reason for $migx->getFieldSource();

Have to test this, if its still working after the latest additions and will add a configuration-option for mediasources before 2.0 pl comes out.

Mark Hamstra

Hey Bruno!

Sorry for taking so long to approve your comment - missed the notification and spam is kicking in again :P

The configurator sounds interesting. I think the biggest hurdle for designers to really get into using MIGX more is the JSON config (which isn't really hard, but definitely is a scary acronym!).

Thanks for the note on the MIGX method, that will help.

Marc

Mark, you did a great job, and I love your snippet!

I was looking at different ways to combine a "photo manager" in the MODX backend and a JS slider. I have implemented your MIGX-Flexslider for testing needs and it works like a charm (with media source as well).

Unfortunately, managing photos within a resource and through the JSON grid created via the MIGX-TV-input proves very hard and limited (how do you delete a picture? and so on).

The best solution I found for myself is to combine Modx Gallery plugin and Flexslider. The Gallery plugin offers a good picture manager that is not bound to a concrete resource. And in my experience, Flexslider works better than Galleriffic, the plugin usually associated with Gallery. Result here: http://cercec.in2p3.fr/index.php?id=42

Mark Hamstra

> how do you delete a picture? and so on
Right click, and click Remove? :P http://markh.nl/s/06/5ni.png

I do agree Gallery makes managing pictures a tad more straightforward, though there are scenarios (like in my case, the images in an actual blog post) where a resource-specific gallery is preferred.

Thanks for sharing and leaving a comment!

Marc

Ooops! You're right, Mark, thanks for the tip, erasing pictures is fairly easy. Thank you for taking the time to answer comments, and thank you for your modx blog. For me a non-developer, your input is very helpful.

alyainec

Hi Mark,
MIGX with Fancybox was i used before and it's working great.
And wow i tried to use and follow your instructions here, but i got a little problem in input tv (image).
When i was trying to upload a sample image on the MIGXTV the set, description input type (text) was ok, but the image has a input text also it supposed to be a image browse.
Im not use if i have a mistake for following the instructions or did i miss something.
Please advise me.

Thanks & best regards.

alyainec

Hi Mark,
Sorry for my wrong grammar on my first comment because i was on a hurry.

Here is what i added on Form tabs :

[{"caption":"ImageGallery", "fields": [
{"field":"set","caption":"Set"},
{"field":"description","caption":"Description"},
{"field":"image","caption":"Image","inputTV":"imageTv"}
]
}]

i created a imageTV input as image and put on form tabs.
and now it's working.
Actually i've use your snippets and combining with MIGX plus the fancybox for my gallery and it was so very easy for the end user's for showing a gallery on their content.
unlike on the documentation on MIGX.Fancybox
In here... you have to manually add placeholders like [[+img.0]] , [[+img.1]], [[+img.2]] on the resource content.

Compare to your snippets.. it was automatically added and there's no need to manually.

A big help!
Thank you again.

alyainec

Hi Mark,
Another problem i was encountered while trying to use the media sources for an image TV.
It does not display anything. Do you have any idea or suggestions?
Thanks!

Mark Goessens

Hi Mark,

I'm developing a mobile website for our company. http://m.ideebv.com/
I made this with jQuery Mobile with Modx as CMS.

The problem is, I use a imageslider as header; flexslider.
I made this work on the homepage. But on the next page, after you click on the first button 'wie zijn wij?'
the slider doesn't show up automatically. It only shows up when I refresh the page. When I go back to the homepage, the problem appears again. Then I have to refresh the home-page again to show the slider.

Does you know how I can solve this problem?
My jquery knowledge isn't the good to solve the problem by myself.

Greets Mark

Mark Hamstra

No experience with that myself, but you probably need to re-initiate the slider on one of the events jQM allows you to use...

Mark Goessens

Okay. That's a pity.
I did everything you wrote. Except I was wondering where I have to put the following code:

[[mh.parseMIGXToGallery?
&tpl=`mh.images.gallery.tpl`
&input=`[[*mh.images]]`
&set=`name of the set`
]]

The rest was completely clear for me. So you'd think that the setting is completely the same as everyone else. Because I didn't install anything else yet on modx.

Greets Mark

Mark Hamstra

The point is that jQuery Mobile takes over control of the page - it loads in a new page via AJAX and injects that into the page. If you've got your flexslider init in a standard jQuery document ready bit, that wont work on second pages cause that event has already been triggered. jQuery Mobile insteads triggers its own event when it is done building a page, I think pageready but look in the docs, which you need to use instead, to make sure it gets initiated on every page.

Or you might be able of using it in the global header thingy.

Been a while since I worked with jQM, so you'll definitely need to check their documentation.

Mark Goessens

Hi Mark,

My problem which I had before is solved.
I had to use pageinit, not document ready.

But I have a new problem. Maybe you know what went wrong:

Since I prefetched the page, the flexslider script has a problem.
When you go to: m.ideebv.com you see the flexslider with one image.
When you go to the first next page, 'Wie zijn wij', the flexslider shows a slider with 5 pictures.
But he doesn't show up how it has to be. The size is smaller. (when you look at your desktop)(see the attachment) When you look at your telephone, the slider has an error too.

It's strange that when I refresh the page, the problem disappears. Also when I minimalise the browser, and then maximalise again, the problem disappears to.
So it has something to do with a refresh. Maybe the script doesn't fires automatically on the next pages.

Do you know what the problem is?
When I disable the data-prefetch the problem disappears to. But I want to prefetch the page for sure.

Greets, Mark

smiley

Hi. Great tutorial!

Does anyone have any clue how to use the imageUpload?

Carolin Collins

Smiley - don't forget to create the "image" tv in Step 1 point 4. I overlooked that myself at first and could figure out how to upload an image into the table. Now all is ok.

smiley

Carolin,
thanks for the tip. But I ment the multi upload. (sorry for not being clear). There is a snippet "migxImageUpload" which should start "components/migx/model/imageupload/imageupload.php" and IMO be able to upload multiple images at once.

Csaba

Hi,

Could you please tell me how can recreate this with migx?
This is my portfolio item section I would like to recreate via modx:
Currently I have this on my old website : http://dobodesign.com/index.php?h=1


some text

www.something.com
some text that talks about the site dsdkfj ksdlj fsdfj ksdjsdfsdfjsdfsdsdsd.




1 of 2


something web site

Design
CMS
CSS/(X)HTML
Programming






Csaba

Sorry just realized the allowed html after posting. Anyway if you please look at my code here: http://dobodesign.com/index.php?h=1 I have a page with portfolio items. That is what I am about to create with modx and migx.

Comments are closed :(

While I would prefer to keep comments open indefinitely, the amount of spam that old articles attract is becoming a strain to keep up with and I can't always answer questions about ancient blog postings. If you have valuable feedback or important questions, please feel free to get in touch.