简体   繁体   中英

How to make Angular JS use a hash URL to represent an action?

I've written a program that uses DOM-manipulation and jQuery to respond to the user inputting a comma-separated list of values in a hash-URL and wish to do it in Angular, instead.

I have been writing a program, on and off, in my spare time, that draws fractal images, such as the famous Mandelbrot fractal. Here's the URL: http://danielsadventure.info/html5fractal/docs/intro.html . I did this as an exercise to flex my HTML5 muscles with features like the canvas element and web workers. The program works great. Here is a rendered image of the "Negabrot" fractal from my program:

在此输入图像描述

Recently, I've been teaching myself Angular, and I decided to rewrite the Javascript using Angular instead of jQuery. Once again, I'm doing this as an exercise.

Angular is, indeed, a very suitable tool because there are lots of forms that the user may use to describe the fractal image to be drawn. I was able to use Angular to bind a model to the forms and get rid of the DOM-manipulation code that I was previously using. Yay! Angular is awesome!

There is another feature of my program that it is not entirely clear how I should convert it to work with Angular. My program is a Single Page Application. There is only one thing it does: draw fractal images. However, I use hash-URLs to keep track of what fractal is being drawn and what configuration is used to draw it. For example, you can follow the URL below to see a zoomed-in 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 list of comma-separated values that describe different aspects of the programs configuration. If you draw something beautiful with it, you can simply send someone else the URL and they can draw the same thing; easy as pie!

In order to accomplish this, I listen for an event that indicates that the hash-URL has changed and respond to it by updating the configuration on the form, once again using old-fashioned DOM-maniputation.

I previously asked on StackOverflow how to respond to hash-URLs, and I was directed to ngRoute. ngRoute looks very useful, but it looks like it is associated primarily with templates and controllers.

In my program, I need not load any additional templates. All I need is to respond to a new hash-URL by updating the configuration and drawing a new fractal. I also want to update the hash-URL with the same when the user manually updates the configuration and draws a new fractal.

In short, what I want to happen is this:

When the user enters a new hash-URL, the program should respond by updating the model that is bound to the inputs so that the form values change.

When the user manually changes the inputs and clicks a button to draw again, the hash-URL should be updated with the new configuration.

With angular ui-router you could 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 url property you can specify your params. I've picked just some of your params.

$stateParams service will inject all the params that are passed in the url.

The following is just to show how I've 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 app you'll probably create a directive for your fractal class because you're doing DOM stuff. I'm just adding everything in the controller of the state to keep the demo simple. You can add the directive to the fractal.html partial.

Please have a look at the demo below or in this jsfiddle . Please notice that you're not seeing the url parameters in jsfiddle.

In your app they will be present like in the following screenshot: 在此输入图像描述

 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> 

ok I am not quite sure what JS code you already have, so I am going to show some snippets you may find helpful!

This is a watch on the URL - so everytime 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 a 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
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM