Merge pull request #11154 from agileware/CRM-20421
[civicrm-core.git] / ang / crmRouteBinder.md
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.)