I'm using query parameters for paging and filtering. query-params-new provides a relatively convenient way to manage array returned from route.model.
But now I've got a page with a single object (f.e. group) as model, and a list of dependent objects (f.e. users in group), displayed at the same time. List should have paging.
The problem is that I dont know where I should put request for the list. Query parameters, needed for constructing list query, are only available in route.model method and in controller as properties.
Now I'm trying to find a proper way to get both object and list with paging in same route/controller. None of the ways I could invent, like combining them both into single object in route.model or requesting list from controller's observes function, seem right and clean. Currently I've stopped on saving query parameters in route.model and using them in route.setupController for second request, but that is a hack obviously.
I'm just looking for the right approach, without fighting framework.
Related
using (var context = ContentSearchManager.GetIndex(index).CreateSearchContext())
{
var items = context.GetQueryable<SearchResultItem>()
.Where(item => item.TemplateId == _templateId)
.ToList();
var resultList = new List<MediaItem>();
I then go through and use conditionals to filter the results more and add them to resultList.
I add them to the list with (MediaItem)x.GetItem();
I have terrible performance, which I am sure is caused by GetItem querying the database 10,000s of times.
I need to have the results as a MediaItem due to needing to edit the fields in these items.
How can I avoid querying Sitecore for each item with GetItem() ?
Thanks,
Sitecore Noob
I tried using .GetItem() with poor performance. I also tried researching how to get back Items instead of SearchResultItem
Yes, this would be a very slow and ineffective way of loading the needed items, since you're basically loading references to all media items of the given type into memory from Solr (GetQueryable<T>) and then load everything again from the database through .GetItem(). But the pattern you're trying isn't too far off actually.
Using the SearchContext, you should do the filtering in where you get all items references you need, letting Solr do the computation. I.e. not the 10,000s of items, just the ones you're editing. Once you get the filtered result back from Solr, you perform the .GetItem() on the items you want to work with. Always check that the item (and version) actually exists after a .GetItem() since the Solr index can sometimes be out of sync with the database, so a Solr response can in rare cases reference an item that doesn't exist.
How you should write the Solr query is a bit hard to describe without knowing more details about what filters you need. In general, I'd recommend using the SolrNet API directly instead of going through the ContentSearchManager wrapper. It's sometimes hard to understand what the search manager does under the hood and you basically need to monitor the Search.log in order to verify that your code does what it's supposed to do. A small mistake in the .Where() could cause the entire media library to be loaded into memory and you'll experience the same performance issue again as you have right now. With the query you have today, it's worth noting the difference between the underlying _template and _templates index fields, as the first one (that's used in your sample code) will return items of exactly that given template while the later one will also include all items inheriting from that template.
I want to have a template which has a listing of objects in the column on the left while showing a detail of an object at the same time. Can I somehow make use of generic ListAPIView and RetrieveAPIView? In REST API, a client would just do two requests and compose the data together itself. Is a similar approach possible using TemplateHTMLRenderer?
I think, there are two ways of doing it.
The most common way is to make two async requests on frontend (one for list, second for retrieve).
The second way is to go away from REST and overwrite retrieve method - it should return both information about specific object and about all objects.
If you want to get HTML, try StaticHTMLRender
I am trying to build a UI with left side bar having filters and right side having actual filtered data.
For loading data into the dynamic part of the UI(right side), which approach is considered better in terms of code quality and app performance ?
Use sub routes (for dynamic part of the UI)
Use separate components that load their own data (for dynamic part of
the UI)
There is not a direct correct answer for that; you can use both ways but here is a few things to consider and in the end I generally prefer to use sub-routes due to the following:
Waiting for UI to load: In case you are using separate components to load their own data; then you need to handle the loading state of the components. What I mean is; if you simply use sub-routes; then model hooks (model, beforeModel, etc.) will wait for the promises to be solved before displaying the data. If you simply provide a loading template (see the guide for details) it will be displayed by default. In case you use components, you might need to deal with displaying an overlay/spinner to give a better UX.
Error Handling: Similarly like loading state management; Ember has already built in support for error handling during route hook methods. You will need to deal with that on your own if you prefer components to make the remote calls. (See guide for details)
Application State: Ember is SPA framework; it is common practice to synchronize application state with the URL. If you use sub-routes; you can simply make use of the query parameters (see the guide for details) and you will be able to share the URL with others and the application will load with the same state. It is a little bit trickier to do the same with components; you still need to use query parameters within the routes and pass the parameters to the components to do that.
Use of component hook methods: If you intend to use the components then you will most likely need to use component hook methods to open the application with default filter values. This means you will need to make some remote call to the server within one or more of init, willRender, didReceiveAttrs component hook methods. I personally do not like remote calling within those methods; because I feel like this should better be done within routes and data should be passed to the components; but this is my personal taste of coding that you should approach the case differently and this is perfectly fine.
Data down, actions up keeps components flexible
In your specific example, I'll propose a third option: separate components that emit actions, have their data loaded by the route's controller, and never manipulate their passed parameters directly in alignment with DDAU.
I would have one component, search-filter searchParams=searchParams onFilterChange=(action 'filterChanged'), for the search filter and another component that is search-results data=searchResults to display the data.
Let's look at the search filter first. Using actions provides you with maximum flexibility since the search filter simply emits some sort of search object on change. Your controller action would look like:
actions: {
filterChanged(searchParams){
this.set('searchParams', searchParams);
//make the search and then update `searchResults`
}
}
which means your search-filter component would aggregate all of the search filter fields into a single search object that's used as the lone parameter of the onFilterChange.
You may think now, "well, why not just do the searching from within the component?" You can, but doing so in a DRY way would mean that on load, you first pass params to the component, a default search is made on didInsertElement which emits a result in an action, which updates the searchResults value. I find this control flow to not be the most obvious. Furthermore, you'd probably need to emit an onSearchError callback, and then potentially other actions / helper options if the act of searching / what search filter params can be applied together ever becomes conditionally dependent on the page in the app.
A component that takes in a search object and emits an action every time a search filter field changes is dead simple to reason about. Since the searchParams are one-way bound, any route that uses this component in it's template can control whether a field field updates (by optionally preventing the updating of searchParams in an invalid case) or whether the search ever fires based of validation rules that may differ between routes. Plus, theres no mocking of dependencies during unit testing.
Think twice before using subroutes
For the subroutes part of your question, I've found deeply nested routes to almost always be an antipattern. By deeply, I mean beyond app->first-child->second child where the first child is a sort of menu like structure that controls the changing between the different displays at the second child level by simple {{link-to}} helpers. If I have to share state between parents and children, I create a first-child-routes-shared-state service rather than doing the modelFor or controllerFor song and dance.
You must also consider when debating using children route vs handlebars {{if}} {{else}} sections whether the back button behavior should return to the previous step or return to the route before you entered the whole section. In a Wire transfer wizard that goes from create -> review -> complete, should I really be able to press the back button from complete to review after already having made the payment?
In the searchFilter + displayData case, they're always in the same route for me. If the search values need to be persistent on URL refresh, I opt for query params.
Lastly, note well that just because /users/:id/profile implies nesting, you can also just use this.route('user-profile', { 'path' : 'users/:id/profile' }) and avoid the nesting altogether.
Should you use a separate controller like sessionController, and pass around the desired variables to other controllers using the needs: hook?
This seems like it would get very un-DRY very quickly.
A simple internal variable like #1 won't get reflected in the URL. You can save it to the database but that seems costly and silly for something that gets changed frequently as part of the app experience, as opposed to an infrequently changed user setting.
It seems the best way is to reflect the state in the URL one way or another.
Or should you have a dynamic segment as the parent url segment to all your routes?
If you go with option 2, how do you obtain and remember the subroute url segments when using transitionTo or link-to?
or should you change the query parameters in such a way that it sets it regardless of what route you may be in?
Would this cause conflicts or management hassles if you're using other query parameters, or can you cleanly append and remove individual query parameters?
Or is there another better way to handle such things?
I am very new to ember.
how do i create a component/view that has to load data, for ex if i need a search box on multiple page (with some extra properties passed in), which hits the database and gets results. every documentation i read says the model has to be passed in to the view. however i do not want to copy the code which loads search results from the backend on every page.
ideally what i want to do
{{view myseachbox... extrafilter="yyy"}}
is setting the controllerBinding to say MySearchController where the functionality of fetching data will be implemented the way to go?
any pointers are appreciated.