Materilize CSS: Change Tab text and indicator colour using only HTML and colour classes

When using the excellent Materialize CSS framework, it isn’t immediately obvious how to to change both text and indicator/underline colour of Tabs using only HTML and the Materialize colour classes (i.e. no changes to CSS).

To change the text color, just add the text-colour classes to the <a> tags, e.g.:

<a class="indigo-text text-darken-4" href="#test1">Test 1</a>

To change the indicator/underline colour, you can insert this before the </ul>:

<div class="indicator indigo darken-4" style="z-index: 1;"></div>

To wrap this up as a complete, 2-tab, example:

<div class="row">
  <div class="col s12">
    <ul class="tabs">
      <li class="tab col s6">
        <a class="active indigo-text text-darken-4" href="#test1">Test 1</a>
      </li>
      <li class="tab col s6">
        <a class="indigo-text text-darken-4" href="#test2">Test 2</a>   
      </li>
      <div class="indicator indigo darken-4" style="z-index: 1;">
      </div>
    </ul>
  </div>
  <div id="test1" class="col s12">Test 1</div>
  <div id="test2" class="col s12">Test 2</div>
</div>

CakePHP 3: Access a method from one Behavior (or the Table class) in another Behavior

Please note that this post refers to CakePHP 3.

Short answer is, as long as you have added both Behaviors in the Table class, you can call a method from one Behavior in another as follows:

class SecondBehavior extends Behavior {
    public function secondBehaviorMethod() {
        return $this->_table->firstBehaviorMethod();
    }
}

I couldn’t find anything about this in the docs, so initially I hoped/presumed that, as long as I had added both Behaviors to my Table class, it was simply a case of calling the method as I would in a Table, so:

In MyTable.php:

public function initialize(array $config) {
    ...
    $this->addBehavior('FirstBehavior');
    $this->addBehavior('SecondBehavior');
}

In FirstBehavior.php:

class FirstBehavior extends Behavior {
    public function firstBehaviorMethod() {
        return "done";
    }
}

In SecondBehavior.php:

class SecondBehavior extends Behavior {
    public function secondBehaviourMethod() {
        return $this->firstBehaviorMethod();  //Gives "Call to undefined method" error
    }
}

However, this just results in a “Call to undefined method” error. However, Behaviors have the $_table property (see the Behavior API) that allows you to access other methods of the current Table. Therefore, just adding _table to the call to firstBehaviorMethod ($this->_table->firstBehaviorMethod()) fixes the issue. As well as accessing methods from other added Behaviors, you can also use find ($this->_table->find(…)), etc.

CakePHP 3: Date/Time Disappearing during Hydration

When generating a date/time for saving to a MySQL database, I’ve always tended to generate it with the PHP date method (date('Y-m-d H:i:s')). However, using CakePHP 3, with dateTime validation set on the field, the date/time would disappear during hydration of the entity, but without a validation error.

I still suspected that validation was the issue here, so started having a look at the validation method to try to work out what was going wrong. Before I got very far, I noticed that the datetime validation method validates any value that is a Cake DateTime object. Therefore, the simple solution is to create the date/time using Cake’s built-in Time class:

use Cake\I18n\Time;
$datetime = Time::now();

Cake then happily saves the DateTime in the standard MySQL format.

ReactJS – Invariant Violation due to refs or multiple copies of React

I am using ReactJS, with Gulp, Browserify, etc, all managed by NPM. I just updated my packages (npm update), but got the following error:

