| 1 | # crmRouteBinder |
| 2 | |
| 3 | Live-update the URL to stay in sync with controller data. |
| 4 | |
| 5 | ## Example |
| 6 | |
| 7 | ```js |
| 8 | angular.module('sandbox').config(function($routeProvider) { |
| 9 | $routeProvider.when('/example-route', { |
| 10 | reloadOnSearch: false, |
| 11 | template: '<input ng-model="filters.foo" />', |
| 12 | controller: function($scope) { |
| 13 | $scope.$bindToRoute({ |
| 14 | param: 'f', |
| 15 | expr: 'filters', |
| 16 | default: {foo: 'default-value'} |
| 17 | }); |
| 18 | } |
| 19 | }); |
| 20 | }); |
| 21 | ``` |
| 22 | |
| 23 | Things to try out: |
| 24 | |
| 25 | * Navigate to `#/example-route`. Observe that the URL automatically |
| 26 | updates to `#/example-route?f={"foo":"default-value"}`. |
| 27 | * Edit the content in the `<input>` field. Observe that the URL changes. |
| 28 | * Initiate a change in the browser -- by editing the URL bar or pressing |
| 29 | the "Back" button. The page should refresh. |
| 30 | |
| 31 | ## Functions |
| 32 | |
| 33 | **`$scope.$bindToRoute(options)`** |
| 34 | *The `options` object should contain keys:* |
| 35 | |
| 36 | * `expr` (string): The name of a scoped variable to sync. |
| 37 | * `param` (string): The name of a query-parameter to sync. (If the `param` is included in the URL, it will initialize the expr.) |
| 38 | * `format` (string): The type of data to put in `param`. May be one of: |
| 39 | * `json` (default): The `param` is JSON, and the `expr` is a decoded object. |
| 40 | * `raw`: The `param` is string, and the `expr` is a string. |
| 41 | * `int`: the `param` is an integer-like string, and the expr is an integer. |
| 42 | * `bool`: The `param` is '0'/'1', and the `expr` is false/true. |
| 43 | * `default` (object): The default data. (If the `param` is not included in the URL, it will initialize the expr.) |
| 44 | * `deep` (boolean): By default the json format will be watched using a shallow comparison. For nested objects and arrays enable this option. |
| 45 | |
| 46 | ## Suggested Usage |
| 47 | |
| 48 | `$bindToRoute()` was written for a complicated routing scenario with |
| 49 | multiple parameters, e.g. `caseFilters:Object`, `caseId:Int`, `tab:String`, |
| 50 | `activityFilters:Object`, `activityId:Int`. If you're use-case is one or |
| 51 | two scalar values, then stick to vanilla `ngRoute`. This is only for |
| 52 | complicated scenarios. |
| 53 | |
| 54 | If you are using `$bindToRoute()`, should you split up parameters -- with |
| 55 | some using `ngRoute` and some using `$bindToRoute()`? I'd pick one style |
| 56 | and stick to it. You're in a complex use-case where `$bindToRoute()` makes |
| 57 | sense, then you already need to put thought into the different |
| 58 | flows/input-combinations. Having two technical styles will increase the |
| 59 | mental load. |
| 60 | |
| 61 | A goal of `bindToRoute()` is to accept inputs interchangably from the URL or |
| 62 | HTML fields. Using `ngRoute`'s `resolve:` option only addresses the URL |
| 63 | half. If you want one piece of code handling all inputs the same way, you |
| 64 | should avoid `resolve:` and instead write a controller focused on |
| 65 | orchestrating I/O: |
| 66 | |
| 67 | ```js |
| 68 | angular.module('sandbox').config(function($routeProvider) { |
| 69 | $routeProvider.when('/example-route', { |
| 70 | reloadOnSearch: false, |
| 71 | template: |
| 72 | '<div filter-toolbar-a="filterSetA" />' |
| 73 | + '<div filter-toolbar-b="filterSetB" />' |
| 74 | + '<div filter-toolbar-c="filterSetC" />' |
| 75 | + '<div data-set-a="dataSetA" />' |
| 76 | + '<div data-set-b="dataSetB" />' |
| 77 | + '<div data-set-c="dataSetC" />', |
| 78 | controller: function($scope) { |
| 79 | $scope.$bindToRoute({expr:'filterSetA', param:'a', default:{}}); |
| 80 | $scope.$watchCollection('filterSetA', function(){ |
| 81 | crmApi(...).then(function(...){ |
| 82 | $scope.dataSetA = ...; |
| 83 | }); |
| 84 | }); |
| 85 | |
| 86 | $scope.$bindToRoute({expr:'filterSetB', param:'b', default:{}}); |
| 87 | $scope.$watchCollection('filterSetB', function(){ |
| 88 | crmApi(...).then(function(...){ |
| 89 | $scope.dataSetB = ...; |
| 90 | }); |
| 91 | }); |
| 92 | |
| 93 | $scope.$bindToRoute({expr:'filterSetC', param:'c', default:{}}); |
| 94 | $scope.$watchCollection('filterSetC', function(){ |
| 95 | crmApi(...).then(function(...){ |
| 96 | $scope.dataSetC = ...; |
| 97 | }); |
| 98 | }); |
| 99 | } |
| 100 | }); |
| 101 | }); |
| 102 | ``` |
| 103 | |
| 104 | (This example is a little more symmetric than a real one -- because the A, |
| 105 | B, and C datasets look independent. In practice, their loading may be |
| 106 | intermingled.) |