Jump to content
eelkenet

Into Nature (Ember.js + Processwire)

Recommended Posts

I have just delivered this website: Into Nature (Dutch only), about an 'Art expedition' through the province of Drenthe, in The Netherlands. I built the site for Vandejong.

The site is made using two distinct parts/techniques: Processwire for the back-end (through a RESTful json api) and the front-end is built on Ember.js. This is my third large site built this way, and the first I am completely happy about. 

A page called 'API' is the main interface between the two: it uses urlSegments and parses the content from the PW pages into Ember-friendly JSON data. As Ember is very strict (heavily based on the Convention over Configuration concept), and Processwire is extremely versatile, the way Ember requires its data dictates the way I shaped the API. Both @clsource's REST-helper and ProCache are used to format and cache the API responses, making the API very responsive. 

Something that was initially hard to wrap my head around was how to deal with the site's routing/pagetree. While Google now indexes modern 'single-page' web applications, for instance Facebook still scrapes their opengraph from the raw HTML pages. I dealt with this by giving the Ember app and the PW page-tree use the exact same routes / pages. Every Processwire page is a valid starting point for the Ember app, while also including the scrapeable meta tags belonging to that exact URL. As a result, the whole thing is nicely CURL-able and bot-friendly.

  • Like 15

Share this post


Link to post
Share on other sites

It's a very nice website indeed.

I just had an issue while viewing the site from my mobile phone (Chrome on Android): there appears to be no background on the navigation, so it is very hard to read depending on the scroll position. I would suggest to add some kind of background color to make the navigation links more visible.

  • Like 1

Share this post


Link to post
Share on other sites

I just had an issue while viewing the site from my mobile phone (Chrome on Android): there appears to be no background on the navigation, so it is very hard to read depending on the scroll position. I would suggest to add some kind of background color to make the navigation links more visible.

Thanks for the notice, not sure how that one sneaked in there, fixed that.

Share this post


Link to post
Share on other sites

Very nice work!

Could you tell us something about the map? Are you using geojson for the routes/polylines?

Share this post


Link to post
Share on other sites

Thanks @Mats! The map tiles are designed in Mapbox, the map itself is built on Leaflet.js, using the great Ember Leaflet addon. 

Once you get the hang of Ember.js (quite a steep learning curve) and it all starts making sense, it becomes really logical and fast to build upon.

Because it is so strict, you can predict how components and external addons will work, and how to separate your functions. All using the same philosophy. As a result, it's peanuts to toggle markers and other layers on the map that relate to other items in the application.

Let me illustrate how easy it is.

The polylines are added by adding a Polyline-layer, which is part of the Ember Leaflet addon. It requires 'locations': an array of lat/lng points.

The stripped down version of it works like this:

1. From the API we get, for instance, this JSON for the Hoofdroute (main route) -> http://www.intonature.net/api/routes/hoofdroute

2. Through the default Ember Data Rest adapter this JSON gets loaded into the route*  model:

// models/route.js
import DS from 'ember-data';
export default DS.Model.extend({
  title: DS.attr("string"),
  points: DS.attr("array"), 
  color: DS.attr("string"),
});

3. After the model promise is fulfilled, it renders the Template belonging to the Route*, often with help from an Controller to 'decorate' the data.

If that made no sense, you can also read that previous sentence like this: Through the magic of Ember this ends up at the Map Component which is inserted into the Explore template. The stripped down version of the Explore template:

{{!-- templates/explore.hbs --}}
 
   {{map-component
      locations=locations
      currentItem=currentItem
      currentPreviewItem=currentPreviewItem
      routes=routes
    }}

4.And then inside the map component we get the following: 

{{!-- components/map-component.hbs --}}

