How do I make Angular JS use a hash url to represent an action?

TL; DR;

I wrote a program that uses DOM manipulation and jQuery to respond to the user by entering a comma-separated list of values ​​in the hash url and would instead want to do this in Angular.

Long version

In my spare time, I wrote a program that captures fractal images, such as the famous Mandelbrot fractal. Here's the url: http://danielsadventure.info/html5fractal/docs/intro.html . I did this as an HTML5 muscle flexion exercise with features like canvas element and web workers. The program works great. Here is a rendering of the "Negabrot" fractal image from my program:

enter image description here

I recently taught myself Angular and I decided to rewrite Javascript using Angular instead of jQuery. Once again, I do this as an exercise.

Angular is indeed a very suitable tool because there are many shapes that the user can use to describe the fractal image to be drawn. I was able to use Angular to bind the model to the forms and get rid of the DOM manipulation code I used earlier. Hooray! Angular is awesome!

There is one more feature of my program that is not entirely clear how I should transform it to work with Angular. My program is a single page application. There is only one thing: paint fractal images. However, I use a hash url to keep track of which fractal is being drawn and what configuration is used to draw it. For example, you can follow the URL below to see an enlarged section of the Multibrot-5 fractal:

http://danielsadventure.info/html5fractal/index.html#103,0.41000000000000014,1.0299999999999998,0.41999999999999993,1.04,2,1261,true,z%20 ^% 205% 20% 2B% 20c, -2,2, -2 , 2

As you can see, the URL consists of a comma-separated list of values ​​that describe various aspects of the program's configuration. If you're drawing something pretty with it, you can just send someone else the url and they can draw the same thing; easy as pie!

To accomplish this, I listen to an event that indicates that the hash url has changed and responded by updating the form's configuration, once again using old-fashioned DOM manipulation.

I previously asked StackOverflow how to react to a hash url and I was directed to ngRoute. ngRoute looks very useful, but it looks like it deals primarily with templates and controllers.

In my program, I don't need to download additional templates. All I need to do is respond to the new hash url by updating the config and creating a new fractal. I also want to update the hash url the same when the user manually updates the config and draws a new fractal.

In short, I want this to happen:

When the user enters a new hash url, the program should respond by updating the model bound to the inputs to change the form values.

When the user manually modifies the input and clicks the button to re-draw, the hash url should update with the new configuration.

+3


source to share


2 answers


With angular ui-router you can do it like this:

angular.module('demoApp', ['ui.router'])
    .config(function($stateProvider, $urlRouterProvider) {
        //
        // For any unmatched url, redirect to /fractal
        $urlRouterProvider.otherwise("/fractal?minr=-0.29&maxr=-0.27&mini=-0.64");
        //
        // Now set up the states
        $stateProvider
           .state('fractal', {
              url: '/fractal?minr&maxr&mini',
              templateUrl: 'app/partials/fract.html',
              controllerAs: 'fract',
              controller: function($stateParams) {
                  console.log($stateParams);
                  var fract = new Fractal($stateParams);
                  this.getSettings = fract.getSettings;
              }
       });
});

      

In the property, url

you can specify your parameters. I have selected just a few of your options.

$stateParams

the service will inject all the parameters that are passed in the URL.

Below is how I implemented the Fractal class:

function Fractal(settings) {
    var that = this;
    this.settings = settings;

    this.getSettings = function() {
        return that.settings;
    }
}

      

And the partial fract.html

looks like this (it only outputs the settings):



<h1>Drawing fractal in this state</h1>
<hr/>
{{fract.getSettings()|json}}

      

In your application, you will probably create a directive for your fractal class because you are doing DOM stuff. I just add everything to the state controller to keep the demo simple. You can add a directive to fractal.html

partial.

Please see the demo below or in this jsfiddle . Please note that you do not see the url parameters in the jsfiddle.

In your application, they will be presented as in the following screenshot: enter image description here

angular.module('demoApp', ['ui.router'])
	.config(function($stateProvider, $urlRouterProvider) {
  //
  // For any unmatched url, redirect to /state1
  $urlRouterProvider.otherwise("/fractal?minr=-0.29&maxr=-0.27&mini=-0.64");
  //
  // Now set up the states
  $stateProvider
    .state('fractal', {
      url: '/fractal?minr&maxr&mini',
      templateUrl: 'app/partials/fract.html',
      controllerAs: 'fract',
      controller: function($stateParams) {
      	console.log($stateParams);
      	var fract = new Fractal($stateParams);
        this.getSettings = fract.getSettings;
      }
    });
});

// here comes your fractal class
function Fractal(settings) {
  var that = this;
	this.settings = settings;

  this.getSettings = function() {
    return that.settings;
  }
}
      

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js"></script>

<div ng-app="demoApp">
    <script type="text/ng-template" id="app/partials/fract.html">
        <h1>Drawing fractal in this state</h1>
        <hr/>
        {{fract.getSettings()|json}}
    </script>

    <div ui-view></div>
    <!-- We'll also add some navigation: -->
    <a ui-sref="fractal({minr:-0.3, maxr:-0.2, mini: -0.64})">Draw fractal</a>
    
</div>
      

Run codeHide result


+2


source


ok I'm not really sure if you already have the JS code, so I'm going to show you some snippets that you might find useful!

This is a clock by url - so every time the hash changes , this function will be called:

$scope.$on('$locationChangeSuccess', function(event, newState, oldState) {
  var values = $location.hash().split(','); // => [103,true,...]
  var desc = ['point1', 'boolean']; // I don't know wich values you have..

  $scope.values = {}; // values model
  angular.forEach(values, function(value, index) {
    $scope.values[desc[index]] = value; // {'point1': 103} => nested attribute
  }); 
});

      



Then you can bind this to the form:

// HTML
<form ng-submit="updateHash()">
  <input ng-model="values.point1" />
</form>

// JS
$scope.updateHash = function() {
  var updatedValues = [];
  angular.forEach($scope.values, function(value) {
    updatedValues.push(value);
  }); 
  $location.hash(updatedValues); // update URL
};

      

+1


source







All Articles