“Uncaught Invariant Violation: addComponentAsRefTo(…): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component’s `render` method, or you have multiple copies of React loaded (details: https://fb.me/react-refs-must-have-owner).”

I was quite confused by this, as I am not using refs anywhere, and I thought NPM was meant to stop me from having multiple copies of things loaded. It turns out that isn’t the case – https://gist.github.com/jimfb/4faa6cbfb1ef476bd105 – and NPM is not great at deduplicating dependencies. Therefore, the update had introduced a second version of react (under material-ui), which was causing this error.

Thankfully the fix is pretty easy, and is now my standard NPM equivalent of switch it off and on again:

  • Delete the node_modules directory
  • Run npm install

This should give you a clean set of dependencies and no duplication of react. Of course, if you are using gulp or similar, remember to re-build your bundles before reloading your app to check that the error has gone away. This caused me a moment of frustration before I realised my schoolboy error!

AngularJS: labelled empty option in select box when using ng-options

I’m using ng-options to generate the options for select boxes in AngularJS. By default, when a selected value is not defined, Angular adds a blank option, without a label. I wanted to give this blank option a label, e.g. “Select something”, but couldn’t see how to do it.

Not surprisingly it’s very easy, and is actually mentioned in the docs (https://docs.angularjs.org/api/ng/directive/select – 4th paragraph, beginning “Optionally” at time of writing), but like, I guess, most people, I never read the docs properly and just scanned the examples, which don’t show this.

All you need to do is add the labelled blank option in the select box, as you normally would if you were just writing the HTML, e.g.:

<select name="myselect" ng-model="MyCtrl.selected" 
   ng-options="option.value as option.label for option in MyCtrl.options">
   <option value="">Select something</option>
</select>

Angular will then generate the remaining options from the options array that you pass to it, and leave the blank option at the top.

In the above example, to not show a blank option at all, remove the option tag and set Ctrl.selected to be equal to the value that you want to be selected by default in the controller, e.g.

In controller.js

this.options = [ { value: 1, label: 'Yes' }, { value: 0, label: 'No' } ];
this.selected = 1;

In view.html

<select name="myselect" ng-model="MyCtrl.selected" 
   ng-options="option.value as option.label for option in MyCtrl.options">
</select>

CakePHP 3 JSON Views: Setting _jsonOptions special parameter

CakpePHP 3 makes creating JSON and XML views of your data very easy, certainly easier than I remember finding it in Cake 2. However, I found the documentation didn’t make it particularly clear how/where to set the _jsonOptions parameter. All the docs (http://book.cakephp.org/3.0/en/views/json-and-xml-views.html) say is:

“The JsonView class supports the _jsonOptions variable that allows you to customize the bit-mask used to generate JSON. See the json_encode documentation for the valid values of this option.”

It’s actually pretty simple, just put this in your Controller action:

$this->set('_jsonOptions', JSON_HEX_TAG );

I was actually doing this to try to get rid of the JSON_HEX_TAG option, which seems to be set by default. So I actually did this, to unset all the JSON options:

$this->set('_jsonOptions', '');

AngularJS: Images failing to load (Not Found) with curly brackets in URL

This is one of those schoolboy errors that, in hindsight, is blindingly obvious and I should have been able to work out for myself, but required a bit of searching before I managed to ask Google (other search providers are available) the right question.

Basically, I had a load of image src paths that were defined using AngularJS expressions, e.g.

<img src="content/images/results/{{result.id}}.jpg" />

The images loaded fine, but I kept getting 404 (Not Found) errors on image URLs that contained the curly brackets of my expressions (/content/images/results/%7B%7Bresult.id%7D%7D.jpg). This seemed like it was because the page was trying to load the images before Angular has done it’s work and converted the expressions into sensible URLs. And that’s exactly the problem!

The solution is a simple one: use ng-src instead of src if the URL has an expression in it, e.g.

<img ng-src="content/images/results/{{result.id}}.jpg" />

OxTALENT Awards for Medical Sciences

The 2015 OxTALENT (Teaching and Learning Enhanced with Technology) Awards saw a triple success for Medical Sciences, recognising innovative use of technology for teaching and learning within the Division:

Dr Lizzie Burns – Winner in the Outreach and Public Engagement: TES Connect category, for ‘CSlide’: a Nobel Prize-Winner Inspires Science, Art and Creativity

Dr Richard Harrington – Runner-up in the Use of WebLearn to Support Teaching, Learning or Outreach category for Promoting Online Learning and Collaboration with Graduate-Entry Medical Students

Dr Damion Young and Jon Mason – Runner-up in the Innovative Teaching with Technology category for PULSE – Pop-Up Learning Space Experiment

Congratulations to all the winners and runners-up at the awards. If you would like to talk to us about any aspect of using technology for teaching or learning, or simply have a learning/teaching challenge for which technology could provide the answer, please contact us.

PHP: Naturally sorting multidimension arrays by string values

This is just one of those things that I have to do often enough for it to be useful to have it close at hand, but not often enough that I actually remember how to do it.

I often run into this problem when using CakePHP, as results are returned from the database in a multidimensional array. Since MySQL doesn’t (as far as I’m aware) have an easy natural sort option, even if the results are sorted by name, strings that have numbers in can end up in the wrong order, e.g. “Item 10” before “Item 2”. Using a combination of usort() and strnatcasecmp(), it’s pretty easy to naturally sort an array by a value that appears in each subarray, like this:

usort($array, function($a, $b) {
    return strnatcasecmp($a['name'], $b['name']);
});

As an example, say I got the following array back from MySQL:

$array = array(
    array(
        'id' => 265,
        'name' => 'Drawer 1'
    ),
    array(
        'id' => 971,
        'name' => 'Drawer 10'
    ),
    array(
        'id' => 806,
        'name' => 'Drawer 2'
    ),
    array(
        'id' => 429,
        'name' => 'Drawer 20'
    )
);

“Drawer 10” comes before “Drawer 2”, which is obviously not what I want. Using strcasecmp() as the sort function on the above array wouldn’t change it, but using strnatcasecmp() gives the required result:

$array = array(
    array(
        'id' => 265,
        'name' => 'Drawer 1'
    ),
    array(
        'id' => 806,
        'name' => 'Drawer 2'
    ),
    array(
        'id' => 971,
        'name' => 'Drawer 10'
    ),
    array(
        'id' => 429,
        'name' => 'Drawer 20'
    )
);

PHPFlickr and SSL/HTTPS – certificate issues on local Windows development server

We use PHPFlickr, which is a very handy PHP wrapper for the Flickr API, in CSlide. Recently, Flickr announced that they are making their API SSL only (on June 27th 2014), and PHPFlickr was updated accordingly. However, I could not get it to work on my local XAMPP development server, running on Windows 8. I initially assumed it was an issue with the changes to the PHPFlickr class (as it had previously worked fine), but, after much debugging, found that the problem was that CURL (which is used to make the requests to the API) could not verify Flickr’s SSL certificate. This is because CURLs default behaviour is to not trust any Certificate Authorities (CAs).

The solution is to give CURL a bundle of trusted CA certificates, which can be download from the CURL site. You can tell CURL about this bundle by setting the path to the CA bundle file in php.ini (and remembering to restart Apache afterwards). Adding this at the end of php.ini did the trick for me, but obviously change the path according to your own setup:

curl.cainfo=c:\xampp\php\cacert.pem

You can also point CURL at the CA bundle when setting up your curl connection, using curl_setopt to set CURLOPT_CAINFO to the full path to your CA bundle file. I didn’t want to do this with PHPFlickr, as it would involve changing the core PHPFlickr code, which would inevitably cause problems when that code gets updated:

curl_setopt($ch, CURLOPT_CAINFO, "c:\xampp\php\cacert.pem");

Note that one bad solution that was commonly suggested was to set CURLOPT_SSL_VERIFYPEER to false, which, as the name suggests, stop CURL from trying to verify the certificate. As rmckay says in the curl_setopt docs, this allows man in the middle attacks, so should be avoided!