{"id":7307,"date":"2013-03-14T15:32:05","date_gmt":"2013-03-14T14:32:05","guid":{"rendered":"https:\/\/blog.trifork.com\/?p=7307"},"modified":"2013-03-14T15:32:05","modified_gmt":"2013-03-14T14:32:05","slug":"angularjs-lessons-learned","status":"publish","type":"post","link":"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/","title":{"rendered":"AngularJS: Lessons learned"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2013\/03\/AngularJS-large1.png\" alt=\"AngularJS large\" width=\"200\" height=\"56\" border=\"0\" \/>At Devoxx 2012 I attended the AngularJS presentation by Igor Minar and Misko Hevery. I was very enthusiastic about the capabilities of this front-end framework. Therefore I started experimenting with it. I created a sample for the Axon Framework, <a href=\"\/2012\/11\/27\/basic-axon-framework-sample-using-vert-x-and-angular-js\/\">read more about it here<\/a>. After my experiments I felt confident enough to start using it in real projects. One of them was adding management reporting using the <a href=\"http:\/\/www.highcharts.com\">HighCharts<\/a> library.<\/p>\n<p>The next step was a bigger project, writing an Elasticsearch plugin to query your Elasticsearch instance. This project has to integrate with a javascript library to interact with Elasticsearch. The layout and other front-end components were implemented using <a href=\"http:\/\/twitter.github.com\/bootstrap\/#\">Twitter Bootstrap<\/a>. Therefore I also used another AngularJS plugin to integrate with Bootstrap.<\/p>\n<p>In this blog post I&#8217;ll give you some lessons learned with respect to AngularJS.<\/p>\n<p><!--more--><\/p>\n<h2>Introduction<\/h2>\n<p>I am not going to write down a complete guide into AngularJS. Check <a href=\"\/2012\/11\/27\/basic-axon-framework-sample-using-vert-x-and-angular-js\/\">my previous blog post<\/a> for more startup documentation. For more background on the Elasticsearch plugin check my other blog post <a href=\"https:\/\/blog.trifork.com\/2013\/03\/12\/introducing-a-query-tool-as-an-elasticsearch-plugin-part-1\/\" target=\"_blank\" rel=\"noopener\">Introducing Query tool for Elasticsearch<\/a>\u00a0(part 1).<\/p>\n<p>If you want to tag along using the sources, checkout my Github project.<\/p>\n<p><a href=\"https:\/\/github.com\/jettro\/angular-es-client\">https:\/\/github.com\/jettro\/angular-es-client<\/a><\/p>\n<h2>The AngularJS tricks<\/h2>\n<h3>Use the angular quick start (aka angular-seed)<\/h3>\n<p>The Angular team provides a project template that they call the angular-seed. This project gives a complete skeleton for an AnagularJS project. Including the option to run unit tests and integration tests. It has all the libraries at the right location, wires the different components like controllers, services, directives and filters. More information about the angular-seed project can be found here:<\/p>\n<p><a href=\"https:\/\/github.com\/angular\/angular-seed\">https:\/\/github.com\/angular\/angular-seed<\/a><\/p>\n<h3>Using partials<\/h3>\n<p>When you have the plugin installed in Elasticsearch and you browse to the plugin, you&#8217;ll get to see the index.html. This is where it all starts. In the html tag you can find the ng-app directive with the name of the application. You&#8217;ll also find the ng-controller directive for the navigation and my own navbar directive. Finally you&#8217;ll see the ng-view directive that is the placeholder for the actual content coming from the partials. Now how is everything initialized. That starts with the initialization of the different components in the application module. This can be found in the file app.js<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nangular.module('myApp', &#x5B;'myApp.filters', 'myApp.services', 'elasticjs.service', 'myApp.directives', 'ui.bootstrap']);\n<\/pre>\n<p>In this module we configure the different components by loading them for the myApp module. Notice that we inject more than our own components. We also inject the Elasticsearch module.<\/p>\n<p>Another important part of the application module setup is the routing configuration. Angular is created to be a one page application. Usually every application needs more than one functionality. Therefore we need at least navigation. When using multiple views, you could store everything in one big html file. This is of course hard to maintain. Therefore routing also introduces the partial concept. This way you can inject content coming from another html file into the existing location. The following code block shows the configuration<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nangular.module('myApp', &#x5B;'myApp.filters', 'myApp.services', 'elasticjs.service', 'myApp.directives', 'ui.bootstrap']).\n        config(&#x5B;'$routeProvider', function ($routeProvider) {\n            $routeProvider.when('\/dashboard', {templateUrl: 'partials\/dashboard.html', controller: DashboardCtrl});\n            $routeProvider.when('\/node\/:nodeId', {templateUrl: 'partials\/node.html', controller: NodeInfoCtrl});\n            $routeProvider.when('\/stats', {templateUrl: 'partials\/stats.html', controller: StatsCtrl});\n            $routeProvider.when('\/query', {templateUrl: 'partials\/query.html', controller: QueryCtrl});\n            $routeProvider.otherwise({redirectTo: '\/dashboard'});\n        }]);\n<\/pre>\n<p>Notice that we also provide a default, in our case the dashboard. At the moment <em>\/query<\/em> is the most interesting one. Here I have created the most functionality. Three things are important to know, in the url the current items can be found in the format #\/query. The content of a partial is added to the tag containing ng-view. The configuration also contains the Controller that picks up the request and the view or partial to load.<\/p>\n<h3>Custom directive for navigation<\/h3>\n<p>One very nice aspect of AngularJS is about adding your own html elements or attributes. I wanted to have navigation on the top of the page with the current page highlighted. The following code block shows the html for adding the navigation. As you can see this is not to much. It contains one element with one attribute.<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;div ng-controller=&quot;NavbarCtrl&quot;&gt;\n    &lt;navbar heading=&quot;Elasticsearch GUI&quot;\/&gt;\n&lt;\/div&gt;\n<\/pre>\n<p>The following code block shows the JavaScript directive for handling this new html element. This directive can be found in the <em>directives.js<\/em> file.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nangular.module('myApp.directives', &#x5B;]).\n        directive('navbar', &#x5B;'$location', function ($location) {\n            return {\n                restrict: 'E',\n                transclude: true,\n                scope: {heading: '@'},\n                controller: 'NavbarCtrl',\n                templateUrl: 'template\/navbar\/navbar.html',\n                replace: true,\n                link: function ($scope, $element, $attrs, navbarCtrl) {\n                    $scope.$location = $location;\n                    $scope.$watch('$location.path()', function (locationPath) {\n                        navbarCtrl.selectByUrl(locationPath)\n                    });\n                }\n            }\n        }]);\n<\/pre>\n<p>The name of the directive is the same as the used html element. The heading value is added to the scope, this way the template can use the parameter as taken from the heading attribute of the html element. During the linking phase we add a watch to the current location. If it changes we check all navigation items and change the active item. The code to do this is coming from the configured controller. The next code block shows this controller.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nfunction NavbarCtrl($scope) {\n    var items = $scope.items = &#x5B;\n        {title: 'Home', link: 'dashboard'},\n        {title: 'Queries', link: 'query'},\n        {title: 'Statistics', link: 'stats'}\n    ];\n\n    this.select = $scope.select = function (item) {\n        angular.forEach(items, function (item) {\n            item.selected = false;\n        });\n        item.selected = true;\n    };\n\n    this.selectByUrl = function (url) {\n        angular.forEach(items, function (item) {\n            if ('\/' + item.link === url) {\n                $scope.select(item);\n            }\n        });\n    };\n}\n<\/pre>\n<h3>Creating your own service<\/h3>\n<p>AngularJS is an mvc framework. Just like we did on the server, we want controllers to have as little amount of functionality as possible. That is why we try to extract functionality into services and inject the service into the controller. The following code block shows creating and registering the service. For now just one method is shown, obtaining all the indexes.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nvar serviceModule = angular.module('myApp.services', &#x5B;]);\nserviceModule.factory('elastic', &#x5B;'$http', function (http) {\n    function ElasticService(http) {\n        this.indexes = function (callback) {\n            http.get('\/_status').success(function (data) {\n                callback(data.indices);\n            });\n        };\n\t}\n    return new ElasticService(http);\n}]);\n<\/pre>\n<p>In the second line we register the service <em>elastic<\/em>. This service has one method called <em>indexes<\/em>. What the service does is not that important yet. Now let us have a look at how this is used in a controller.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nfunction QueryCtrl($scope, $dialog, ejsResource, elastic) {\n    $scope.loadIndices = function () {\n        elastic.indexes(function (data) {\n            $scope.indices = data;\n        });\n    };\n}\nQueryCtrl.$inject = &#x5B;'$scope', '$dialog', 'ejsResource', 'elastic']\n<\/pre>\n<p>By reusing the name <em>elastic<\/em> we can inject the service. In line 3 we demonstrate using the indexes function. The last line is used to be able to inject objects by name even if we use minify and obfuscate functionality. To make the picture complete the next block shows the html to print the buttons for the different indexes.<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;div class=&quot;row-fluid&quot;&gt;\n    &lt;div class=&quot;span2&quot;&gt;&lt;span class=&quot;text-info&quot;&gt;Available indexes:&lt;\/span&gt;\n        &lt;button popover-placement=&quot;right&quot;\n                popover=&quot;Here you can select the index that we query over. If you select nothing, we query over all data&quot;\n                class=&quot;btn btn-mini btn-link&quot;&gt;&lt;i class=&quot;icon-question-sign&quot;&gt;&lt;\/i&gt;&lt;\/button&gt;\n    &lt;\/div&gt;\n    &lt;div class=&quot;span10&quot;&gt;\n        &lt;div id=&quot;chooseIndexBtn&quot; class=&quot;btn-group&quot; data-toggle=&quot;buttons-checkbox&quot;&gt;\n            &lt;div class=&quot;btn btn-mini&quot; ng-click=&quot;chooseIndex(key)&quot; ng-repeat=&quot;(key,value) in indices&quot;&gt;{{key}}&lt;\/div&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n<\/pre>\n<h3>Creating a popover for help buttons<\/h3>\n<p>The html block in the previous section also shows the integration with the popover. There are two additional attributes for the button called <em>popover-placement<\/em> and <em>popover<\/em>. This functionality is provided by the angular\/bootstrap integration. You can find <a href=\"http:\/\/angular-ui.github.com\/bootstrap\/\">more information about this plugin here<\/a>. Using it is as easy as initializing the <em>ui.bootstrap<\/em> library as a module. Check the section describing the app.js file.<\/p>\n<h3>Creating a modal for adding facets<\/h3>\n<p>Creating the module is similar to the popover. We just make use of the bootstrap\/angularjs integration. The following code block show the function that opens a dialog and handles the response of the dialog.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n    $scope.openDialog = function () {\n        var opts = {\n            backdrop: true,\n            keyboard: true,\n            backdropClick: true,\n            templateUrl: 'template\/dialog\/facet.html',\n            controller: 'FacetDialogCtrl',\n            resolve: {fields: angular.copy($scope.fields)}};\n        var d = $dialog.dialog(opts);\n        d.open().then(function (result) {\n            if (result) {\n                $scope.facets.push(result);\n                $scope.changeQuery();\n            }\n        });\n    };\n<\/pre>\n<p>The source code shows we obtain the template from the file facet.html and the logic is in the FacetDialogCtrl controller. These files contain some logic for selecting the right facet and showing different input fields based on the selected facet. The code is straightforward and therefore I leave it up to the reader to have a look at the code.<\/p>\n<h2>Concluding<\/h2>\n<p>This blog post has given an idea about writing components with AngularJS. You have seen how to use the different Bootstrap components using AngularJS. I hope you have more understanding now about what you can do with AngularJS. Of course I still only scratched the surface here.<\/p>\n<h2>References<\/h2>\n<ul>\n<li><a href=\"http:\/\/angularjs.org\">AngularJS<\/a><\/li>\n<li><a href=\"http:\/\/twitter.github.com\/bootstrap\/#\">Twitter Bootstrap<\/a><\/li>\n<li><a href=\"http:\/\/angular-ui.github.com\/bootstrap\/\">Bootstrap AngularJS integration<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>At Devoxx 2012 I attended the AngularJS presentation by Igor Minar and Misko Hevery. I was very enthusiastic about the capabilities of this front-end framework. Therefore I started experimenting with it. I created a sample for the Axon Framework, read more about it here. After my experiments I felt confident enough to start using it [&hellip;]<\/p>\n","protected":false},"author":60,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"content-type":"","footnotes":""},"categories":[15,78,65,10],"tags":[309,109,61],"class_list":["post-7307","post","type-post","status-publish","format-standard","hentry","category-enterprise-search","category-frontend","category-big_data_search","category-development","tag-angular-js","tag-angularjs","tag-elasticsearch"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>AngularJS: Lessons learned - Trifork Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"AngularJS: Lessons learned - Trifork Blog\" \/>\n<meta property=\"og:description\" content=\"At Devoxx 2012 I attended the AngularJS presentation by Igor Minar and Misko Hevery. I was very enthusiastic about the capabilities of this front-end framework. Therefore I started experimenting with it. I created a sample for the Axon Framework, read more about it here. After my experiments I felt confident enough to start using it [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/\" \/>\n<meta property=\"og:site_name\" content=\"Trifork Blog\" \/>\n<meta property=\"article:published_time\" content=\"2013-03-14T14:32:05+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2013\/03\/AngularJS-large1.png\" \/>\n<meta name=\"author\" content=\"Jettro Coenradie\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jettro Coenradie\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/\",\"url\":\"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/\",\"name\":\"AngularJS: Lessons learned - Trifork Blog\",\"isPartOf\":{\"@id\":\"https:\/\/trifork.nl\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2013\/03\/AngularJS-large1.png\",\"datePublished\":\"2013-03-14T14:32:05+00:00\",\"author\":{\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/198c00ea654e6a5e38e33511d983613d\"},\"breadcrumb\":{\"@id\":\"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/#primaryimage\",\"url\":\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2013\/03\/AngularJS-large1.png\",\"contentUrl\":\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2013\/03\/AngularJS-large1.png\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/trifork.nl\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"AngularJS: Lessons learned\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/trifork.nl\/blog\/#website\",\"url\":\"https:\/\/trifork.nl\/blog\/\",\"name\":\"Trifork Blog\",\"description\":\"Keep updated on the technical solutions Trifork is working on!\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/trifork.nl\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/198c00ea654e6a5e38e33511d983613d\",\"name\":\"Jettro Coenradie\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/bfce5dacae07c9ed6b0283448d22fee7?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/bfce5dacae07c9ed6b0283448d22fee7?s=96&d=mm&r=g\",\"caption\":\"Jettro Coenradie\"},\"url\":\"https:\/\/trifork.nl\/blog\/author\/jettro\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"AngularJS: Lessons learned - Trifork Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/","og_locale":"en_US","og_type":"article","og_title":"AngularJS: Lessons learned - Trifork Blog","og_description":"At Devoxx 2012 I attended the AngularJS presentation by Igor Minar and Misko Hevery. I was very enthusiastic about the capabilities of this front-end framework. Therefore I started experimenting with it. I created a sample for the Axon Framework, read more about it here. After my experiments I felt confident enough to start using it [&hellip;]","og_url":"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/","og_site_name":"Trifork Blog","article_published_time":"2013-03-14T14:32:05+00:00","og_image":[{"url":"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2013\/03\/AngularJS-large1.png","type":"","width":"","height":""}],"author":"Jettro Coenradie","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Jettro Coenradie","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/","url":"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/","name":"AngularJS: Lessons learned - Trifork Blog","isPartOf":{"@id":"https:\/\/trifork.nl\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/#primaryimage"},"image":{"@id":"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/#primaryimage"},"thumbnailUrl":"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2013\/03\/AngularJS-large1.png","datePublished":"2013-03-14T14:32:05+00:00","author":{"@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/198c00ea654e6a5e38e33511d983613d"},"breadcrumb":{"@id":"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/#primaryimage","url":"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2013\/03\/AngularJS-large1.png","contentUrl":"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2013\/03\/AngularJS-large1.png"},{"@type":"BreadcrumbList","@id":"https:\/\/trifork.nl\/blog\/angularjs-lessons-learned\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/trifork.nl\/blog\/"},{"@type":"ListItem","position":2,"name":"AngularJS: Lessons learned"}]},{"@type":"WebSite","@id":"https:\/\/trifork.nl\/blog\/#website","url":"https:\/\/trifork.nl\/blog\/","name":"Trifork Blog","description":"Keep updated on the technical solutions Trifork is working on!","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/trifork.nl\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/198c00ea654e6a5e38e33511d983613d","name":"Jettro Coenradie","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/bfce5dacae07c9ed6b0283448d22fee7?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/bfce5dacae07c9ed6b0283448d22fee7?s=96&d=mm&r=g","caption":"Jettro Coenradie"},"url":"https:\/\/trifork.nl\/blog\/author\/jettro\/"}]}},"_links":{"self":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts\/7307","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/users\/60"}],"replies":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/comments?post=7307"}],"version-history":[{"count":0,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts\/7307\/revisions"}],"wp:attachment":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/media?parent=7307"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/categories?post=7307"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/tags?post=7307"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}