back Using custom assets in packages

In this tutorial you will master custom package development by learning how to use custom assets like CSS and JavaScript and how to build upon the defaults provided by the core application.

Please note that BIIGLE switched the frontend JavaScript framework from AngularJS to Vue.js! While the principles of including custom assets in packages are still correct, the implementation of an AngularJS module is no longer possible (unless you include AngularJS as a custom asset in your package as well). AngularJS, ngResource and Angular UI Bootstrap are no longer available throughout the application.

In previous tutorials on package development you've always used assets provided by the core application, like Bootstrap for styling. In this tutorial we'll talk about what assets are provided by default and how you can add to them with your own. If you haven't done the previous tutorialy yet, start there and come back later.

What's already there

The BIIGLE frontend is built upon two frameworks, Bootstrap for CSS and AngularJS (exdended with the ngResource module) for JavaScript. Using Angular UI Bootstrap you can use the interactive components of Bootstrap, too.

In addition to the basic frameworks, the BIIGLE core application also provides AngularJS modules e.g. for easy interaction with the RESTful API.

Each view extending the base app template automatically has all these assets available. While you are able to ignore them and use your own frameworks for package development, you are highly encouraged to stick to the default frameworks, keeping the application lean and consistent.

Using the defaults

Using Bootstrap for styling is really simple. Just use the documentation as reference for what classes and components are available and you are done. You'll recall having used it already, implementing a panel in the dashboard view mixin or using the grid system in the new view of your quotes module.

For using AngularJS you can stick to their documentation as well. If you are not familiar with it, you should learn about it first since we can't give you a crash course in the scope of this tutorial. If you already have some experience with AngularJS you should be able to follow along fine. And maybe reading this tutorial will help you understanding the basics of AngularJS, too.

Building upon the API

While showing you how to use the provided client side API and how to extend it with custom assets, we will extend the previously developed quotes module yet again. First we will implement a button that should interactively refresh the displayed quote in the quotes view and then we will add some custom styling.

To give an example on how to use the provided client side API we would like our refresh button to simply display a user feedback message through the integrated messaging system first, without interacting with the backend. This will show you how to add core BIIGLE modules as a dependency to your custom AngularJS modules and how to use the provided services.

To add custom JavaScript to a view, we need to add to the scripts section of the base app template. The scripts are usually located at the bottom of a page body so if we wanted to use the default assets already in the content section of the template it wouldn't work. To append our JavaScript to the scripts section, add the following to the index.blade.php template of our quotes package:

<script type="text/javascript">
   // your script goes here

Looking at the HTML of the rendered page, you'll notice that the new script tag is already appended at the right position following all default scripts. So let's populate the tag with the following script:

