Angular sidebar search directive - angularjs

In my angular application I have a global sidebar navigation directive which among other things provides a global search for the user (with bunch of criteria, not just a text field).
Upon searching I'd like to show a page with the search results.
Now, the sidebar is defined in the main app html, what is the best way of sharing the search results data?
Should the sidebar be in charge of performing the search? If so how do I share it's data to the specific page results?
Or on the other hand, perhaps the specific search results page should be in charge of this data? If so how do I connect it with the sidebar search parameters when performing a search?
Any best practices of this scenario are appreciated.

Steps to make your future bright:
Separate your search module in 3 modules: main, sidebar, results
Translate data between each of them with one major SearchResultsService that will:
a) acquire collection of sidebar filters with true or false for each key (key as name for GET param that will be used for passing to search API of your back-end);
b) serialize or deserialize data depending on results module approach;
c) do some pagination;
d) hold or even cache data if you need (for infinite scroll functionality);
sidebar and results will be views of main (child modules), so you will be able to share main controller methods if needed (noob way)
When I was facing implementation of such module I've used black magic to escape $watch and $on event system. If you are young - then use $on that will allow you to notify each of modules about something important (such pagination change, item selection, etc.) and keep them separated same time.
You are free to place your existing sidebar in main module but I'd moved from directive to view with own controller and template.
Directives are used for reusable items either for pasting functionality. But dat sidebar obviously should be defined as separate part of app (aka module) but not as directive.
P.S. Keep your controllers simple.
Google list:
Multiply satisfection
Your golden chest
Root of AngularJS evil

Angular services are substitutable objects that are wired together using dependency injection (DI). You can use services to organize and share code across your app.
https://docs.angularjs.org/guide/services

Related

Need help structuring an Angular app

I have a basic angular app, users can post Adverts, now I am working on how to search / filter for adverts. I am currently using a filter like so..
<a data-ng-repeat="advert in filtered = (adverts | filter:filterBySearch) | startFrom:(currentPage-1)*entryLimit | limitTo:entryLimit" data-ng-href="#!/adverts/{{advert._id}}" class="list-group-item">
where I have created my own fiter, filterBySearch
I also have a range slider for filtering by a range of prices, and I have all this working with pagination.
My concern is because I have declared <section data-ng-controller="AdvertsController"> at the top of the homepage in order to use the AdvertsController for Advert logic, I am now finding I am putting logic in here around, pagination, the range slider, filtering logic etc. I would like to know how best to split this logic out into their own controllers or whatever it needs.
The controller should not he hadling such complex business logics in itself. It must be used for binding scope elements, handling events, updating states etc. I would suggest pulling your pagination, range slider, filtering logics etc in dedicated angular service and inject the it into your AdvertsController. This way you can also utilize them in any other view/controller you require.
Read more about modularity in AngularJS here
If you have business logic pertaining to elements inside of your tag, then it still makes sense to have all of your code inside of that controller. It might make more sense to rename your Controller to something that better suits your needs.

is it right to do using ui-router to activate menu items. Any advices to help me understand this?

