Guide on creating an AngularJS application using Yesod

After successfully developing a small app using Yesod, I am now focused on adding improved interaction to it with the help of AngularJS.

Currently, it appears that the AngularJS support in Yesod is still in an experimental phase. Additionally, the documentation available is not very user-friendly, especially for someone like me who is not well-versed in all of the Yesod concepts.

So, I am considering various ways to integrate AngularJS with the Yesod framework. My proposed approach includes:

  1. Creating the front-end using AngularJS.
  2. Building the web-service with Yesod.
  3. Connecting the front-end and web-service through GET and POST http requests. Data will be sent to the server via input forms (taking advantage of Yesod's capabilities) and information will be sent to the front-end in the form of JSON objects.

While the ideal scenario would be to write everything in Haskell, the current situation may not allow for that. Therefore, I am seeking advice on whether my proposed alternative is a viable one and if there are ways to enhance it.

Thank you.

Answer №1

Although I had no prior knowledge of haskell or yesod, integrating angular with Yesod was surprisingly straightforward. Make sure to carefully follow the steps to create a functional app!

Below are the steps I took to get set up:

  • Started with the Yesod quickstart guide to create a Yesod app

    brew install haskell-stack

    stack new my-project yesod-sqlite && cd my-project

    stack install yesod-bin cabal-install --install-ghc

    stack build

    stack exec -- yesod devel

You can now access a basic OTB web app here. The structure of the generated app is as follows:

https://i.sstatic.net/OZPEF.png

  • Integrated angular, jQuery, and bootstrap using bower
  • Utilized a custom .bowerrc file to store the packages in the static folder

.bowerrc

{
    "directory": "static/bower_modules"
}

bower.json

{
  "name": "my-project",
  "version": "0.0.0",
  "authors": [
      "Atef Haque <<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="6142021310070b33040503060e430e0200">[email protected]</a>>"
  ],
  "description": "Haskell with Angular",
  "keywords": [
      "haskell",
      "angular"
  ],
  "license": "MIT",
  "ignore": [
      "**/.*",
      "node_modules",
      "bower_components",
      "static/bower_modules",
      "test",
      "tests"
  ],
  "dependencies": {
      "angular": "~1.5.3",
      "angular-route": "~1.5.3",
      "bootstrap": "~3.3.6"
  }
}

Running bower install installs the packages in the static/bower_packages directory

https://i.sstatic.net/bLoYf.png

  • Added scripts and templates to the static/scripts and static/templates directories respectively

https://i.sstatic.net/ZkgOC.png

app.js

var app = angular.module('app', ['ngRoute']);

app.run(['$rootScope', function ($rootScope) {
    $rootScope.title = 'home';
}]);

app.config(['$routeProvider', function ($routeProvider) {
    $routeProvider
    .when('/', {
        templateUrl : 'static/templates/layout.html',
        controller  : 'HomeCtrl'
    });
}])

app.controller('HomeCtrl', ['$scope', 'Comment', function ($scope, Comment) {
    $scope.comments = [];
    $scope.post = function () {
        Comment
        .post($scope.message)
        .success(function (data) {
            $scope.comments.push(data);
        })
        .error(function (error) {
            console.log(error);
        });
    };
}])

app.service('Comment', ['$http', function ($http) {
    this.post = function (comment) {
        return $http
        .post('http://localhost:3000/comments', {message: comment})
    }
}])

layout.html

<div class="jumbotron">
    <div class="container">
        <div class="page-header" align="center">
          <h1>Haskell <small>+</small> Angular</h1>
        </div>
        <div class="well well-lg">
            <div class="row">
                <div class="col-lg-12">
                <form role="form" ng-submit="post()">
                        <legend>Post a comment</legend>
                        <div class="form-group">
                            <label for="">Message</label>
                            <input type="text" class="form-control" placeholder="Message" ng-model="message">
                        </div>
                        <button type="submit" class="btn btn-primary">Comment</button>
                    </form>
                </div>
            </div>
            <hr style="border: 2px solid steelblue">
            <div class="row">
                <div class="col-lg-6" ng-repeat="comment in comments">
                    <div class="panel panel-info">
                        <div class="panel-heading">
                            <h3 class="panel-title">Comment #{{ comment.id }}</h3>
                        </div>
                        <div class="panel-body">
                            {{ comment.message }}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

With the frontend now set up, it was time to configure the backend to serve the index.html for angular to take control!

  • Modified the templates/default-layout-wrapper.hamlet and removed unnecessary default content

default-layout-wrapper.hamlet head

<!doctype html>
<html lang="en" ng-app="app">
  <head>
    <meta charset="UTF-8">

    <title>{{ title }}
    <meta name="description" content="">
    <meta name="author" content="">

    <meta name="viewport" content="width=device-width,initial-scale=1">

    <style link="rel" src="static/bower_modules/bootstrap/dist/css/bootstrap.min.css">

    ^{pageHead pc}

default-layout-wrapper.hamlet body

<body>
    <div class="container" ng-controller="HomeCtrl">
      <div ng-view>
    <script type="text/javascript" src="static/bower_modules/jquery/dist/jquery.min.js">
    <script type="text/javascript" src="static/bower_modules/bootstrap/dist/js/bootstrap.min.js">
    <script type="text/javascript" src="static/bower_modules/angular/angular.js">
    <script type="text/javascript" src="static/bower_modules/angular-route/angular-route.min.js">
    <script type="text/javascript" src="static/scripts/app.js">

Unfortunately, Stackoverflow may not support hamlet code snippets, so I had to separate it.

Now, when you visit here, you'll have a web app with an angular frontend powered by a Yesod backend.


Notable Points to Consider:

  1. Posting comments functions without additional backend code? Yes, it's built-in :)

I hope this explanation clarifies any confusion you may have had!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Leverage the AJAX Search Lite Plugin within the top navigation of your WordPress website's header file

I am interested in utilizing the "AJAX Search Lite 3.0.6" plugin within my WordPress Theme. Plugin Link After implementing the shortcode, I noticed that it was not appearing on the navigation bar as expected: <body> <!-- Header --> & ...

How to use jQuery to delete specific table rows while keeping the first row safe from removal

Currently, I have a jQuery script that successfully removes table rows when a button is clicked. However, I want to prevent the button from removing the first row of the table. Is there a way to achieve this? $("#remove").click(function(event) { ...

Is there a way to automatically zoom in when clicking on a marker and centering the map to that

I have integrated a map into my project where I am currently plotting random coordinates. These coordinates are stored in a data table and everything is functioning smoothly. However, I am facing an issue with implementing a zoom feature using the panTo m ...

I encountered a Node.js 203 error specifically on my computer - could this be linked to a specific environment and is there a way to resolve it

Let me explain what happened: I was working on a Nodejs-express-angular example by Brian Ford the other day, and everything was running smoothly. update:----------------this part of problem has been solved---------- However, after a few hours (during wh ...

Tips for preventing tiny separation lines from appearing above and below unordered list elements

I am attempting to utilize twitter bootstrap to create a select-option style list. How can I eliminate the thin separation lines above and below the list of items? Refer to the screenshot: https://i.sstatic.net/1khEE.png Below is the visible code snippe ...

I need assistance in locating an error; the error message states that $ is not defined and an object is expected

Issue with $ not being defined, object expected.. I am trying to verify if all sets of radio buttons are checked when a button is clicked! Please help. <script type="text/javascript> $(document).on('click', 'form', function () { ...

Is it possible to install in a directory other than the one specified in package.json

I have organized my folders exactly how I want them to be. In one specific folder, all of my configuration files reside (most importantly package.json). I am looking to install this package.json configuration in a different path, specifically c\instal ...

determining the position of the substring located within an "if any" function

I have two lists A and B. My goal is to extract a string from list A, find a matching string in list B, and then determine the index of that matching string in list B. I've attempted several nested loops without success. I haven't come across an ...

Populating dropdown menu with data fetched from an AJAX request

I've created a dropdown in my cshtml page: @( Html.Kendo().DropDownList().Name("ddlCode").OptionLabel("Select Code...").BindTo(@ViewBag.dropDown) .DataTextField("Title") .DataValueField("domainCode") The dropdown is bound up ...

Is it possible to filter a single field for two different values in a relationMapping using Objection.js?

In an Objection.js model, I have a relation mapping where I need to set a filter on a field that can only have two possible values: null or 0. Here is an example of the relation I am using: static get relationMappings() { return { dipendenti: { ...

The React MUI Tab component triggers a re-render and clears the states of its child components

In our endeavor to create a Tab Page using React MUI, we aim for each Tab to contain a child component. While there are no issues when adding these child components individually to a page without tabs, problems arise when incorporating them within the Tab ...

Trouble with ScrollView not scrolling on Nativescript-Vue

I am facing an issue with a scrollable view in my project. I have a list of items that should be scrollable, but for some reason it is not scrolling as expected. The structure involves a vertical stack layout wrapped in a scrollview, and inside the stackla ...

Utilizing JSONObject List in ZK Grid and Listbox

Hi everyone, I am looking for some assistance with the zk (zkoss) framework as I am still new to it. My goal is to create a grid or listbox using a list of JSON objects. I have been searching for examples that can guide me on how to use a list of JSON obje ...

Ways to create distinct identifiers within a recurring function:

I am using this function twice: function (data) { $.each(data.items, function(i,item) { var $items = $('<div class="col-sm-4 grid-item"><div class="thumbnail"><input type="checkbox" name="thing_'+i+'" ...

Executing a personalized function within a Server Component with the Next JS App Router while the application is running

In my server component Home, I call the function getMyAge: // app/page.tsx import { getMyAge } from './utils/datetime'; export default function Home() { return ( <Page> <Paragraph>I'm {getMyAge()} years old</Parag ...

Monitor the item for fluctuations in its worth

Is there a way to watch an object and wait for all of its values to become truthy? const myObject = { key1: null, key2: null, key3: null, } // Perform asynchronous operations that update myObject const checkValues = async () => { awa ...

Deciphering click event parameters in an AngularJS application

Currently, I am in the process of improving a feature in an existing application that has already been deployed. Unfortunately, all the JavaScript code is minified, and I only have access to the HTML files. I need to implement a function that triggers when ...

The autocomplete feature in Codemirror seems to be failing specifically when writing JavaScript code

I am having trouble implementing the autocomplete feature in my JavaScript code editor using Codemirror. Can someone please help me figure out what I'm doing wrong? Here is the snippet of code : var editor = CodeMirror.fromTextArea(myTextarea, { ...

Is it advisable to authenticate/authorize static files employed in pages that require authentication?

I am in the process of developing a cutting-edge angular 2 application. All back-end functionality is handled through REST API, ensuring seamless communication between the client and server. For enhanced security measures, all APIs that require authentica ...

What could be causing the jQuery effect to not function properly?

After completing a course on Codecademy, I successfully ran the code. However, I prefer to copy and paste the code into my own jquery folder for future reference and practice. The objective of this project was to make the element 'krypton' bounc ...