7

In our Angular app we have to deal with id's that contain a "dot". For example:

book = {
  id: '123.456'
}

We have problems using such id's as url parameters. All works well if navigation occurs through "Angular", namely clicking on the link that invokes $state.go('bookDetails', {bookId: book.id});. But things do not work when reloading the page

"Cannot GET /bookDetails?bookId=123.456"

in the controller:

$scope.viewBookDetails = function() {
    $state.go('bookDetails', {bookId: book.id});
}

in the view

<a href="" ng-click="viewBookDetails(); $event.stopPropagation();">

in the router:

.state('bookDetails', {
    url: '/bookDetails?bookId'
}

in the browser:

https://example.com/bookDetails?bookId=123.456

The link works if the "dot" is replaced with %2E in the browser.

We tried to replace "dot" with "%2E" in the parameter for $state.go()

$scope.viewBookDetails = function() {
    $state.go('bookDetails', {bookId: book.id.split('.').join('%2E')});
}

but does not work because the "%" is automatically encoded and the "dot" in the browser is replaced by "%252E"

https://example.com/bookDetails?bookId=123%252E456
1

3 Answers 3

6

The refresh problem I was getting with a url query parameter containing a 'dot' is a server problem.
It is caused by the way I deal with html5mode (redirect to index.html if is not a static resource) in the grunt server settings

// The original grunt server settings
connect: {
  options: {
    port: 9000,
    // Change this to '0.0.0.0' to access the server from outside.
    hostname: 'localhost',
    livereload: 35729
  },
  livereload: {
    options: {
      open: true,
      middleware: function (connect) {
        return [
          require('connect-modrewrite')(['^[^\\.]*$ /index.html [L]']), //Matches everything that does not contain a '.' (period) and causes the problem
          connect.static('.tmp'),
          connect().use(
            '/bower_components',
            connect.static('./bower_components')
          ),
          connect().use(
            '/app/styles',
            connect.static('./app/styles')
          ),
          connect.static(appConfig.app)
        ];
      }
    }
  },

I changed

require('connect-modrewrite')(['^[^\\.]*$ /index.html [L]']),

to

require('connect-modrewrite')([
  '!\\.html|\\.js|\\.css|\\.svg|\\.jp(e?)g|\\.png|\\.gif|\\.ttf$ /index.html'
]),
Sign up to request clarification or add additional context in comments.

Comments

2

If you are using connect-history-api-fallback on your server (like lite-server does), the URLs with a dot are not rewritten by default.

connect-history-api-fallback code

if (parsedUrl.pathname.indexOf('.') !== -1) {
  logger(
    'Not rewriting',
    req.method,
    req.url,
    'because the path includes a dot (.) character.'
  );
  return next();
}

Starting with connect-history-api-fallback version 1.2.0 the URLs with dots are allowed and you can solve this problem by using a a rewrite roule

Example

If your URL with dot is /api/test/:id (ex. /api/test/123.34 ) and you angular app lives in the index.html page you can add a rewrite rule to the connect-history-api-fallback like this

rewrites: [
  {
    from: /^\/api\/test\/[0-9]+\.[0-9]+$/,
    to: 'index.html'
    }
  }
]

Comments

1

I completely revised the code. I can use dots fine so please fork the plunker to show where you are getting an error.

http://plnkr.co/edit/Ct09Q9uoc282JuWdsO1s?p=preview

console.log("Scripts loading... ");

// Here's a skeleton app.  Fork this plunk, or create your own from scratch.
var app = angular.module('demonstrateissue', ['ui.router']);

app.controller('myController', function($scope, $state){
  $scope.book = {
  id: '123.456'
};
  
  $scope.viewBookDetails = function() {
    console.log('new id');
    $state.go('bookDetails', {bookId: 456.3456});
  }
});



// Empty config block.  Define your example states here.
app.config(function($stateProvider, $urlRouterProvider, $urlMatcherFactoryProvider) {
  $stateProvider.state('bookDetails', {
    url: '/bookDetails:bookId',
    controller: function($scope, $stateParams) { 
      $scope.book = {
       id: $stateParams.bookId
      };
    },
    template: "<h3>book: {{book}}</h3>"
  });

  $urlRouterProvider.otherwise("/bookDetails/91.23");
});

// Adds state change hooks; logs to console.
app.run(function($rootScope, $state, $location) {
  
  $rootScope.$state = $state;
  $rootScope.$location = $location;
  
  function message(to, toP, from, fromP) { 
    return from.name  + angular.toJson(fromP) + " -> " + to.name + angular.toJson(toP);
  }
  
  $rootScope.$on("$stateChangeStart",   function(evt, to, toP, from, fromP)      { console.log("Start:   " + message(to, toP, from, fromP)); });
  $rootScope.$on("$stateChangeSuccess", function(evt, to, toP, from, fromP)      { console.log("Success: " + message(to, toP, from, fromP)); });
  $rootScope.$on("$stateChangeError",   function(evt, to, toP, from, fromP, err) { console.log("Error:   " + message(to, toP, from, fromP), err); });
});
body { 
  margin-top: 6em;
}
.link { 
    text-decoration: underline;
    color: blue;
}

.link:hover {
    cursor: pointer;
}

.header { 
  position: fixed;
  right: 0;
  top: 0;
  height: 6em;
  width: 100%;
  border-bottom: 1px solid gray;
}
<!DOCTYPE html>
<html>
  <head>
    <script src="https://code.angularjs.org/1.2.25/angular.js"></script>
    <script src="https://rawgit.com/angular-ui/ui-router/0.2.13/release/angular-ui-router.js"></script>
    <script src="main.js"></script>
    <link rel="stylesheet" href="styles.css" />
    <title>Plunk demonstrating ui-router issue</title>
  </head>

  <body ng-app="demonstrateissue">

      <div ui-view>ui-view not populated</div>  
    
      <div class="header">
        Current URL: <b>{{$location.url() }}</b> <br>
        Current State: <b>{{$state.current.name }}</b> <br>
        Current Params: <b>{{$state.params | json }}</b><br>
      </div>
    <br/>
    <br/>
    <div ng-controller="myController">
      {{book}}
      <br/>
      <a ui-sref="bookDetails({bookId: 6789.1011})">Change params to book with dot</a><br/>
      <button ng-click="viewBookDetails()">View Book Details</button>
      </div>
  </body>
</html>

8 Comments

how would you use this in $stateProvider.state() ?
I updated the code please review and let me know where your error.
As I already said, the problem is not going to the state but when reloading the bookDetails page (browser refresh when on the bookDetails).
I put my demo app in Webstorm ran it. Then tried links and clicked refresh and everything works fine. I can change my params and it works fine. Please review your code compared to mine. If you STILL are having issues Paste your code in a plunker so users can help you debug a working example instead of just slices you copied and pasted.
interesting, I am using html5mode, could that be a difference?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.