I have a form where user is able to choose search options. When user clicks "Search" button,
an appropriate GET controller's action is invoked:
public ActionResult Search(SearcherViewModel model, int pageNo=1)
{
var results = xService.GetSearchResults(model);
return View("Index", results);
}
GetSearchResults method does not connect to the database, but instead it call some third party web service. This however is not the main issue.
Therefore, the url can look as follows:
http://localhost/Search?startDate=20120210&offerType=3&foodId=4&&Destination=456
How can I implement a pagination of search results? In particular, how should I construct the page numbers and how to use my model?
Kinda depends on how much stuff you are paginating. If it is small then you can hold the whole thing in javascript memory using preloading and then paginate based on the javascript objects. If you are looking at potentially tens of thousands or more of items to paginate, then you may consider preloading part of that and then using ajax to load pages later on depending on how the user navigates.
Related
In Sails.js, a route is set up against a controller method which can render a view. For the most part, this is straightforward, i.e. you could set up a GET /users route that points to UserController.find (which is usually set up automatically anyway).
However, say the home page of a blog renders the 10 most recent posts in the main section and a column with a list of authors and categories. The controller method has to fetch posts, authors, and categories before rendering the view and sending it back to the client. Clearly, a method like this doesn't really belong in PostController, AuthorController, or CategoryController.
What's the best thing to do in this situation? Create a controller for rendering views that rely on data from multiple models? Is there a good name for such a controller?
Thanks!
What I would do (this is purely opinion-based) is creating a PageController and create an action for each page you'd want.
For your home page example you can create a home action, get whatever you need and then render it with res.ok() (if everything is fine).
Another option would be to use Sails as a pure API and use HTTP requests (Ajax) or sockets to get your data in JSON. If you want to do so, I'd advise you to use a front end framework such as Angular, Ember, React...
By the way you could also create actions rendering HTML in your existing controllers and create a route to hit them through Ajax requests and just print them in your page. I'd prefer the 2nd solution because it takes full advantage of the Blueprint API (you don't need new controller or action whatsoever).
As Yann pointed out, this answer has to be a little opinionated. It seems that you are using the views system and not building a single page application. For the home page, I would go for an IndexController.js file with a home(req, res) action.
// api/controllers/IndexController.js
module.exports = {
home: function (req, res) {
// Retrieve all the information you need
// Take care about managing the asynchronous calls before rendering the view
return res.view('homepage');
}
};
Declare the route
// config/routes.js
module.exports.routes = {
'get /': 'IndexController.home'
}
Create the view in views/homepage.ejs.
If views from different controllers use the same element, requiring different controllers to pass the same datas (hence maybe doing the same processing) to the views, wouldn't it be better to make an AJAX call to a single controller?
Let's say we have this:
Model/Post.php
Model/User.php
Model/Service.php
Controllers/UsersController.php
Controllers/ServicesController.php
Views/Users/view.ctp
Views/Services/view.ctp
Views/Elements/list_users_post.ctp
A user belongs to a service, and a user has many posts.
In Views/Services/view.ctp, I want to display a list of each user of a particular service, and for each user, some related infos and a list of his 10 last posts.
In Views/Users/view.ctp, I want to display user's related infos and a list of his 10 lasts posts.
The element Views/Elements/list_users_post.ctp allows me to factor the code displaying a table of a user's posts. It needs the var $userPostList to be set, and to be structured the same as the result of $this->Post->find('all', array('conditions' => array('user_id' => $userId))).
So in my UsersController::view($userId) and ServicesController::view($serviceId) actions, I end up with some duplicated code retrieving users' posts.
I thought to refactor the code so the action ServicesController::view($serviceId) don't make any find on Post model, but instead, the view Services/index.ctp makes AJAX calls to the action UsersController::view($userId) for each user. That way, no more duplicated code, but with the overhead of AJAX calls.
Any thoughts?
A good way to avoid duplicate code in your case is to move it to the model layer. So instead of this in the controller:
$this->Post->find('all', array('conditions' => array('user_id' => $userId)))
You would have this in the controllers:
$this->Post->getUserPosts($userId);
And this in the Post model:
function getUserPosts($userId){
return $this->find('all', array('conditions' => array('user_id' => $userId)));
}
AJAX calls are also a good solution but even with them it would be best to keep the business logic in the models if possible.
Other ways to avoid duplicate code include(but are not limited to):
Using Helpers in Views
Using Components in Controllers
Using Behaviors in Models
That should get you started on keeping your code DRY.
So right now I am accessing the data using the findAll: 'GET api.php/stores-for-you?pid=977&sid=5938&max_fav=3&max_reco=3' which is a way to access the api for my application where pid is the page id and sid is the section id. So each page has different pid and sid. So as of now I have to make findall call for each api call which i find not a good option.
I was thinking if i can use findone along wih the Findall here. where I can access the api to the particular stores-for-you and then use findone to hit the particular url with the parameters.
Can anyone help me out. If the question is still not clear can you just write me back i would try to explain it more.
Your findAll shouldn't have the page and section IDs hardcoded into the GET string, since that would make your can.Model specific to just that page. Your code that calls findAll should be providing those.
can.Model subclasses represent a type of data that you get from the server. findAll is meant to retrieve some number of objects of that type. Your GET string seems to want to return stores. We could call your model WebStore (I'm avoiding using "Store" because that word has a different meaning in CanJS).
can.Model.extend("WebStore", {
findAll : "GET /api.php/stores-for-you?max_fav=3&max_reco=3"
}, {});
In this spec for your findAll, we've left in some paging keys (max_fav and max_reco seem to be result limiters), so that other code can't come along and request 3000 results at a time. The other ones, though, are for specific pages. They're effectively queries. If you put them into your findAll spec, your Model could only ever retrieve that page, limiting its reusability.
so now in other code, probably a controller prototype init():
var ws_ajax = WebStore.findAll({ pid : 977, sid : 5938 });
ws_ajax.done(function(stores) {
//Handle the array of 0-3 stores returned from the server.
});
So now you can call on other pages and sections later on in other code just by doing the same call with different pid and sid values. You don't need to try to hack findOne for a different page.
HOWEVER... if for some reason you use that particular page/section combination frequently, you might consider using another function on your WebStore model's static properties, and then it would look like this:
can.Model.extend("WebStore", {
findAll : "GET /api.php/stores-for-you?max_fav=3&max_reco=3",
findMyPage : function() {
return this.findAll({ pid : 977, sid : 5938 });
}
}, {});
Note that findMyPage has no special meaning in CanJS. You're just wrapping findAll with frequently used values to save yourself excessive typing and maintenance headaches if those values change.
An exercise left to the reader is to implement "findMyOtherPage" which would find... your other page.
EDIT: more detail added on request.
I'm asking what is the best strategy for filtering with Symfony2.
I want to filter a table of entities (hotels). This filter should allow me to :
choose hotels with or whitout email, with or without web site etc.
choose hotels based on state and/or city (relation OneToMany)
choose what information I want to display on the table with checkboxs (for example display "email adress" on the hotel table, but do not display "tel" or "web site").
First I think to build the filter form on the HotelController. When the filter is submitted, I had a FlashBag for every $_POST sended, redirect to the same page, and if there are FlashBag I send cookies to the $reponse. Then I display the table filtered with data who are on the cookie.
But I dont't really like this, cause I had a very big indexAction() on the HotelController, and I think it'as not really clean to change $_POST to FlasBag to Cookie, is it ? I do this redirection, cause by refreshing the page, data are not posted again.
I'm also asking a question, to prevent a too big IndexAction() method, can I put some code to another method, for exemple a method PostToFlashBag() and another FlashBagToCookie(), or every method on a Controller has to end with the word "Action" and must be accessible with the router ?
Then, I think to another thing : had an entity "Filter", with every row I need. For exemple "WithEmail", "DisplayTel" etc.. Then I can build a FilterType easily, and update the Filter entitie, to redirect to the same page (again, to prevent reposting data if the user refreshes the page). Finally, I can display the table with the object Filter, with a method on the HotelRepository.
That seems great, but I'm a little worry because the filter entity will only have one entry, and I have to find the Filter(1). Due to MVC, is it correct to have a model with only one entry ?
What strategy would you choose (maybe another one) ? I'm interesting to learn good practice with MVC and Symfony2 devloppemnt.
Having a dedicated model class - let's call it Filter - that will receive the values input by the user, is definitely the way to go.
More over, use the Symfony2 form on this input, so you can have validation, and be sure that the withEmailis trully a boolean, etc. From you Filter, build you SQL/Doctrine query and return what your controller have to return, be it a view, or raw datas.
You can have any method you want in a controller. After all, controllers in Symfony2 are plain old PHP objects. They only have to implement ContainerAwareInterface. Usually they inherits Controller, but this inheritance only brings some proxy methods, like getDoctrine or render.
The only convention is that methods which are used as route must end with Action
I was wondering whether anyone had any recommendations regarding populating multiple view models on a screen. I have:
a) a view model that has a list of tasks
b) a view model that has a list of users
I populate these in the 2 x ajax success (separate ajax calls) something like:
success: function (data) {
masterVM.User = ko.mapping.fromJS(data, mapping);
ko.applyBindings(masterVM);
}
/* another ajax call: */
success: function (data) {
masterVM.Task = ko.mapping.fromJS(data, mapping);
ko.applyBindings(masterVM);
}
And currently calling in each ajax success call.
I hope my question is clear and concise. Please ask for further detail if required.
Regards
Phil
We currently handle this by sending all of the data to our page in a single Ajax call, then applying a ko.mapping to map the data to the view models on our main view model.
We began by making two calls just as you are but decided that it would be better to reduce the number of http requests being made, which is why we combined the data sets into a single hierarchical object structure, and it's working great.
This question and the subsequent answer contain a snippet of the approach we're taking:
Map JSON data to Knockout observableArray with specific view model type
As long as you dont notice a performance issue, I recommend making separate calls because this will keep your services loosely coupled with your presentation needs.
However, if its a perf issue, you can make your web service aggregate the data and return it in one shot. But you lose a little maintainability by this.