GitHub PullRequestReviewCommentEvent at 2014-12-09 01:13:31
Repository: thinknear.github.com
Actor: igorb2
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls/comments/21501138","id":21501138,"diff_hunk":"@@ -0,0 +1,281 @@\n+---\n+layout: post\n+title: \"Advanced Angular UI Router Part I: Nested Views\"\n+date: 2014-10-24 13:18:03 -0700\n+comments: true\n+categories: \n+---\n+\n+At ThinkNear, we have an in-house administrative dashboard that our ad operations team uses to set up and manage ad campaigns.\n+The dashboard is an AngularJS frontend with a Ruby on Rails backend.\n+Our first version worked, but it was missing back button support, and permalinks were an ugly hack.\n+To fix these issues, we recently overhauled it to use [ui-router](https://github.com/angular-ui/ui-router).\n+While ngNewsletter's [Diving deep into the AngularUI Router](http://www.ng-newsletter.com/posts/angular-ui-router.html) was helpful, I found it didn't go deep enough.\n+\n+\n+### Background\n+\n+Our object hierarchy goes Contract > Insertion Order > Campaign.\n+We have many Contracts, Contracts can have many Insertion Orders, and Insertion Orders can have many Campaigns.\n+\n+The original design was an expanding list.\n+From the master Contract list, you could expand any number of Contracts.\n+Within each expanded Contract, you could navigate to any of its child Insertion Orders or Campaigns.\n+The idea was that our ad ops often needed to reference one object while working on another.\n+However, allowing any number of expansions at any level made it hard to save the UI state into the URL, and prevented us from using UI Router effectively.\n+\n+\n+We asked the Ad Ops team, and found that they never looked at more than one Contract at a time on the same page.\n+When they wanted to view multiple Contracts, they opened multiple tabs or windows in the browser and looked at them side-by-side.\n+What they really needed was an easy way to open multiple Contract tabs from the main list, and a good single-Contract view.\n+Additionally, they wanted to be able to use the back button to go up the hierarchy.\n+\n+\n+### Goal \n+\n+Together with our Product team, we re-designed the page structure to better meet the needs of Ad Ops.\n+Here's what we came up with:\n+\n+Contract List View:\n+\n+    [Site Header]\n+      [Contract Section Header]\n+        [Contract1]\n+        [Contract2]\n+        …\n+    [Site Footer]\n+    \n+Contract Detail View:\n+\n+    [Site Header]\n+      [Contract Section Header]\n+        [Contract 1 Header]\n+          [Back to List button]\n+        [Contract 1 Details]\n+          [Contract 1 Insertion Order 1]\n+          [Contract 1 Insertion Order 2]\n+            …\n+    [Site Footer]\n+\n+Insertion Order Detail View:\n+\n+    [Site Header]\n+      [Contract Section Header]\n+        [Insertion Order 1 Header]\n+          [Back to Contract button]\n+        [Insertion Order 1 Details]\n+          [Insertion Order 1 Campaign 1]\n+          [Insertion Order 1 Campaign 1]\n+          …\n+    [Site Footer]\n+\n+Campaign Detail View:\n+\n+    [Site Header]\n+      [Contract Section Header]\n+        [Campaign 1 Header]\n+          [Back to Insertion Order button]\n+        [Campaign 1 Details]\n+    [Site Footer]\n+\n+One wrinkle was that despite the conceptual nesting, we wanted a flat URL structure for permalinks:\n+\n+* /contracts - List\n+* /contracts/new - New contract\n+* /contracts/1 - Contract 1 details\n+* /contracts/insertion_orders/new - New insertion order\n+* /contracts/insertion_orders/1 - Insertion order 1 details\n+* /contracts/campaigns/new - New campaign\n+* /contracts/campaigns/1 - Campaign 1 details\n+\n+Once states are set up, UI Router handles permalinks and back button support.\n+The tricky part was organizing the states to get both the permalinks and site layout we wanted.\n+\n+We tried nesting the states but using absolute URL patterns to flatten the URL structure:\n+\n+    .state('contracts', { \n+      url: \"/contracts\" \n+    })\n+    .state('contracts.contract_selected', { \n+      url: \"/{contract_id:[0-9]+}\"\n+    })\n+    .state('contracts.contract_selected.insertion_order_selected', { \n+      url: \"^/contracts/insertion_orders/{insertion_order_id:[0-9]+}\"\n+      // Absolute URL with no contract ID\n+    })\n+    \n+Unfortunately, this doesn't work because UI router requires the `contract_id` be present for the parent state!\n+So we structured the states to mirror our permalink structure:\n+\n+    .state('contracts', { \n+      url: \"/contracts\" \n+    })\n+    .state('contracts.contract_selected', { \n+      url: \"/{contract_id:[0-9]+}\"\n+    })\n+    .state('contracts.insertion_order_selected', { \n+      url: \"/insertion_orders/{insertion_order_id:[0-9]+}\"\n+    })\n+\n+To get the site layout we wanted, we took advantage of UI Router's [named views](https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views).\n+Named views allow you to have DOM elements from child states replace DOM elements from parents. \n+If you declare a state with the `templateUrl` and `controller` at the top level, \n+it will implicitly insert it in an un-named `ui-view` element in the parent. \n+But f you declare a `views` property in your state, you can name views, and specify where in the hierarchy they should be inserted.","path":"source/_posts/2014-10-24-advanced-angular-ui-router-part-i.markdown","position":124,"original_position":124,"commit_id":"9fc5a534103a433fa428d62fdd64266cbf7f288a","original_commit_id":"9fc5a534103a433fa428d62fdd64266cbf7f288a","user":{"login":"igorb2","id":6913055,"avatar_url":"https://avatars.githubusercontent.com/u/6913055?v=3","gravatar_id":"","url":"https://api.github.com/users/igorb2","html_url":"https://github.com/igorb2","followers_url":"https://api.github.com/users/igorb2/followers","following_url":"https://api.github.com/users/igorb2/following{/other_user}","gists_url":"https://api.github.com/users/igorb2/gists{/gist_id}","starred_url":"https://api.github.com/users/igorb2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/igorb2/subscriptions","organizations_url":"https://api.github.com/users/igorb2/orgs","repos_url":"https://api.github.com/users/igorb2/repos","events_url":"https://api.github.com/users/igorb2/events{/privacy}","received_events_url":"https://api.github.com/users/igorb2/received_events","type":"User","site_admin":false},"body":"There's no i in team, but there is one in 'if':  \"...But if you declare..\"","created_at":"2014-12-09T01:13:31Z","updated_at":"2014-12-09T01:13:31Z","html_url":"https://github.com/ThinkNear/thinknear.github.com/pull/8#discussion_r21501138","pull_request_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls/8","_links":{"self":{"href":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls/comments/21501138"},"html":{"href":"https://github.com/ThinkNear/thinknear.github.com/pull/8#discussion_r21501138"},"pull_request":{"href":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls/8"}}},"pull_request":{"url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls/8","id":25614039,"html_url":"https://github.com/ThinkNear/thinknear.github.com/pull/8","diff_url":"https://github.com/ThinkNear/thinknear.github.com/pull/8.diff","patch_url":"https://github.com/ThinkNear/thinknear.github.com/pull/8.patch","issue_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/issues/8","number":8,"state":"open","locked":false,"title":"Angular UI Router - Nested Views","user":{"login":"selenetelenav","id":4967788,"avatar_url":"https://avatars.githubusercontent.com/u/4967788?v=3","gravatar_id":"","url":"https://api.github.com/users/selenetelenav","html_url":"https://github.com/selenetelenav","followers_url":"https://api.github.com/users/selenetelenav/followers","following_url":"https://api.github.com/users/selenetelenav/following{/other_user}","gists_url":"https://api.github.com/users/selenetelenav/gists{/gist_id}","starred_url":"https://api.github.com/users/selenetelenav/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/selenetelenav/subscriptions","organizations_url":"https://api.github.com/users/selenetelenav/orgs","repos_url":"https://api.github.com/users/selenetelenav/repos","events_url":"https://api.github.com/users/selenetelenav/events{/privacy}","received_events_url":"https://api.github.com/users/selenetelenav/received_events","type":"User","site_admin":false},"body":"A post describing the advanced UI router functions we use in Ventura 2","created_at":"2014-12-06T03:31:02Z","updated_at":"2014-12-09T01:13:31Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2805a339055dfea4fcd457f7260c80b712c2b4f6","assignee":{"login":"igorb2","id":6913055,"avatar_url":"https://avatars.githubusercontent.com/u/6913055?v=3","gravatar_id":"","url":"https://api.github.com/users/igorb2","html_url":"https://github.com/igorb2","followers_url":"https://api.github.com/users/igorb2/followers","following_url":"https://api.github.com/users/igorb2/following{/other_user}","gists_url":"https://api.github.com/users/igorb2/gists{/gist_id}","starred_url":"https://api.github.com/users/igorb2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/igorb2/subscriptions","organizations_url":"https://api.github.com/users/igorb2/orgs","repos_url":"https://api.github.com/users/igorb2/repos","events_url":"https://api.github.com/users/igorb2/events{/privacy}","received_events_url":"https://api.github.com/users/igorb2/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls/8/commits","review_comments_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls/8/comments","review_comment_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls/comments/{number}","comments_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/issues/8/comments","statuses_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/statuses/9fc5a534103a433fa428d62fdd64266cbf7f288a","head":{"label":"selenetelenav:uirouter","ref":"uirouter","sha":"9fc5a534103a433fa428d62fdd64266cbf7f288a","user":{"login":"selenetelenav","id":4967788,"avatar_url":"https://avatars.githubusercontent.com/u/4967788?v=3","gravatar_id":"","url":"https://api.github.com/users/selenetelenav","html_url":"https://github.com/selenetelenav","followers_url":"https://api.github.com/users/selenetelenav/followers","following_url":"https://api.github.com/users/selenetelenav/following{/other_user}","gists_url":"https://api.github.com/users/selenetelenav/gists{/gist_id}","starred_url":"https://api.github.com/users/selenetelenav/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/selenetelenav/subscriptions","organizations_url":"https://api.github.com/users/selenetelenav/orgs","repos_url":"https://api.github.com/users/selenetelenav/repos","events_url":"https://api.github.com/users/selenetelenav/events{/privacy}","received_events_url":"https://api.github.com/users/selenetelenav/received_events","type":"User","site_admin":false},"repo":{"id":27620387,"name":"thinknear.github.com","full_name":"selenetelenav/thinknear.github.com","owner":{"login":"selenetelenav","id":4967788,"avatar_url":"https://avatars.githubusercontent.com/u/4967788?v=3","gravatar_id":"","url":"https://api.github.com/users/selenetelenav","html_url":"https://github.com/selenetelenav","followers_url":"https://api.github.com/users/selenetelenav/followers","following_url":"https://api.github.com/users/selenetelenav/following{/other_user}","gists_url":"https://api.github.com/users/selenetelenav/gists{/gist_id}","starred_url":"https://api.github.com/users/selenetelenav/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/selenetelenav/subscriptions","organizations_url":"https://api.github.com/users/selenetelenav/orgs","repos_url":"https://api.github.com/users/selenetelenav/repos","events_url":"https://api.github.com/users/selenetelenav/events{/privacy}","received_events_url":"https://api.github.com/users/selenetelenav/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/selenetelenav/thinknear.github.com","description":"thinknear.github.com","fork":true,"url":"https://api.github.com/repos/selenetelenav/thinknear.github.com","forks_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/forks","keys_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/keys{/key_id}","collaborators_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/teams","hooks_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/hooks","issue_events_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/issues/events{/number}","events_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/events","assignees_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/assignees{/user}","branches_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/branches{/branch}","tags_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/tags","blobs_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/git/refs{/sha}","trees_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/git/trees{/sha}","statuses_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/statuses/{sha}","languages_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/languages","stargazers_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/stargazers","contributors_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/contributors","subscribers_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/subscribers","subscription_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/subscription","commits_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/commits{/sha}","git_commits_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/git/commits{/sha}","comments_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/comments{/number}","issue_comment_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/issues/comments/{number}","contents_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/contents/{+path}","compare_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/compare/{base}...{head}","merges_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/merges","archive_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/downloads","issues_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/issues{/number}","pulls_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/pulls{/number}","milestones_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/milestones{/number}","notifications_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/labels{/name}","releases_url":"https://api.github.com/repos/selenetelenav/thinknear.github.com/releases{/id}","created_at":"2014-12-06T03:12:08Z","updated_at":"2014-12-06T03:12:08Z","pushed_at":"2014-12-06T03:30:49Z","git_url":"git://github.com/selenetelenav/thinknear.github.com.git","ssh_url":"git@github.com:selenetelenav/thinknear.github.com.git","clone_url":"https://github.com/selenetelenav/thinknear.github.com.git","svn_url":"https://github.com/selenetelenav/thinknear.github.com","homepage":null,"size":2693,"stargazers_count":0,"watchers_count":0,"language":"CSS","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"source"}},"base":{"label":"ThinkNear:source","ref":"source","sha":"e55b8ade9c6ecc9294ae2662eeaca5a03b8f830c","user":{"login":"ThinkNear","id":653390,"avatar_url":"https://avatars.githubusercontent.com/u/653390?v=3","gravatar_id":"","url":"https://api.github.com/users/ThinkNear","html_url":"https://github.com/ThinkNear","followers_url":"https://api.github.com/users/ThinkNear/followers","following_url":"https://api.github.com/users/ThinkNear/following{/other_user}","gists_url":"https://api.github.com/users/ThinkNear/gists{/gist_id}","starred_url":"https://api.github.com/users/ThinkNear/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ThinkNear/subscriptions","organizations_url":"https://api.github.com/users/ThinkNear/orgs","repos_url":"https://api.github.com/users/ThinkNear/repos","events_url":"https://api.github.com/users/ThinkNear/events{/privacy}","received_events_url":"https://api.github.com/users/ThinkNear/received_events","type":"Organization","site_admin":false},"repo":{"id":23165489,"name":"thinknear.github.com","full_name":"ThinkNear/thinknear.github.com","owner":{"login":"ThinkNear","id":653390,"avatar_url":"https://avatars.githubusercontent.com/u/653390?v=3","gravatar_id":"","url":"https://api.github.com/users/ThinkNear","html_url":"https://github.com/ThinkNear","followers_url":"https://api.github.com/users/ThinkNear/followers","following_url":"https://api.github.com/users/ThinkNear/following{/other_user}","gists_url":"https://api.github.com/users/ThinkNear/gists{/gist_id}","starred_url":"https://api.github.com/users/ThinkNear/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ThinkNear/subscriptions","organizations_url":"https://api.github.com/users/ThinkNear/orgs","repos_url":"https://api.github.com/users/ThinkNear/repos","events_url":"https://api.github.com/users/ThinkNear/events{/privacy}","received_events_url":"https://api.github.com/users/ThinkNear/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ThinkNear/thinknear.github.com","description":"thinknear.github.com","fork":false,"url":"https://api.github.com/repos/ThinkNear/thinknear.github.com","forks_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/forks","keys_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/teams","hooks_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/hooks","issue_events_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/issues/events{/number}","events_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/events","assignees_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/assignees{/user}","branches_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/branches{/branch}","tags_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/tags","blobs_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/git/refs{/sha}","trees_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/statuses/{sha}","languages_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/languages","stargazers_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/stargazers","contributors_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/contributors","subscribers_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/subscribers","subscription_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/subscription","commits_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/commits{/sha}","git_commits_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/git/commits{/sha}","comments_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/comments{/number}","issue_comment_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/issues/comments/{number}","contents_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/contents/{+path}","compare_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/merges","archive_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/downloads","issues_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/issues{/number}","pulls_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls{/number}","milestones_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/milestones{/number}","notifications_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/labels{/name}","releases_url":"https://api.github.com/repos/ThinkNear/thinknear.github.com/releases{/id}","created_at":"2014-08-20T21:52:40Z","updated_at":"2014-11-26T18:52:27Z","pushed_at":"2014-11-26T18:53:06Z","git_url":"git://github.com/ThinkNear/thinknear.github.com.git","ssh_url":"git@github.com:ThinkNear/thinknear.github.com.git","clone_url":"https://github.com/ThinkNear/thinknear.github.com.git","svn_url":"https://github.com/ThinkNear/thinknear.github.com","homepage":null,"size":2693,"stargazers_count":0,"watchers_count":0,"language":"CSS","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":5,"mirror_url":null,"open_issues_count":2,"forks":5,"open_issues":2,"watchers":0,"default_branch":"source"}},"_links":{"self":{"href":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls/8"},"html":{"href":"https://github.com/ThinkNear/thinknear.github.com/pull/8"},"issue":{"href":"https://api.github.com/repos/ThinkNear/thinknear.github.com/issues/8"},"comments":{"href":"https://api.github.com/repos/ThinkNear/thinknear.github.com/issues/8/comments"},"review_comments":{"href":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls/8/comments"},"review_comment":{"href":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/ThinkNear/thinknear.github.com/pulls/8/commits"},"statuses":{"href":"https://api.github.com/repos/ThinkNear/thinknear.github.com/statuses/9fc5a534103a433fa428d62fdd64266cbf7f288a"}}}}