Backbone.js loads URL twice because of encoding mismatch - backbone.js

I'm still using Backbone.js and am having some problems with URL's loading twice (at least in Chrome) when there is JSON in the hash.
Say I want to navigate to this hash: #{"name":"Viktor"}. After encoding it with encodeURIComponent('{"name":"Viktor"}') it becomes %7B%22name%22%3A%22Viktor%22%7D.
If I then try to navigate to the hash like this.navigate('%7B%22name%22%3A%22Viktor%22%7D') Backbone decodes the hash to #{"name":"Viktor"} and saves it. Then a hashchange event is triggered which calls Backbone.history.checkUrl() which indirectly calls Backbone.history.getHash() and returns hash #{%22name%22:%22Viktor%22} from window.location.href, but this is only partly decoded, at least in Chrome.
This leads to the following check failing:
if (current === this.fragment) return false;
Because #{"name":"Viktor"} does not equal #{%22name%22:%22Viktor%22}. I think this is the gist of the problem. If I override Backbone.history.getHash() and replace %22 with " my problem is solved, but I feel I should do something different?
Thank you in advance!

Your issue is a bug fixed in Backbone 1.4, so I will suggest updating your Backbone version.
The only way I think it can be solved is patching Backbone as you done.

Related

Resolving relative $resource path in Angular 1.5

