Cascading dropdowns that keep state in the querystring - angularjs

I am trying to build a page with three cascading dropdowns, lets say Brand, Model and Year, which is rather easy using something like Knockoutjs.
The thing is that I'd like to keep state in the querystring, so while making my choices, the URL is being updated also to something like '/Cars?Brand=Toyota' etc. (if the browser supports it).
Furthermore, if the users hits '/Cars?Brand=Toyota&Model=Corolla', I'd like the dropdowns to reflect that state.
At first I thought that something like this would be trivial with Knockoutjs, but is seems that it's not.
So, is there a way to 'bind' to querystring parameters using Knockoutjs? If not, is this something that can be done using a framework like Angular?

The easiest way would to just bind the values to your view model and then use jquery to update the querystring when the values change.
Updating existing URL querystring values with jQuery
If you then wanted to be really clever then i imagine you could move this code into a custom binding extension.
http://knockoutjs.com/documentation/custom-bindings.html

Related

Angular lookup value/display value

I have some data on a model that comes in the form of a code such as "US60" and "US70".
I need to take that value and show a display value such as "US 7day/60hour" and "US 8day/70hour". I'm not sure if there is any best practices way to do this in Angular, and I'm not having much luck googling it.
What I would do is have a service that I pass in type and value, and it would return a display value, but as with many things in Angular, since this is my first Angular project, I don't know if it's a good way to do it or not.
I'm just needing to use the display value in html such as {{settings.cycle}} I am already able to access the variable, but I want to show the display value, not the actual value.
If I am getting the gist of your question correctly, you have the value available but want to alter how it is displayed on screen right?
There are two main approaches to do this in Angular, using a directive or a filter.
A filter is basically like a pipe in Unix. You can alter a value before it is being displayed. For example:
{ username | uppercase } will transform the username into an all-caps username. Naturally, you can define your own filters for your use case. Filters are mostly used to transform single values. So for your case, a filter sounds best.
A directive is commonly used to create entire components on a page. For example: <user-profile-card></user-profile-card> would be transformed, using the directive, into the appropriate html/css/logic. So these are used often for larger transformations which involve logic, like server requests. Still these directives could also be used for very small components.
So for your case, although what you are actually want to do is not completely clear to me honestly, a filter seems to be your best shot ;)

is angulars reloadOnSearch evil?

I've been programming with angularjs for some time now, however, i started using reloadOnSearch for my application and at first i thought this was going to save me for uneccesary reloads of controllers and ajax calls against my API.
For example say i have a list of things, and then present them in a view. When you first access this view it fetches the list of items from the API and presents them, and when you click on an item it adds the ?id=xxx&view=show query parameter to the url without reloading the controller.
When we access a single item from the already loaded list, it just fetches the list item from that we already have and presents it. However, if we forcibly reload the page, the controller now realizes that it has to fetch it from the API instead as it doesn't exist within the list collection.
At first this seemed like a great thing. However as i think about it, i now have to manage the state of the entire controller, whereas before i could have state enclosed within single functions in the controller.
I seem to be having trouble deciding if reloadOnSearch is evil and should be avoided, or if it is worth keeping around. What is your opinion?
Also, would it be better to use something like ui-router instead? I just saw a introduction video which implies that one can have better control of state.
I've converted my app to use ui-router instead. which basically nests application logic in "sub scopes" making it possible to share data between states/pages. I realize now that this is possible without ui-router aswell, however one would have to create pages that has a hierarchy of <div ng-controller>
So in conclusion, the way i was using reloadOnSearch was indeed evil. However, there were better ways of using it aswell.

Putting presentation data in angular controller?

Got a webapp I'm building in Angular.
This app walks a user to authorizing accounts, presenting specific instructions based on the users choices.
I've implemented this as HTML that is shown or hidden based on values in the model, so for 3 different choices, I have 3 different sets of HTML sections that are similar but with different texts.
In the spirit of DRY, I should instead have one set of HTML sections, and instead switch the text based on the values of the model. This means putting text data inside the model, including small snippets of markup, like anchor and strong tags.
Does putting presentation data into the controller violate the principals of Angular?
There are quite a number of options to avoid repeating code depending on what you are looking to do. The following ideas are things I would consider and use when they make sense (I placed these from simple to complex, so you probably can skip the first few):
ng-bind -- Put it on a span/div. Simple & works to bind the model to the display
ng-switch, ng-if, ng-hide, ng-show -- Work to conditionally show an element
custom directive -- use this when you want to alter the behavior of an element or if you want to alter the dom based on a template. If you use "ng-transclude" the contents of the element you template will be included in the result. This can be very elegant but it works best when you have a single format. I can provide examples but angular's documentation also has excellent examples.
service -- I generally use this just to provide data only. This could be via a restful api and $resource or via $http calls. Either way, I wouldn't recommend doing much more than load/save data here.
$scope method -- In other words:
$scope.myMethod = function(x,y,z) { /* code making decisions based on the model */ }
Then you can call this method from one of the previous either via a prebuilt directive (ng-show, etc) or via a custom directive that manipulates the dom for how you expect it to be.
ng-bind-html -- Last option I know to suggest is to use this directive combined with the $sce service to bind whatever you want to the DOM. If you are binding something with angular code in it - make sure to use the $compile service as well. I generally don't favor this approach except as a last resort because it makes it harder to find where elements in the DOM are coming from and it can make debugging + testing a real pain. That said, these tools wouldn't exist if people didn't need them.
I'm sure that this isn't complete and maybe others have suggestions but that is where I would start. Best of luck!
I would put the text data in a separate angular service. This article gives an example: http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/
Then if you decided at some point to move it to some other storage, your service would still be the single access point for the rest of the app.

