I am trying to implement a search function for my website. When the user types a search term foobar into a input box and submits it, he is redirected to http://mydomain.com/search?query=foobar.
Problem:: How should I grab the GET parameters query from the URL, and send it to the backend and get a array of results back as a JSON response? Should I even do it this way?
My current attempt below does not even cause the search function to be triggered.
Router
var AppRouter = Backbone.Router.extend({
routes: {
'search?query=:query': 'search'
// ... and some other routes
},
search: function(query) {
this.photoList = new SearchCollection();
var self = this;
this.photoList.fetch({
data: {query: query},
success: function() {
self.photoListView = new PhotoListView({ collection: self.photoList });
self.photoListView.render();
}
});
}
});
var app = new AppRouter();
Backbone.history.start({
pushState: true,
root: '/'
});
There have been several issues filed against Backbone for this very issue. There is an existing plugin that works well for this:
https://github.com/jhudson8/backbone-query-parameters
Alternatively, I'm currently using query string parameters in a mock API that matches Backbone's route matching. Looks something like this
Route
"/api/v2/application/:query"
Query
application: function(query) {
var params = $.deparam(query.slice(1));
// params.something...
}
As to your actual issue at hand how are you redirecting to index.html to support pushState?
I hit this same issue and contemplated using backbone-query-parameters, but that should be considered generally an incorrect approach.
The url query string is not meant for the front end. They get sent to the server and force a refresh when navigating from page.html to page.html?something=something.
You should be using hash fragments instead. i.e. http://www.example.com/ajax.html#key1=value1&key2=value2 then just get those values the normal backbone way and build your request params from that.
See https://github.com/jashkenas/backbone/issues/891, https://developers.google.com/webmasters/ajax-crawling/docs/specification, https://www.rfc-editor.org/rfc/rfc3986#section-3.5
You can always read the URL via jQuery URL plugin. It works well.
https://github.com/allmarkedup/jQuery-URL-Parser
There are very few cases when you need to read the URL and extract the GET params. I think that you are doing things wrong and here are my options:
1) if you are having just one page in your app (single app page) you can display results as they type in your input field or after they hit submit
2) if you are redirecting the user to a different page that means you can bootstrap data so that after the page is loaded backbone will just have to render your results and only make other requests if you change your search word
3) you can have a javascript variable which is initialized on page load directly from the server where working with GET params is probably easier
Related
Am playing with Framework7 to do hybrid mobile app development. I have three tabs (bottom fixed), which are Home, Contacts and Settings/Profile
My app.js file looks somewhat like this:
var $$ = Dom7;
var app = new Framework7({
//.....
data: function () {
return {
user_profile : ''
}
},
on: {
tabShow( tab ) //-- when a bottom tab is clicked
{
if( $$(tab).attr('id') == 'view-settings' )
{
//.. do ajax call and get the response data in "resp"
app.data.user_profile = resp.response.profile; //setting the info to app's data
}
}
},
routes: routes
});
var settingsView = app.views.create('#view-settings', {
url: '/settings/'
});
And in routes.js:
routes = [
{
path: '/',
url: './index.html',
},
{
path: '/contacts/',
componentUrl: './pages/contacts.html',
},
{
path: '/settings/',
componentUrl: './pages/settings.html',
}
];
This Contacts page contains static content. For the Home page, am doing the AJAX API call during the deviceready state. Because am setting up some headers for authentication and stuff(for all the AJAX api calls) in there.
The problem am facing is, am unable to display the content in Settings page. It is always empty!
Am using this in that template page:
<div class="item-title item-label">Full Name - {{$root.user_profile.full_name}}</div>
I want to compile that template only when clicking the respective tab button.
Maybe that's the problem.
Any suggestions?
After going through the documentations again and again, I got another way to do this.
So, during the tabShow event, I check whether the user is accessing the Settings/Profile tab. If so, I check whether an object in app.data (eg: app.data.user_profile is empty or not(am storing the profile details there). If empty, I would do an AJAX API call to get the profile details. When the profile details is obtained, I would use app.form.fillFromData() method to fill the form. Documentation here: https://framework7.io/docs/form.html#form-data-app-methods
Make sure to name the form as well as the input elements in that form, and the same name should be use in the object(key name) when calling the fillFromData() function.
And one more thing, for the routes, /settings/ path, I used url instead of the componentUrl property to pass the url of the page.
This may not be the best solution, but am still learning. And it seems to have solved by current problem.
Thank you
I'm trying out KeystoneJS to build a site in which people can submit words and ask other users for synonyms. So I've build a simple Word model:
var keystone = require('keystone');
var Types = keystone.Field.Types;
var Word = new keystone.List('Word', {
map: { name: 'word' }
});
Word.add({
word: { type: Types.Text, required: true, initial: "New word", index: true }
});
Word.defaultColumns = 'word';
Word.register();
The idea is that a user enters a word in an input box on the homepage, clicks "Submit," and the word gets added as an item in the Word model. But I can't figure out the interplay between the Javascript on the page that handles the event that fires on clicking "Submit" and the code that actually creates a new items in the DB, like so:
var keystone = require('keystone'),
Word = keystone.list('Word');
var newWord = new Word.model({
word: newWord // read from the input box on the home page
});
newWord.save(function(err) {
// post has been saved
});
I originally naively supposed that I could make Word part of the locals object and create the new item from the JS that lives on the page, but this doesn't work. So I imagine I need to read the word from the input box, then make an AJAX call to a route that saves the word to the DB. Here is where my understanding of KeystoneJS breaks down. Where would I put the code to accept that AJAX call and create the new item?
The javascript in the page, and keystone on the server side are indeed two seperate domains. To interact with keystone from your page you have to make HTTP calls. These calls are routed to keystone views.You can find an example in de keystone demo site:
in keystone.js the routes are set:
keystone.set('routes', require('./routes'));
in routes/index.js the routes are defined, ie:
app.get('/blog/:category?', routes.views.blog);
In the example above the url "/blog/news" is handled by the view routes.views.blog, and "news" is an (optional) parameter.
In your case you would end up (if you want you use REST style) with something like:
app.post('/word/:newword?', routes.views.word);
The .post method routes urls to this view if and only if the request is a POST request.
The solution that you suggest, making a AJAX call is one of the possible solutions.You could also use a form and post the whole page.
Within the view you create a handler for the view like this:
view.on('post', function(next){
if (req.params.newword){
//store and create answer
next();
}
});
Is it possible to have a Router implementation similar to the following?
var Router = Backbone.Router.extend({
routes: {
'' : 'search',
'*querystring' : 'results'
},
search: function() {
// load search view
},
results: function(querystring) {
// load search view
// make ajax request using querystring
}
});
The search view has a form that when submitted should go to the results view which will parse the url for the query, submit an ajax request and then display the response.
Obviously something like this would make more sense
'results?*querystring' : 'results'
But I can't get my form to submit the URL in that format.
When put my form action as <form action="index.html/results"> I get http://localhost:8000/index.html/results?c=foo&a=bar as my URL.
This is close, but I really need http://localhost:8000/index.html#/results?c=foo&a=bar and when I try to do this with <form action="index.html#/results"> it gives me http://localhost:8000/index.html?c=foo&a=bar#/results which is not what I want :(
This is why I would rather just have no form action and instead have a route that can will parse the query if one exists.
Ok thanks for reading. Hopefully someone understands some of that and can help me out.
don't put pushstate to true, set it to false
Backbone.js PushStates: Fallback for Internet Explorer not working
Remove form or prevent the submission
Simply get the params and trigger a route
Handle the params appropriately in the triggered route.
Router
routes:{
'search':'search' //queryString is automatically passed as last param in backbone 1.1
},
search: function(queryString){
//Write your logic to do the search
}
View:
events:{
'submit form':'preventAndNavigate'
},
preventAndNavigate: function(e){
e.preventDefault();
var query = $(e.currentTarget).serialize();
Backbone.history.navigate('search?'+query,{trigger:true});
}
Docs :
Backbone Routers now handle query params in route fragments, passing them into the handler as the last argument. Routes specified as strings should no longer include the query string ('foo?:query' should be 'foo').
References :
http://backbonejs.org/#changelog
I have an ember action that submits a form using jquery post like this,
submit : function(){ //submitting new story
this.set('onsubmit' , true);
var self = this;
$.post( "/api/post", { some post data })
.done(function(data) {
self.transitionTo('post' + data);
});
}
The urls are like this, domain example.com. Post is located at example.com/api/post. After posting the user must be redirected to example.com/post/3432232 (some value returned by the post data).
However after posting this transition to "example.com/api/post/5000203" not example.com/post/234234234.
Which doesnt exists and gives a 404. How can I make the user go back to example.com/post/234234234 using transition-to function in ember?
Edit - Routes are as follows
App.Router.map(function() {
this.resource('index', {path : '/'} , function() {
this.resource('p', function(){
this.resource('post', { path: ':post_id' });
});
});
});
Thanks
In Ember routing system transitionTo takes as arguments route's name and route's model (the same model, that route returns when Ember.Route.model hook is invoked). There are two type of router's transitions in Ember's glossary: URL transition (happens when client handles new URL, fo ex., when you open a page from scratch) and "named transition" (which happens when this.tranisionTo is called).
Ember.Route tries to load model based on params list (:post_id, for example) while handling "URL transition". In other hand, while it handling "named transition" it doesn't call model hook, but uses already provided model (this.tranisition('post', PostModel)`). Docs.
So, if you have post_id on hands, just call this.store.find('post', postId) to get PostModel and provide requested model to PostRoute: this.transitionTo('post', this.store.find('post', data).
P.S. Please, consider reading this question and an answer to get your routing structure actually nested: Nested URLs: issues with route transition.
P.P.S. I looked at the Ember's documentation and found new (at least for me) signatures for transitionTo method: http://emberjs.com/api/classes/Ember.Route.html#method_transitionTo. According to this doc, you can rewrite your example to get it work: this.tranisitionTo('post', data), where data is post_id.
I have an index that creates randomly generated dynamic content.
So everytime you load the index, it'll create a series of view that are dependent on what my Rails model has produced and sent to Backbone.
From backbone, I am curious what I could do to "refresh" the page without doing something like this :
window.location = '/'
I'd like to do it within Backbone.. something like this :
Backbone.history.navigate('/', {trigger: true, replace: true});
But this doesn't necessarily send a new request to the url.
All I would need to do to accomplish my goals is send a GET request to /, which should return a JSON object I can pipe through the rest of my Backbone app.
Is there a way to send this request within Backbone? Or should I just go a traditional jQuery route, and just make a $.get request?
Since your REST api returns a JSON object, simply use a Backbone.Model to structure this data. You can then bind events to do whatever you like in your application.
var RandomData = Backbone.Model.extend({ url: '/' });
var randomData = new RandomData();
// Here, `Backbone` can be substituted by any `View`, `Collection`, `Model...
Backbone.listenTo( randomData, 'change', function() {
//Do something everytime this changes.
});
// When you need to issue a GET '/' request. The following will put the
// JSON response inside of `randomData.attributes`
randomData.fetch();