What I did is:
When a menu item is clicked, a action will be done, like deleting a user, sending emails to a group, etc. To this end, for each menu item, I define a ui-router state, and use the state url to activate the state via sref. I thought that a menu action is just a UI component for user to let users to do something, which is just a state of UI.
I was advised that I was using ui-router in a wrong way as a state url can not identify an action. For example, to delete a group of users, the state url can not tell you what group of users have been deleted.
In short, I agree with your manager while being an angular newbie myself. Angular routes are designed for managing different views of your app. I.e. define a route and corresponding view template for each view. If you add application logic into the routes, your application structure gets quickly a mess and difficult to keep clear.
To me it is much more natural that the views are managed by the routes, and each action in each view is handled by the controller of that view. If the actions grow "big", then it is worth refactoring parts of the controller into separate services. If you require some sort of "dynamic HTML" depending on the action, e.g. bootstrap modals are handy for doing that within the current view (see http://angular-ui.github.io/bootstrap/).
E.g. in my current project, I don't actually manually edit the routes at all but let yeoman angular generator to do that for me free of charge - i.e. I instantiate each new view in my dev.env using the following command (more info on this from https://github.com/yeoman/generator-angular)
yo angular:route myNewView
More info on angular philosophy can be read from angular documentation for developers: https://docs.angularjs.org/guide/concepts
You should probably be doing this actions via a method on $scope.
$scope.deleteItem = function (items) {
Service.delete(items);
};
// which is the same as:
$scope.deleteItem = Service.delete;
<a ng-click="deleteItem(item)">Delete This Item</a>
Having it in the URL just seems wrong. I mean what does that look like? www.mysite.com/delete/users?

Many active states simultaneously with ui-router

What would you like to be able to do for example is:
I have an active state 'order.detail' in shell, in these details would provide a link in each product line that leads to 'product.detail' state that is also a state that can be displayed in the shell.
But this link should display the state ' product.detail' as a frame in a dialog without changing the current location and maintain active state in the shell intact.
Also the 'product.detail' state, to be used as a main view of the shell, and to allow their reuse, your template should be wrapped by 'div' template of dialogue.
What I mean is, allow consult the details of something without leaving the current screen, and do so using the same existing details screen, or simply allow the 'Drill down' by related data with existing views.
Sharing state in AngularJS
One of the great things about Angular is that's it quite easy to keep track of state via providers.
For example consider one index view containing a paged grid table with many filter options. Clicking on one of the entries will take you to details view of the entry. When the user goes back from the details to the index he/she will expect that the UI state of the grid will be exactly the way they left it: same page, same sort by, same filters applied, same everything. With traditional techniques you would have to fallback on cookies, query params and/or server side state(less) magic, which all feels (and actually is) very cumbersome and error prone.
Provider values are singletons in the world of Angular, so when we inject the instance in one of the controllers, it will always be the same instance. Controllers on the other hand will be recreated each time one is requested.
Example
Register an empty object to keep track of controllers:
myApp.value('formState', {});
Create a controller, inject the provider value and expose it on the scope:
myApp.controller('MyController', function($scope, formState) {
$scope.formState = formState;
});
Hook any property of the provider value to input elements via the ng-model directive.
<input type="text" ngModel="formState.searchFilter"/>
Now every time the user will leave and re-enters this view the state of the UI is kept intact. You can add as many data to the state as you see fit and maybe even share it among multiple controllers if needed.
Provider types
There are different ways to create provider values: factory, service, value, constant and provider. If you want more control over the state, eg state management, you could use one of the other options. More info can be found here.
To dialog or not to dialog
In traditional websites displaying the details in a dialog was a "cheap" trick to keep track of the state in the background. Of course this is still an option with Angular, but there's no need for it. From the UX view, dialogs are "not done" and should be avoided if possible, but it also introduces pains in the areas of responsiveness and printing.
Plunker examples
Some code examples sharing state among controllers/views.
http://plnkr.co/edit/MwJrk5?p=preview
http://plnkr.co/edit/bNJtOP?p=preview

Do we need multiple controllers to implement routes in angularjs?

There is chance that I might not be able to explain my problem properly. Let me try.
I am developing a single page application using angular. This app basically displays the episodes of an online novel series. There is a navigation bar, which has query menus (Like, latest episode, episode of a particular date, episodes with a particular tag, etc). For each of these queries, i want a separate url.
/latest - should display the latest episode
/tag/:tagname - should return all episodes with that tag.
For all these queries, the resultant view is the same (list of episodes). So I will be using the same partial for all routes.
My question is, Should I actually create a new controller for each query? like, LatestEpisodeController, TagController?
Is there anyway I can use the url to determine what the user wants and run that query from within the same controller?
Ofcourse you can use same controller in routing definition, the question is what is the purpose of that? It will be worse to debug it later, if you have a shared functionality it's better to turn it into a factory or service and then use in controllers.
But the answer is YES, you can use same controllers and implement different behaviour basing on i.e. $location.path()
yes you can use single controller for multiple routing..
you can create different functions in controller and in each function do the according job.
In my case I have created different html page for different url and registered same controller for the html pages and in the html page I have called controller method using ng-init in div portion.
You can use same controller and same views as you wish...
$location can help you to get current path or full url if you want and you can call your service depends on your path...
here I write a little example for you to get the idea
PLUNKER

Backbone Marionette modules as Widgets similar to Twitter Flight

I'm reading up in choosing the correct client-side framework to segment/modularize my frontend code in Widgets.
Basically what I have/want is:
a complex website with multiple pagetypes, so no single-page application.
all pages are able to render a complete page WITHOUT the use of javascript. IOW: javascript is used as enrichment only.
Lots of pages have a very dynamic way in which widgets can be shown on screen. To overcome complexity at the server-side I've modularized my code into widgets (composite pattern), where each widget is responsible for it's own:
server-side controller code
server-side templating (using hogan/mustache)
routing endpoints, should it need to be called from the client
structural css (css converning the structure of the widget as opposed to the look&feel)
a server-side RegionManager ultimately decides which widgets are rendered and where they are rendered on screen. Endresults is that the RegionManager spits out the entire html (server-generated) as the composite of the rendering of all of it's widgets.
Now, some of these widgets DO have client-side logic and need rerendering on the client. Take a searchpage for instance, which needs to be able to update through ajax. (I've described this process, which uses DRY templating on client and server, here)
What I ultimately want is that, given I already use the composite pattern on the server, to extend this to the client somehow so that a Widget (1 particular logic block on the screen) contains all mentioned server-side code, plus all needed client-side code.
I hope this makes sense.
Would Marionette be suited to be used as a client side framework in this scenario? I'm asking since I'm not 100% sure if the concept of a Marionette Module is what I describe as being a Widget in above scenario. (I'm mentioning Twitter Flight in my question, since I believe this would be a fit, but it currently is so new that I'm hesitant to go with it at the moment_
I think basically what I'm asking is if anybody has some experience doing something along these lines.
I think just using Backbone.js is perfect for this type of application you are describing. You have probably already read this, but most of the backbone literature is focused around your views having associated server generated JSON models and collections, then using the View's render function to generate (on the client) the HTML UI that represents the model/collection.
However it doesn't have to be used this way. In fact there is nothing stopping you attaching views to existing elements that contain content already, which gives you all of the benefits of Backbone's modularity, events system and so on. I often use views that have no model or collection, purely because I like the conformity of style. I have also used an approach like I describe below in the cases where I have had to work with older, existing applications that have not yet got, or never will have a nice REST API, but they do provide content in HTML.
Firstly, lets assume the following HTML represents one of your widgets:
<div id="widget">
<div class="widget-title"></div>
<div class="widget-body">
<!-- assume lots more html is in here -->
Do something!
</div>
</div>
In this case, you could use backbone with a single Widget Model. This would be a very simple model, like this:
App.WidgetModel = Backbone.Model.extend({
intialize: function () {
this.url = this.options.url;
}
});
Take note of the fact the Widget receives a URL as a parameter to its constructor/initialize function. This widget model would represent many of your widgets (and of course you could adopt this general approach with more complicated models and pluck different data from the rendered HTML). So next for your views. As you probably know, normally you pass most views a model or collection when you instantiate them. However in this case, you could create the Widget model in your View's initialize Function and pass it a URL from the pre-rendered HTML as follows:
App.WidgetView = App.View.ComboboxView = Backbone.View.extend({
initialize: function () {
this.model = new App.WidgetModel({}, { url: this.$("a").attr("href") });
}
// rest of the view code
});
So instantiating the view would be something like:
new App.WidgetView({el: $("#widget")})'
By doing all of the above you can do pretty much everything else that backbone offers you and its modular and encapsulated nicely, which is what you are after.
The end result of this whole approach is:
You have rendered the Widget UI as pure HTML which (I assume) is functional without JavaScript.
You attach a View to the existing HTML.
You pass into the View as options, content by extracted (such as a URL) from the rendered HTML with jQuery.
The View is responsible for instantiating the Model passing on the relevant options the model needs (such as a URL).
This means all dynamic server side content is intially contained in the rendered HTML and your View is a modular JavaScript component that can do stuff to it, which I think is the end result you're after.
So you mentioned that you would like to have AJAX functionality for your widgets and that fine with this approach too. Using this approach, you can now use the standard Backbone fetch and save functions on the Widget model to get new content. In this example it is from the URL retrieved from the rendered HTML. When you get the response, you can use the view's, render function, or other finer grained functions to update the HTML on the page as required.
A few points:
The only thing to look out for is that you'll need to change the content type of the fetch and save functions to "text/html" if that's what the server is providing. For example:
this.model.fetch({
type: "POST",
contentType: "text/html"
});
Lastly, the model I have proposed is instantiated with no content. However if your ajax calls are a content type of "text/html", you may need to play around with you model so it can store this content in its attributes collection properly. See this answer for more information.

Resources