Preferred way of creating links with backbone.js

I'm trying to wrap my head around backbone.js but I'm finding it hard due to the lack of (IMO) good examples.
First of all, what is the best way of getting a link to an object.
If I want to get the edit url of an Album model I could do album.url() + '/edit', is this really the best way?
Also, I'm trying to make my application work 100% without javascript so I don't want my URLs/links to say /albums/#1/edit, I want it to be /albums/1/edit and override this in JS.
I'm thinking I create normal URLs and use jQuery.live to call router.navigate in backbone.js
I never got this to work however, when I call router.navigate('/albums/2', true) the URL changes but my show action is never called. If I refresh it's called so the route is matched.
What am I missing?
The basic answer, which is kind of frustrating, is "there is no preferred way!". Backbone.js doesn't tell you how to set up links, you can do it any way you like. I found this flexibility just as annoying as you do, at least at first.
So here's the way I'm approaching this on my current project, with the (big) caveat that this is just one of many ways to do things in Backbone:
For the most part, I don't use actual links. There's no explicit reason not to, but it means you have to keep track of a bunch of URL strings that have to be consistent. I would rather stick all the URL formatting in my routers and not deal with it elsewhere.
To open a new "top-level" view, like an editing screen, I set something that fires an event. In the application I'm currently working on, I have a global State model, and to open a new view I call state.set({ topview: MyTopView }). This causes the state object to trigger change:topview.
Any piece of the UI that needs to change when the top-level view changes has an update method bound to change:topview. When the event fires, they look at state.get('topview') and update as necessary.
I treat my routers as only marginally specialized parts of the UI - they're essentially views that render in the browser address bar, rather than the window. Like other views, they update the state object on UI events (i.e. a new URL), and like other views, they listen to the state object for changes that cause them to update. The logic that the editing screen has the URL albums/<albumid>/edit is fully encapsulated in the router, and I don't refer to it anywhere else.
This works well for me, but it adds an entirely new pattern, the global State object, to the Backbone structure, so I can hardly call this the "preferred" approach.
Update: Also note that .url(), in the Backbone idiom, refers to the model's URL in the back-end API, not the front-end URL (it's not like Django's get_absolute_url). There is no method in the default Backbone setup that gives you a user-facing URL for your model - you'd have to write this yourself.
Also, I'm trying to make my application work 100% without javascript; so I don't want my URLs/links to say /albums/#1/edit, I want it to be /albums/1/edit and override this in JS.
you can do exactly this w/ pushState. just enable it in your Backbone.history.start call:
Backbone.history.start({pushState: true})
this tells Backbone to use the HTML5 History API (a.k.a. "PushState"), which uses full URLs exactly like you're wanting.
read up on the history api here: http://diveintohtml5.ep.io/history.html
and I wrote up a 2 part series on using pushstate w/ the second part focusing on progressive enhancement in backbone, to do what you're needing:
http://lostechies.com/derickbailey/2011/09/26/seo-and-accessibility-with-html5-pushstate-part-1-introducing-pushstate/
and
http://lostechies.com/derickbailey/2011/09/26/seo-and-accessibility-with-html5-pushstate-part-2-progressive-enhancement-with-backbone-js/
hope that helps :)

CakePHP - Can you make forms submit params as named params?

So GET forms make the usual urls like
.../search/?q=apple
Can you make a form create urls like
.../search/q:apple/
Thanks, guys. I've found a different solution. I just submit the form as a POST and in the controller's action I read the post data and create a url with the post data as named params and then $this->redirect('...'); to it.
The methods to create these URL's can be found here: http://book.cakephp.org/view/842/url
If I understand you correctly, you're not looking to create a different URI, per se, but rather to serialize the form data in a different way. In other words, you're interested in modifying the query string rather than the URI itself.
As far as I know, that's the way that forms serialize their data and there's no way to truly override this behavior. If you really want to do this, I suspect you'll have to capture the submit event, manually serialize the form data into the format you want, append that format to the form's action value, make a custom request to the page (via location.href, etc.) and return false so that the form itself never actually gets submitted.
Of course, you could also submit via Ajax where you have a little more control.
I'm not aware of any other way to do what I think you're asking.

Resources