{{#leaflet-map id='explore-map'}}

  {{tile-layer url=tileLayer}}

  {{#each routes as |route|}}
    {{#if route.points}}
      {{#polyline-layer
        locations=route.points
        color=route.color
      }}
        <span class="route-label">{{route.title}}</span>
      {{/polyline-layer}}
    {{/if}}
   {{/each}}

{{/leaflet-map}}  

And that is essentially it. We load the /explore route*, which loads data from the server and puts it into a model, then renders its template. This template contains a component which draws polylines on top of a tile layer.

My previous large map-oriented / PW- & RequireJS powered project took overall about 300 hours to develop. In large because I had to invent the wheel, discover fire, and then deal with burning wheels everywhere. In short: I was missing a strict, well defined idea of which part is responsible for which function, something that happens to a lot of developers I have noticed. That, combined with the incredible shitty documentation of the non-standard Google Maps stuff and some other setbacks made it quite a tedious project. (The resulting site is still quite nice though: http://www.vangoghroute.nl )

Developing Into Nature took me around 90 hours, of which maybe half map-related. I still had to figure out a lot, yet I don't think I'll quickly return to the old world of "Php-generated html with bits of Javascript here and there and oh let's throw some more Ajax at it shall we?"

Let me clarify: We are dealing with 2 kinds of routes here. First of all the "Into Nature art routes", and then there are the 'Ember Routes'. 

Ember Routes are the urls of the app, and responsible for getting data and injecting that data into models. 

Edited by eelkenet
  • Like 5

Share this post


Link to post
Share on other sites

Thanks for your detailed answer! Very impressive stuff. 

The vangoghroute.nl site is very nice too.

  • Like 2

Share this post


Link to post
Share on other sites

Developing Into Nature took me around 90 hours, of which maybe half map-related. I still had to figure out a lot, yet I don't think I'll quickly return to the old world of "Php-generated html with bits of Javascript here and there and oh let's throw some more Ajax at it shall we?"

Would you go with this approach for a standard content type site?  I really like the idea of decoupling the content store and the rendering - it opens up lots of opportunities.  I can see that you can get the meta tags available to search engines, but what about the content?  Do you have a noscript option on the processwire routes that dumps out an HTML version of the page, or do you rely on Google's best effort indexing of javascript pages?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By quickjeff
      Hi Guys, 
      I have been debugging a site for the last 2 hours and cannot solve the issue. 
      I have a site running on 3.0.148. 
      I installed the Kongondo Blog module and was updating the templates to include the website style. 
      Once everything was set and done, I checked the page tree to see an error appear. 
      Template must be assigned a name before 'filename' can be accessed
      The same error appears in templates. 
      Debugging Steps
      I checked the templates in the server to ensure I didnt accidentally delete the namespace.  Deleted cache in browser and server under assets Still no go. 
      Any help is appreciated. 
      Thanks! 
    • By Spyros
      Hello
      I'm having a strange issue with the $page->find(), for some reason I'm missing some of the pages from the results. I found then that I was missing all the pages with the same "PAGE NAME". Is it a bug or am I missing something?
      PS 
      If I change the "PAGE NAME" of one of the missing ones then I'm retrieving the page without any problem.
      Thank you
    • By Guy Incognito
      This short script loops through some images from an XML feed and pushes new ones to an image field. It all works perfectly, except for some reason the last image (only) in the loop each time doesn't receive the image description... can everyone spot why? TIA! 🙂 
      foreach ($propertyImages as $img) { $fileName = trim($img[0]); if ( !empty($fileName) ) { $imgPath = '../property_data/'.$fileName; if(file_exists($imgPath) && !in_array(strtolower($fileName),$currentImages)) { $p->property_images->add($imgPath); $p->save(); $newImg = $p->property_images->last(); $newImg->description = $img[1]; $p->save(); } } }  
    • By MateThemes
      Hello everyone!
      I am working with Processwire since some time. But some topics are quite hard for me.
      I have a Portfolio (Gallery) Page.
      I am build a template with Portfolio Index and pages with portfolio entries.
      Structure:
      Portfolio Index
      -- Portfolio Entry
      -- Portfolio Entry
      and so on.
      Portfolio Entry has an Image field with max 12 images and are accessible Templates. 
      Now I want to display the single Portfolio Entry on the Portfolio Index and Paginate them. In the index page all images of a single Entry page should be displayed (I should not be organized as albums, where a random image of the portfolio entry should be displayed). 
      I have no clue to achieve this. May someone could give me an advice.
      Thank you in advance!
    • By ngrmm
      I have a page with a table. Each table row has a page-reference field and a checkbox.
      The Page sends emails to all users (page-refrence->email-field) and change the value of the checkbox in a row to 1.
      It works with this:
      <?php // event ID fron url query $eventID = $input->get('eventID','int'); // get event-page $event = $pages->get($eventID); // config $fromEmail = $event->event_mail_from; $fromName = $event->event_mail_from_name; $emailSubject = $event->event_subject; // email html body ob_start(); include('./_inc/emailbody.inc'); $emailBody = ob_get_clean(); // make event-page editable $event->of(false); // loop through table and send out emails foreach($event->event_clients_list as $event_table_row) { // get client page $clientPage = $event_table_row->client_name; // get client email $clientEmail = $clientPage->email; // if client isn't invited yet (checkbox not checked) if($event_table_row->client_invited == '') { // send email $m = new WireMail(); $m->to($clientEmail); $m->from($fromEmail, $fromName); $m->subject($emailSubject); $m->bodyHTML($emailBody); $m->send(); // mark client as invited $event_table_row->client_invited = 1; $event->save('event_clients_list'); } } ?> But i have to use a variable in my emailbody.inc which i'm able to get in the table-loop.
      So i do the including of the body inside my loop. But this doesn't work anymore. Page sends out the emails but is unable to change the value of the checkbox.
      I get no errors!
      I'm using ProTable
      <?php // event ID fron url query $eventID = $input->get('eventID','int'); // get event-page $event = $pages->get($eventID); // config $fromEmail = $event->event_mail_from; $fromName = $event->event_mail_from_name; $emailSubject = $event->event_subject; // loop through table and send out emails foreach($event->event_clients_list as $event_table_row) { // get client page $clientPage = $event_table_row->client_name; // get client email $clientEmail = $clientPage->email; // email html body ob_start(); include('./_inc/emailbody.inc'); $emailBody = ob_get_clean(); // make event-page editable $event->of(false); // if client isn't invited yet (checkbox not checked) if($event_table_row->client_invited == '') { // send email $m = new WireMail(); $m->to($clientEmail); $m->from($fromEmail, $fromName); $m->subject($emailSubject); $m->bodyHTML($emailBody); $m->send(); // mark client as invited $event_table_row->client_invited = 1; $event->save('event_clients_list'); } } ?>  
×
×
  • Create New...