angular.module('biigle.quotes', ['biigle.ui.messages']);
angular.module('biigle.quotes').controller('QuotesController', function($scope, msg) {
   $scope.refreshQuote = function () {
      msg.info("I don't do anything yet!");

We create a new Angular module called biigle.quotes and add the biigle.ui.messages module as a dependency. This enables us to inject the msg service into the controller function of the QuotesController we subsequently define. We then add the refreshQuote function to the scope of the controller that will display an info message when called.

Let's edit the content section of our quotes view to see if everything works:

<div class="container" data-ng-app="biigle.quotes" data-ng-controller="QuotesController">
   <div class="col-sm-8 col-sm-offset-2 col-lg-6 col-lg-offset-3">
         {{ Inspiring::quote() }}
      <button class="btn btn-default" data-ng-click="refreshQuote()">refresh</button>

Here we tell AngularJS to load the biigle.quotes module and to use the QuotesController as the controller for the entire container. We also add a button and define the refreshQuote function to be called whenever the button is clicked. Try it out; click the new button and see how the msg service works.

Now you know how to use the provided AngularJS modules in your own modules and how to access the services and factories. Let's go on to extend the new biigle.quotes module and include it as custom asset.

Adding your own assets

In the little JavaScript example above we implemented a new AngularJS module using the script tag, putting the code directly into the HTML. Working with real JavaScript and CSS you usually load these assets as external files. Now, all public files - including CSS and JavaScript assets - reside in the public directory of a Laravel application. When custom packages like to use their own assets there needs to be a mechanism to publish the package assets to the public directory.

Let's see how this works by extending our AngularJS module to asynchronously refresh the quotes.

Publishing JavaScript

First, we want to outsource the code written above to its own JavaScript file. Create a new file src/public/assets/scripts/main.js and populate it with the previously written code. Then remove the script tag from the view (not the section, though).

Now we have to tell Laravel that our quotes package has some assets to publish. This is done by adding the following to the boot function of the QuotesServiceProvider:

   __DIR__.'/public/assets' => public_path('vendor/quotes'),
], 'public');

Now Laravel can copy anything located in src/public/assets to public/vendor/quotes, making the assets of the package available for use in the frontend. If you take a look at the public/vendor directory, you'll see assets of other packages there, too. Let's add our quotes assets by running the artisan utility from the root of the BIIGLE installation:

 php artisan vendor:publish --provider="Biigle\Modules\Quotes\QuotesServiceProvider" --force

We tell artisan to publish only the assets of our package so it doesn't overwrite the assets (e.g. configuration files) of other packages. It would do so because we used the force flag, since we want the files to be replaced during developing the JavaScript application. From now on you always have to run this command again after any changes to the JavaScript application, otherwise the public files wouldn't be refreshed.

Our JavaScript is now publicly available so let's re-populate the scripts stack of the view template and everything should be back working again:

<script src="{{ asset('vendor/quotes/scripts/main.js') }}"></script>

The asset helper function is a convenient way to generate URLs to files located in the public directory of the application.

To asynchronously load new qutes from the server, we need a new route and controller method. Since you already know about routes and controllers, let's make it quick:

The test in QuotesControllerTest.php:

public function testQuoteProvider()
   $user = UserTest::create();

   $this->call('GET', 'quotes/new');
   // redirect to login page

   $this->call('GET', 'quotes/new');

The route in routes.php:

Route::get('quotes/new', array(
   'middleware' => 'auth',
   'uses' => '\Biigle\Modules\Quotes\Http\Controllers\QuotesController@quote'

The controller function in QuotesController.php:

 * Returns a new inspiring quote.
 * @return  \Illuminate\Http\Response
public function quote()
   return \Illuminate\Foundation\Inspiring::quote();

When all tests pass, you have done everything right! Now let's rewrite our little AngularJS module in main.js of the package:

angular.module('biigle.quotes', ['biigle.api']);
angular.module('biigle.quotes').controller('QuotesController', function($scope, URL, $http) {
   $scope.refreshQuote = function () {
      $http.get(URL + '/quotes/new').success(function (quote) {
         $scope.quote = quote;

We now require the biigle.api module so we can use the URL constant, containing the base URL of the application. We then use the $http service of the AngularJS core to call the new quotes/new route whenever the button is clicked. The response is written into the quote property of the controller scope. This is done once when the controller is initialized, too, to get an initial quote without having to click the button.

Finally, we have to rewire the view a little bit to display the dynamicly loaded quote. To do so, replace the old blockquote element by this one:

<blockquote data-ng-bind="quote"></blockquote>

This dynamically sets the content of the element to whatever the content of the quote scope property is. Now publish the new JavaScript file, refresh the view and enjoy your asyncronously reloaded quote!

Publishing CSS

Publishing custom CSS files is very similar to publishing JavaScript. Having the boot method of the service provider already prepared, we now only have to create a new CSS file and include it in the view template. So let's see if we can style the displayed quote a little.

Create a new public/assets/styles/main.css file for your package and add the style:

blockquote {
   border-color: #398439;

Then add the new asset to the styles section of the view template:

<link href="{{ asset('vendor/quotes/styles/main.css') }}" rel="stylesheet">

Publish the assets (the command stays the same) and reload the page. Stylish, isn't it?


Congratulations! Now you know (almost) anything there is to know about developing custom packages for BIIGLE. What's still left is how you can implement your views so they can be extended yet by others. You have done it yourself, implementing the dashboard view mixin. There are a few things to keep in mind when registering your own areas for view mixins but we'll cover that in another tutorial.