I have a simple form with an input box and a submit button. It's a dictionary service where the user enters a word in the input box and clicks the button which fires an JQuery method. This method, it turn calls an (Merriam-Webster's) API through a server-side PHP and populates a div (#meaning) with the returned data.
Currently, I am doing this using JQuery which means the div gets updated without a page refresh which is sweet. However, this doesn't change the URL. What I need is for the URL to dynamically change every time a new search is performed. For example, if you enter "cat" and hit Lookup, the URL should change to something like:
.../dictionary/cat
Currently, it just stays:
.../dictionary
If somehow, I'm able to replicate the URL change while still avoiding a complete page refresh, the users would be able to bookmark a particular meaning directly without having to look it up each time they need to. I have tried reading up Angular JS for the job but am struggling real hard to get through. My current Angular implementation is one where I have different HTML documents for different views (e.g., /about, /contact, etc.). But in this particular situation, how can I have a pre-made document when the queries are being fired on the fly? Any suggestions?
Not sure if I've framed the question well enough. Please feel free to ask me for any specifics if necessary. I am struggling to get the point across here but if you look at the dictionary service of sites like SpanishDict, you'll get an idea of what I'm trying to achieve.
I don't need you to write the code for me. I just need a general idea of how to go about it, logic-wise, and what implements to consider using. If not Angular JS, I am open to trying a better suited framework.
It looks as though you want to add ngRoute to your app. You can see an example here.
From the docs:
angular.module('ngRouteExample', ['ngRoute'])
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: 'ChapterController'
});
In this example, :bookId and :chapterId are variables that take their value from the URL. For example, with /Book/MobyDick/ch/7, bookId would be "MobyDick" and chapterId would be "7".
In your case, the url in the when function would possibly look like:
when('/dictionary/:word', {
templateUrl: 'yourTemplate.html', // optional
controller: 'yourController' // optional
})
You're doing it the wrong way around. Don't try to update the URL after the search. Instead, searching should just take you to a new URL, and let the logic flow from there, using a route variable.
Related
We're building an AngularJS application. I'm now building a public form that is customized depending on the URL it's loaded with.
For example
http://url.com.au/Registration.cshtml#?org=org1
will look slightly different than
http://url.com.au/Registration.cshtml#?org=org2
When the page loads I would like to load some info with GET http://url.com.au/api/Org/org1
and use the response to drive some UI elements.
At first the differences will be small (URL for logo, values from some checkboxes) but over time the form may be more and more dynamic.
What is the pattern I should follow to support:
simple differences,
further along the journey a more dynamic ui?
For a simple solution, I would hold the url change in a route parameter for that route. Then access it using $routeParams. That way you can change your base GET url easily.
For both simple and dynamic UIs I would try to keep use of ngInclude to a minimum (less files to load = better load times, also it creates a new scope which isn't always ideal).
For simple differences I would structure your html to be as accommodating to both layouts as possible. Think about using ng-show/hide to trigger DOM changes based of variables retrieved from the GET request. For assets like images the GET request could return the path to the image and you can then use ngSrc to include it.
Further along the journey, you may want to consider having nested routing. UI-Router is a very popular solution.
I want to have multiple main index pages that should be accessable independent from each other. Means: there is no global page providing static links to all of the main index pages. Each should live on its own:
WebContent/indexA.html
WebContent/indexB.html
...
Question: how can I write an angularjs controller that shows these pages, if I do not call the*.html extension, but a get-query on that path?
I want to be able to call:
localhost/indexA?param=123
localhost/indexB?test=xyz
Each of them will then map to their own html page, and the request params are to be processed by the controller of that page only.
Is that possible? If yes, how?
If I understood correctly, that you need to use same code (Controller) on different HTML pages, loaded with different URLs.
First option is to use directives to encapsulate same logical parts (Each directive can have attributes, you can provide them in HTML, depend on how is your HTML structured). This is good, when you do not have any routing in your app now.
Second option is to use ui-router with HTML5 routing (then you can pick parameters from URL), this will probably require you to move more logic on frontend, but it is clear solution, if this is not small project.
Next option is to put required parameters into script tag of HTML and then load them from your controller - this is dirty, but quick way of solving your issue.
Try ui-router for the same.
Based on the parameters you pass, you can choose the active state where you can have your own controller and templateURL
I'm making a mobile app using Cordova and AngularJS. Currently I have installed ui-router for routing but I'm open to any other alternative for routing.
My desire: I want to cache certain views bound with parameters. In other words I want to cache paths (or pages).
Example situation: let's say that we see some dashboard page, click on some book cover which redirects to the path book/2. This path is being loaded for the first time into app. Router redirects from HomeController to BooksController (whatever the name). Now the BooksController loads data for given $stateParams (book id = 2) and creates view filled with info about chosen book.
What I want in this situation:
I go back to the dashboard page - it is already loaded (cached?)
I choose book #2 again
Controller or router notices that data about this book is already loaded
The view isn't being recreated, instead it's being fetched from cache
Actually, it would be best to cache everything what I visit based on path. Preloading would be cool too.
Reason: performance. When I open some list of books then I want it to show fast. When view is being created every time, then animation of page change looks awful (it's not smooth).
Any help would be appreciated.
EDIT:
First of all, since I believe it's a common problem for many mobile HTML app programmers, I'd like to precise some information:
I'm not looking for hacks but a clear solution if possible.
Data in the views uses AngularJS, so YES, there are things like ng-bind, ng-repeat and so on.
Caching is needed for both data and DOM elements. As far as I know, browser's layout operation is not as expensive as recreating whole DOM tree. And repaint is not what we can omit.
Having separate controllers is a natural thing. Since I could leave without it I cannot imagine how it would work anyway.
I've got some semi-solutions but I'm gonna be strict about my desire.
Solution 1.
Put all views into one file (I may do it using gulp builder) and use ng-show. That's the simplest solution and I don't believe that anyone knowing AngularJS would not think about it.
A nice trick (from #DmitriZaitsev) is to create a helper function to show/hide element based on current location path.
Advantages:
It's easy.
KIND OF preload feature.
Disadvantages:
all views have to be in a single file. Don't ask why it's not convenient.
Since it's all about mobile devices, sometimes I'd like to "clear" memory. The only way I can think of is to remove those children from DOM. Dirty but ok.
I cannot easily cache /book/2 and /book/3 at the same time. I would have to dynamically create DOM children on top of some templates for each view bound with parameters.
Solution 2.
Use Sticky States AND Future States from ui-router-extras which is awesome.
Advantages:
Separated views.
Very clear usage, very simple since it's just a plugin for ui-router.
Can create dynamic substates. So it would be possible to cache book1, book2 but I'm not sure about book/1 and book/2
Disadvantages:
Again, I'm not sure but I didn't found an example with caching a pair/tuple (view, parameters). Other than that it looks cool.
This is precisely the problem I had to solve for my site 33hotels.com. You can check it and play with the tabs "Filter" and "Filter List" (corresponding to different Routes), and see that the View is updated instantly without any delay!
How did I do it? The idea is surprisingly simple - get rid of the Router!
Why? Because the way the Router works is it re-compiles the View upon every single Route change. Yes, Angular does cache the Template but not the compiled View populated with data. Even if data do not change! As the result, when I used the Router in the past, the switch always felt sluggish and non-reactive. Every time I could notice annoying delay, it was a fraction of second but still noticeable.
Now the solution I used? Don't re-compile your Views! Keep them inside your DOM at all times! Then use ng-hide/ng-show to hide/show them depending on the routes:
<div ng-show="routeIs('/dashboard')">
<-- Your template for Dashboard -->
</div>
<div ng-show="routeIs('/book')">
<-- Your template for Book -->
</div>
Then create a function routeIs(string) inside your Controller to test if $location.path() matches string, or begins with string as I am using it. That way I still get my View for all pathes like /book/2. Here is the function I am using:
$scope.routeBegins = function () {
return _.some(arguments, function (string) {
return 0 === $location.path().indexOf(string);
});
};
So no need to be smart with caching - just keep it in the DOM. It will cache your Views for you!
And the best part is - whenever your data is changed, Angular will instantly update all the Views inside your DOM, even the hidden ones!
Why is this awesome? Because, as user, I don't have to wait for all the parsing and compiling at the moment I want to see the result. I want to click the tab and see my results immediately! Why should the site wait for me to click it and then begin all the re-compiling as I am waiting? Especially when this could be easily done before, during the time my computer is idle.
Is there any downside? The only real one I can think of is loading memory with more DOM elements. However, this actual byte size of my views is negligible, comparing e.g. with all JS, CSS and images.
Another possible but avoidable downside is the re-compilation cost of the hidden views. This is where you can get smart and avoid computation-heavy parts depending on the current routes.
Also, you are not re-compiling the whole View, just the parts affected by data changes, which also lowers computational cost.
I find it quite remarkable that everyone is using Routes and seems to be completely unaware (or ignorant) of this problem.
1) About static pages in the app (views), angular takes care of loading them.
for example: for your dashboard page you need not worry about caching the page, as angular will take care of it. Angular will only load the dashboard view once and on all next requests for the dashboard view, angular will just show you the view(not load the file for view), if it is all a static view without any data loaded by ajax calls.
2) if your dashboard is itself loading the book list(or similar data) via ajax, then you can tell your controller to only load the data once and store it to localstorage and on subsequent requests to the dashboard page can only load the data from the localStorage.
3) similar approach can be used when your BooksController loads the data into a view. You can check in your BooksController if the request for a particular book is been previously made and if not your can store it to localstorage or database. and if the data was previously requested then you can load the same data from the storage without making a request to server.
Example situation:
say user makes request for book1, then
your controller i.e BooksController check whether the same data was requested before,
if not, you can load the data via the ajax call from server and also save it to local storage.
if it was loaded before you will load the data stored in the localstorage or in the database.
If you're using ui.router, then you should take a look at ui.router extras, specifically the sticky states module. This allows you to cache the state 'tree' (including rendered views) so they don't have to be compiled or re-rendered on state changes.
http://christopherthielen.github.io/ui-router-extras/
Here's a demo:
http://christopherthielen.github.io/ui-router-extras/example/sticky/#/
When building a web app where every page depends on many data sources, what's the best way to fetch the initial bits of data? When I look at twitter, I see the tweets that are visible on page load are in the HTML source, and more tweets are loaded in using AJAX as you scroll down. But there's no convenient way to get data that's already in the DOM to be inserted into the model.
Making a request for the initial data, immediately after page load seams stupid, because you've just made a lot of roundtrips to the server to fetch css, html and javascript. Would it be a bad idea to insert the data into a javascript tag on the page, so a javascript function can add the initial data?
I'm specifically asking for angularjs, but if there's an general technique, please let me know as well.
You'll be referencing your controller anyway on page load, so you won't have to have an inline script tag.
You can either set a default model and use the attribute ng-bind on initial load, or call a function to pass back data.
It's pretty typical to fetch data on load in angularjs.
Would it be best to couple Angularjs with an HTTP Client in the backend like Zend_Http_Client or Guzzle to let the server fetch the data. Then, pass the data as json to javascript upon render.
I know Angularjs is designed for Single Page applications. That's why it makes sense that it lazy loads the data.
However, if we're going to move to the approach where we still render the page dynamically and still delegate the task of organizing the content to Angularjs. What framework will be suitable to contain the AngularJS views. Right now, project templates like angular-seed are all static..
That is, the idea is the server serves a page with the embedded json object. Then angular, takes over in the client side, Fetching additional content where needed.
So instead of just a single page of contact (e.g: index.html), we would have several pages like profiles.html, products.html. The help of the backend would be particularly helpful say you have a section which doesn't change often like your username on the top right side of the page. For me, I just think it's better to have these data preloaded in your page and not have to ask the server after the page has been loaded.
As bigblind have noticed, this seems to be the way sites like facebook, gmail, twitter does it. They contain the data embedded on page load. Then, load additional content via services afterwards.
The idea is something like below:
Webservice <---------- Backend------------> Frontend
<------------------------------------------
Backend delegates the task of querying the webservice to provide initial data in the rendered page to the client. Then client, can directly connect to webservice to fetch additional content.
Using the above setup.. What is the ideal development stack?
One way to do it is to create a directive that handles the initialization before binding happens.
For example:
app.directive('initdata', function() {
return {
restrict: 'A',
link: function($scope, element, attrs) {
if ( attrs.ngBind !== undefined)
{
$scope[attrs.ngBind] = attrs.initdata ? attrs.initdata : element.text();
}
}
};
});
This directive takes either the attribute value as initial value for the bound $scope property, or the textvalue of the element.
Example usage:
<div initdata="Foo Bar" ng-bind="test1"></div>
<div initdata ng-bind="test2">Lorem Ipsem</div>
Working example on http://jsfiddle.net/6PNG8/
There's numerous way to elaborate on this; for example parsing the initdata as json and merging it with the scope, and making it work for more complicated binds, like $root.someprop. But the basis is remarkably simple.
According to the answers on this question, a JSON object in a script tag on the page seems to be the way to go. If ayone comes up with a better idea, I'll accept your answer.
I am using hash-based navigation in my AngularJS app rooted at /.
If a user navigates to my app like this:
http://example.com/?foo
A moment after the page loads, something (possibly Angular) is causing the address bar to look different than I expected.
What I saw:
http://example.com/?foo#/?foo
What I expected to see:
http://example.com/?foo#/
Why is this happening, and can I turn it off?
I'd wager you need to be in 'html5 mode' to not have the hash fragment... though I'm uncertain.
http://docs.angularjs.org/guide/dev_guide.services.$location
$locationProvider.html5Mode(true).hashPrefix('!');
In your app configuration, you can mess with that config param and it'd probably get rid of it.
This appears to be duplicating the hash with the path.
Check out the $location service. It has both path() and hash() methods. The second, duplicated part is the hash, the first part is the path.
Unless you are using html5 mode, all of Angular's part of the URL appears in the fragment. The problem is that Angular doesn't know about the base part of the URL (perhaps the ?foo was needed just to get Angular to load) so it won't attempt to manipulate it, it just puts its own stuff on as a fragment.
I suggest the best thing would be to check $window.location.search for a query string, and if you find one do the redirect to the URL you actually want yourself. You'll still need to do that redirect by assigning to $window.location rather than $location and it will force your angular app to reload but at least you'll end up where you want to get to.
Alternatively you could reconfigure your web server to make the appropriate rewrite, but you may not want to or be able to do that.
Or you tell your users to only use URLs they got from the app, not to try to make them up for themselves.