My Angular 1.5.8 web application is at http://www.example.com/foo/ and my RESTful list of bars is at http://www.example.com/foo/api/bars. When ui-router goes to the list of bars in Angular, my browser is at http://www.example.com/foo/#/bars. I simply want to connect to a RESTful resource to connect to http://www.example.com/foo/api/bars using HTTP GET.
So I try the most obvious thing:
$resource("api/bars/")
In my mind that should resolve api/bars/ to the current path http://www.example.com/foo/#/bars to give me http://www.example.com/foo/api/bar, right? But it doesn't work (even though I could swear it worked with $http). Instead it gives me a $resource:badcfg error.
Angular lets me do the following, but this doesn't produce the correct path, instead giving me http://www.example.com/api/bars:
$resource("/api/bars/")
So I try $browser.baseHref(), but that seems to be set to the empty string (why?), producing http://www.example.com/api/bars again.
$resource($browser.baseHref() + "/api/bars/")
I tried to use $location.path(), but Angular thinks I want the path after the hash sign, so that doesn't work, giving me http://www.example.com/bars/api/bars. Doh!
$resource($location.path() + "/api/bars/")
How can I simply resolve api/bars/ to the base path of the current URL http://www.example.com/foo/#/bars, producing http://www.example.com/foo/api/bars (or even just the path)?
Note that it is not acceptable for me to hard code an absolute path, or to change my HTML code. Why should I? Besides, this application will be mounted at two places on the same server (hey, it's easy with Java, Tomcat, and JAX-RS), at http://www.example.com/foo/ and http://www.example.com/foo-demo/. The application should run unmodified under each location, accessing the RESTful API list of bars at http://www.example.com/foo/api/bars and http://www.example.com/foo-demo/api/bars, respectively.
This is not complicated; this is simple "URI syntax 101" which has been outlined in RFCs for over two decades. I simply want to resolve api/bars/ to the base path of the current URL http://www.example.com/foo/#/bars. How do I do that in Angular 1.5?
OK, there's two ways to do this. If you absolutely want an absolute path, you can use this:
$resource(location.origin + location.pathname + "api/bars/")
Note the use of the location object location.origin to establish the protocol, domain, and port; and location.pathname to establish the base path. (Once again Microsoft will throw a wrench into the works: location.origin doesn't work on IE11 on some versions of Windows 10.)
But the better approach is simply to use:
$resource("api/bars")
"Wait, I thought you said that this will give you an error!" you might exclaim. Well it turns out I was wrong about that. I was indeed getting an error, but it was because I hadn't set the Accept header to ask for JSON, and the plain/text response I was getting (that's the default on my RESTful resource) was confusing $resource. It turns out that a relative URI will work after all.
It is possible to use relative-path syntax for $resource as you can use it with $http which is used by $resource. This can be done by omitting the leading slash (as you suggested) or by prefixing the resource with ./.
Your badcfg error seems more likely to be the case because your request works out correctly but the server response does not match the expectations. So probably you are returning a single object instead of an array. Please use the dev tools to debug what is really returned from the server.
See the docs on $resource for details.

swap or shuffle Page

In myNavigator at present there are three pages: bottom.html, middle.html, and top.html. I want to get the bottom.html above the top.html and for that I am calling pushPage.
But there are two issues here:
The stack looks like this bottom.html, middle.html, top.html, bottom.html.
The new bottom.html is fully reconstructed, so it is wasting lots of
resources in rebuilding the same content.
I tried resetToPage function but the issue is still same.
Since you have tagged Onsen UI 2 then you can use the bringPageTop method which seems to be doing exactly what you want. ^_^
So you can do either of
myNavigator.bringPageTop(0)
myNavigator.bringPageTop('bottom.html')
Good luck with your app!

How can we detect when the model is back to its original value?

JS Bin here
This article describes a clever way to see if your (pure Angular) form model has changed since the initial load. In my mind it's a more accurate test of $dirty/$pristine. This allows us to do things like hide the "Save" button for a form if the model hasn't changed, even if the user has typed then deleted text.
So the question is, how can we do this with angular-formly? My hunch is that onChange for each field might be a good starting point, but I'm having a lot of trouble putting together the rest of the solution.
Hope the question makes sense. Any ideas would be very welcome. Thanks!
This might work in a limited capacity:
scope.initialValue;
var listener = scope.$watch(function(oldVal,newVal){
scope.initialValue = oldVal
listener();
})
atinder was correct in that the pure Angular approach actually works fine with angular-formly. I've created a working JS Bin here.

How do I reliably check the current page after navigation in Selenium WebDriver?

In my web tests using Selenium WebDriver and IEDriverServer.exe 2.32.3.0, I need to check the title of the current page after navigating to a certain URL. Here's the code:
_webDriver.Navigate().GoToUrl("...");
Assert.That(_webDriver.Title, Is.EqualTo("..."));
This sometimes works, but sporadically breaks - the title is not as expected (but still the one from the page before).
I've read on StackOverflow (C# Webdriver - Page Title assert fails before page loads) that IWebDriver.Title property does not auto-wait for navigation to complete (why doesn't it?), but instead you need to manually wait using the WebDriverWait API.
I implemented manual waiting for the title:
var wait = new WebDriverWait(_webDriver, TimeSpan.FromSeconds (3.0));
wait.Until(d => d.Title == expectedTitle);
However, this sometimes waits for 3 seconds, then throws a WebDriverTimeoutException. The build agent running the code is quite fast and the web site I'm testing is trivial (just starting development), so I'm quite sure that it can't really take 3 seconds to navigate. I noticed that on the other StackOverflow question, the original poster also got the WebDriverTimeoutException and just caught and ignored it.
I found that solution a little flaky, so I tried a different workaround. I gave the <title> attribute an ID in my HTML and used IWebDriver.FindElement, which is supposed to wait for the page to complete:
Assert.That(_webDriver.FindElement(By.Id(ViewIDs.Shared._Layout.Title)).Text, Is.EqualTo(page.ExpectedTitle));
At first, that seemed to work. However, it doesn't reliably, it sometimes throws:
OpenQA.Selenium.NoSuchElementException : Unable to find element with id == _Layout-title
at OpenQA.Selenium.Remote.RemoteWebDriver.UnpackAndThrowOnError(Response errorResponse)
at OpenQA.Selenium.Remote.RemoteWebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
at OpenQA.Selenium.Remote.RemoteWebDriver.FindElement(String mechanism, String value)
at OpenQA.Selenium.By.FindElement(ISearchContext context)
at ...
(I also tried upgrading to WebDriver/IEDriverServer 2.33.0.0; but in that version, the text of the <title> tag is always empty...)
Hence my question. How do I reliably check the current page after navigation in Selenium WebDriver? Is there a good pattern that works?
I just solved it with stuff I derived from something I found on CodeProject:
private void WaitUntilLoaded()
{
var wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver, TimeSpan.FromSeconds(%));
wait.Until(x =>
{
return ((IJavaScriptExecutor)this.driver)
.ExecuteScript("return document.readyState").Equals("complete");
});
}
According to w3schools it works for most popular browsers.
As I presently test on Firefox I ended up on Mozilla's own page about how to recognise a page as loaded. IMHO read worth to avoid dragons.
That gave me
wait.Until(p => p.FindElement(
By.Id("somethingThatIsDisplayedWhenMyAsyncronousStuffIsFinished")
).Displayed);
which saved my day.
I have run into very similar issues. I still have not been able to tell why but often if you try to use an element without doing a isElementPresent check it will fail occasionally. My solution is mildly "expensive" but I have not found a reliable alternative as yet.
My example is in java, shouldn't be hard to translate but I don't have the libraries.
waitForElement("_Layout-title", 45);
then continue with your code
Assert.That(_webDriver.FindElement(By.Id(ViewIDs.Shared._Layout.Title)).Text, Is.EqualTo(page.ExpectedTitle));
This tries a set number of times, in this case 45, to find the element. Once id=_Layout-title is present it will continue to the next line of code and do the assert. While it isn't perfect, it did make my test much more stable.

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 :)

Resources