Suppose I've got following elements on the same page:
- Filters panel (something similar to http://www.imdb.com/search/name)
- Items based on filter options
I want to implement following logic:
- URL should contain applied filter data in path (
/appliedOptionA/appliedOptionB
) - When user opens site app get filter data from URL, updates filter panel and items panel
- When user changes filters app updates URL and refreshes items
First idea: configure ng-router, get filter data as param, convert data to model, construct filters panel, load items, construct items panel. Main problem: how should it work when user changes filter? If I'll update URL it will trigger same controller with different param and repeat the process -> get filter data as param, convert data to model, construct filters panel, items and so on. I don't need it - my model and UI is already up to date.
My question:
what is the correct way to keep model and URL synchronized? Should URL manage model (ng-route
) or model changes manage URL (how?) ?
My recommendation would be to use angular-ui-router and use that instead of
ng-router
sinceangular-ui-router
gives you much more flexibility and is, in my opinion, easier to use.Given your "Main Problem", it sounds like you want to have an single application controller (let's refer to this as the
"appCtrl"
) handle all changes and make the necessary changes to its model. The main drawback with this approach is it forces you to the maintain the "filteredItems" model within theappCtrl
, which is not fun and could lead to unnecessary headaches. An easier way to approach it would be to have another controller deal with those changes to the url, rather than theappCtrl
itself. That being said, let me show you how you could accomplish this.*WARNING: The post is very long! (but hopefully helpful) *
Because of this, I have also created a demo of everything I am about to discuss in case you "just want teh codez":
Creating the application
Since we are creating a brand new application, let's start with creating and configuring the application.
NOTE: Remember to load in the
angular-ui-router
script into your application and add the dependency.app.js
Now that we have the
app
, let's add it to the index.html using the ngApp directive.index.html
With the application in place, let's look at the issues you addressed in your question:
What you want to do is create a state. Essentially, a state is activated/deactivated based on the url pattern. Given a particular pattern, you can tell the application how to act (using a controller) and what to look like (using a template). Before continuing, make sure to reference url routing, which will help with url patterns.
We need to:
"filteredItems"
)Let's configure the application, as well as:
home
statefilteredItems
stateapp.js
We have now configured the state's url to take parameters (
appliedOptionA
andappliedOptionB
). Using$stateParams service
, we can create afilteredItemsCtrl
and have that be responsible for filtering out the data.But before we do that, we should create a service. When using Angular, AVOID using controllers to maintain data since they can be created/destroyed. Best practice is to use services instead. That being said, let's create an amazing
itemService
:itemService.js
Gorgeous right? I am glad you think so too!
Now that we have a service, we can create the
FilteredItemsCtrl
. Let's also inject our newly createditemService
into theFilteredItemsCtrl
, so its able to access the data.NOTE: Remember to inject the
$stateParams
service as a dependency!filteredItemsCtrl.js
When we defined our state's url, we set it up like:
url: "/:appliedOptionA/:appliedOptionB"
. This means the$stateParams
will be populated with appliedOptionA, appliedOptionB properties. Also this means every time the URL changes, a newfilteredItemsCtrl
is created, thus, you do not have to maintain the application's model!Before we forget, we also need to create a template for this state.
filteredItems.html
With the state, filteredItemCtrl, and view created, all that is left is to include the
ui-view
directive, in order for the state's template to properly appear:index.html
That is all there is to it! Sorry about the long post, but hopefully you found this informative! Please let me know if you have any issues!
When you want the search keywords to be reflected in the URL (so they can be bookmarked and addressed directly), I would recommend not to force every change of the model (which would be after every keystroke), but wait for a submit of the form. Then you rewrite the URL (via $location.path and/or $location.search) and the ngRoute will kick in.
NgRoute will (re)load/refresh the page as if it were the first time, and that matches the idea of a directly addressable page that includes search keywords. I would not recommend getting the data as soon as you get the submit, because as you stated, ngRoute will refresh the page.