I have a web application with require.js, backbone.js and jquery.
The brief structure of the app is as follows:
There are 2 sections on the screen (toolbar and main content below).
There are multiple components (address management, event management), each triggered by a hash fragment change and require a
page transition.
There is one backbone.js router. It's the heart of the application. The router is activated with a new hash fragment (manually entered,
back button, menu item selection).
Up until now, in the router, I made the page transition, I DIRECTLY called the controller ("view" in backbone) of the selected component.
So there's a CENTRAL handling of controller calling.
But this has to change now to a DISTRIBUTED handling. I now need to respond to a new hash fragment from two different places: From the toolbar component and from router.
So my idea is to exchange the direct controller calling mechanism with pub sub. Now MULTIPLE components could register for a special action and the router just "fires the event".
I searched around and found Chaplin (https://github.com/moviepilot/chaplin), a backbone.js example application.
The developers of Chaplin seem to have a similiar thing called "ApplicationView" (https://github.com/moviepilot/chaplin#toc-router-and-route):
"Between the router and the controllers, there’s the ApplicationView as
a dispatcher."
Is there anyone who has already this kind of architecture and can tell me his experience with this or has anybody solved this in another way?
I've used a similar, though maybe less complex, architecture in this project. I give a pretty good explanation in this answer to a related question. The quick overview:
I manage app-wide events that change the application state using a singleton State model that works similarly to Chaplin's pub/sub architecture. This is just a basic Backbone model, with some added methods to deal with serializing and deserializing attributes as strings, so they can be set in the URL.
Application components that change the application state in response to user interaction or other input do so by setting properties on app.state.
Components that need to update when the application state changes do so by binding to change events on app.state (it looks like this is exactly the way Chaplin's mediator works, though they renamed the methods to fit the pub/sub paradigm).
I treat my routers like specialized views that update the address bar and respond to user input in that area. Changing the address (manually or by clicking on a link) causes the router to set() the necessary changes on the app.state model, and everything else updates accordingly.
I hope that's helpful - I think it's a little simpler than the Chaplin approach.
Related
Well, I have never used and never felt like that I should use the UI router. I was asked in one of the interviews about this and so felt like reading if I am missing something out as an AngularJs developer.
Now, the explanations on internet displays it's strength based on the modularity and reusability of the components. Nesting of views etc.
If I want to reuse components in my view, then can't I use directive instead of a new state? According to this article by scotch.io(top google result) for ui router we can use separate data /controllers in my view. Well, can't I do the same via directive's controller and template. I can still reuse as many times as I want it.
Please let me know if I am missing some cool feature and makes it quintessential to use it in an AngularJs application (yeah a larger one with lots of reusable components of course) .
The whole point of the router is that it uses the URL to change states. If you just used directives, you would have to write your own mechanism for syncing up URLs with specific directives.
AngularJS is a framework for Single Page Application.
Single Page Application (SPA)
Single Page Application is a web application that loads single HTML page and dynamically updates a fragment in the page as the user interacts with the app.
John Papa's blog explains SPA in simple terms.
The biggest advantage of SPA that I see is
once the application is loaded, the state is maintained without
requiring server roundtrip when user navigates.
Users can bookmark deep link into your application. SPA framework (AngularJS) will take care of loading the required state when user open bookmark.
Although it is technically possible to achieve the above in a non-SPA application, it was never as simple as SPA.
SPA is useful for highly complex applications with many pages. For simple applications with 2-3 pages jQuery is the way to go.
Read Single Page Application: advantages and disadvantages for more discussions
You probably know all these and I think you are trying to achieve SPA using directive.
Routing
Routing framework loads a view dynamically based on user action into the main page without refreshing the whole application; providing SPA effect.
There are two popular AngularJS routing frameworks available.
ngRoute
UI-Router
ngRoute is based on URL mapping and UI-Router is based on state name mapping. I prefer UI-Router.
Routing vs Directive
Now, the explanations on internet displays it's strength based on the
modularity and reusability of the components. Nesting of views etc.
Yes directive is used for modularity and reusability and can load views dynamically but cannot choose a view dynamically based on user action. You have to write complex conditions within directive to choose a view dynamically.
For example, if you have an application with 3 links and you need to show a view based on the link user clicked.
Using directive you need to keep track of what the user clicked and write a mucky condition to choose a view to display. Most of the time you will fail to achieve the effect because the link can be accessed in multiple ways.
On the other hand, once routing is configured, the corresponding template will be dynamically loaded when user clicks the link. It is way easier to change the view based on user action.
Another advantage. When user opens a bookmark deep linked into the application, routing framework will take care of loading the sate (It is impossible to achieve this using directive). It feels more natural way of designing an application.
Choice is yours.
I'm working on an isolated scope custom directive that has a few different states.
Does it make sense to use ui-router/ui-view inside this directive in order to handle the states?
It's a "note widget" that lists notes. If there are no notes, it displays a message instead of the list that says they should add a note. If notes are being loaded, it shows that notes are being loaded. If a user adds a note by clicking the add I mentioned above or the + then the view is a textbox. So there is at least 4 different views.
My initial instinct is that it would be polluting the directive and giving it a hard dependency to ui-router and my application because it defines the states. Am I just over worried?
I would tell it this way: yes, use the ui-router, but not for a directive - use it for your appliation. In fact, the best you can do is to read few blog posts and go through sample application to understand the principles. You'll soon realize, that there is no need to use the ui-router partially..
from The basics of using ui-router with AngularJS (by Joel Hooks)
...ui-router fully embraces the state-machine nature of a routing system. It allows you to define states, and transition your application to those states. The real win is that it allows you to decouple nested states, and do some very complicated layouts in an elegant way.
You need to think about your routing a bit differently, but once you get your head around the state-based approach, I think you will like it...
and here AngularJS State Management with ui-router (by Ben Schwartz)
...The most interesting thing about AngularJS's new router, isn't the router itself, but the state manager that comes with it. Instead of targeting a controller/view to render for a given url, you target a state. States are managed in a heirarchy providing inheritance of parent states and complex composition of page components, all the while remaining declarative in nature...
Here I put together all the links, up to date, targeting the sample example, the most interesting code snippet sample.js..
Summary, try to implement the ui-router on the application level. Directive could then by a conductor only, helping your users to navigate, to walk through among states...
I've started to learn AngularJS but I need some application design hints. Of course I'm not asking about the layout but ... how to design my application and it's controllers in a proper way. I have left sidebar with a menu that is loaded from the web using JSON. That needs a controller. That's fine. It works for me. There's a content box as well in a center of my page that loads some data dynamically. In my opinion it requires another controller.
And now comes my solution, that somehow doesn't look good IMHO. When I click a menu item in my sidebar I'm loading a content. Then I'm passing this data into a Service which emits an Event afterwards to the Second controller (which is responsible for controlling my content in a center of my page). When it receives this event it simply gets previously loaded data from the Service and displays it. It generally works.... but ... I'm pretty sure that's not the proper way of doing this.
I would be grateful for any hints. AngularJS has a really poor documentation and tutorial :(
cheers
EDIT:
OK. That's my basic application using JQuery:
http://greatanubis-motoscore.rhcloud.com/index
And that's the same application I'm converting into AngularJS:
http://greatanubis-motoscore.rhcloud.com/angular/index
No worries, some text is in Polish but... I think it really doesn't matter ;)
Note for the AngularJS version: At the moment the content is a HTML but finally it will load JSON data as the other controllers.
I would go about doing this with angular ui-router. With ui-router you can achieve this in a couple of ways. You can use nested routing to have a base state (Your sidebar menu, header etc.) which will act as your shell page, this can have its own controller as well. You could then define each of those other views as child states of the base state. These child states can also have their own controller/views as well, but they will be sitting inside the base state (both visually, and also inherit $scope properties of the base state) optionally they can have separated URLs themselves, but they don't have to, you can just change states without changing the url, by leaving the URL bit empty when you define different states in your $stateProvider configs. Another way would be to use the multiple named views feature.
I have a nontrivial Angular SPA that uses ui-router to manage multiple views, many of which are visible at the same time. I need models to be visible across controllers, so I have services written that allow me to have controllers pull down fresh copies of model data that has been updated.
I apologize in advance for the length of the question, but I will state the problem then state what I have done to address issues I'm sure others in the Angular community have struggled with.
I believe my problem is not understanding the lifecycle of controllers / views, because I get behavior where a controller initializes correctly the first time I go there, but then seems to never run again, even when I navigate to it using something like $state.go("state name").
In one view (contrived example), I show a summary of information about a customer, and in another view I allow a user to update that customer's more detailed profile. I want a user to edit, say, the customer last name in the detailed view, and have the summary view automatically recognize the change and display it.
I have a fiddle that shows 3 views and a simple password changing Service. The flow goes like this:
You can see each view gets initialized and displays the initial password retrieved from the service. All views are in sync with the DataService.
The middle view allows you to enter a new password and change the one stored in the service. Console logging confirms that the service picks up the new password just like you would expect.
(odd behavior #1) When the DataService receives the new password, I would expect the other 2 views (top and bottom) to display the new one. They don't... they still display the initial password.
There is a button to allow a user to go to another state via $state.go("state name") (a child state of the original) which also retrieves the password and displays it. This works the first time (see #5). Now the top view shows the outdated password, the middle view shows the new one, and the bottom one shows the new one as well. This seems normal, since the new view is invoked after the DataService contains a new password value.
(odd behavior #2) If I click back in the middle view and change the password again, and click the button to change states again, the bottom view (which updated just fine in step #4) no longer updates its copy of the password. Now all 3 views show different passwords, even though I am using a single service to pass values between controllers as suggested pretty much everywhere you look for Angular best practices.
Some possible solutions:
Tim Kindberg has an excellent slideshow here that seems to recommend using ui-router's state heirarchy to "pass" data among views that need to pick up values from other views. For a smaller-scale app I think I would settle on this, but I expect our application to have 30+ views displaying data from over 100 REST endpoints. I don't feel comfortable maintaining an application where all the data is being shared by a complex inheiritance tree. Especially using a routing framework that is at version 0.2.8.
I can use events to have controllers listen for changes in the data model. This actually works well. To accommodate performance concerns, I am using $rootScope.emit() and a $scope.$onRootScope('event name') decorator I found on here. With this approach I am less concerned about event performance than I am about wiring this huge app with a bunch of event listeners tying everything together. There is a question about the wisdom of wiring a large app using angular events here.
Using $watch on the value in the DataService? I have not tried this but I am hesitant to hinge an app this size on hundreds of $watches for performances reasons.
A third-party library like bacon.js (or any of a dozen others) that may simplify the event spaghetti, or create observable models that my controllers can watch without the risk of $digestageddon. Of course, if there is a concise way to handle my issue using Angular, I'd prefer not to muddy the app with 3rd party dependencies.
Something that lets controllers actually reference .service modules by reference, so I don't have to depend on tons of event wiring, complex state hierarchies, 3rd party libraries, or seeding the app with hundreds of $watches and then kicking off $digests to update controllers' references to Angular services?
Some solution that relies on time-tested OO and design patterns and not a 3rd-party library or framework that has a version that starts with 0.*.
Thanks in advance... I appreciate the help!
This is no problem of ui.router. If you intend for your model (your data service) to be a single source of truth, you have to refrain from destroying it.. err.. the reference to it that is. And in your case, assigning a primitve (a string) directly to the scope, instead of a reference to it. In other words...
var password = {pw:'initial value'};
and then later setting/binding only on
password.pw = newpassword
{{password.pw}}
Heres a fiddle. And also here is a short little read on scopes, It also includes a video of an angular meetup where Misko talks about "always have(ing) a dot in your model" link and how the $scope is a place to expose your model, not be your model. (aka not a place to assign primitives like password = 'initial value')
Hope this helps!
try remove the animation property of your ion nav view.
remove the property
animation="slide-left-right"
it would be ok.
I'm diving into the whole "single page application" and Backbone.js (specifically Marionette) stuff. I'm working on a decently complicated application. I'm wondering how you set up the router to handle nested views so the "containing views" are also rendered. For instance let's say I have an Admin section and under that I have a Users section. Under Users I have tabs to "Add User" and "Search Users".
If I've selected "Add User", I imagine my URL has the fragment "#admin/users/add". That routes to a view that has the add user form. However, if you go directly to that URL I want to show that form again, but also the top navigation bar with "Admin" highlighted, the admin specific sidebar with my admin navigation and have "Users" button highlighted. I need the whole HTML page, not just the Add User ItemView.
How to do say when the page first loads (refresh or from a bookmark), to load the html structure and "parent views" as well? Thanks!
This is the way you need to think about the app's behavior:
the controller needs to create view instances and pass in the data they need (models, collections, etc.), and then display the views within the regions
the ONLY thing the router does is is match a URL to a controller action (i.e. "if this URL is entered in the address bar, the application should launch this controller action")
So bascially, this is your problem: you're missing a controller action (e.g. MyApp.AdminApp.Users.New.newUser() which will render the views you want, which you can then call from your router...)
One thing that helps (although not related to the problem you're currently facing), is to always call the navigate method with trigger: false (which is the default). This ensures that your app is behaving properly and that the router is limited to matching URLs to controller actions.
Regarding the menu (with highlighted current entry), I would make it a separate Marionette module (https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.application.module.md), and have a collection of models (that don't get saved on the server) to list your menu entries. That way, you can manage the current entry by setting its model activeattribute to true (and checking that attribute in the view to highlight the current entry).
This is probably a lot to take in at first, but after a few more hours of working with Marionette it will all make sense...
(Shameless plug: I'm writing a book on Marionette that takes you from beginner to fully independent with Marionette. In there, I'll be covering this type of functionality, especially the menu management and how to highlight the current option. If you'd like to check it out, there's a free 55-page sample at http://samples.leanpub.com/marionette-gentle-introduction-sample.pdf and the book (which is still being written) is at https://leanpub.com/marionette-gentle-introduction)
I had the same question a time ago, what I strongly recommend is to get involved with Marionette layouts, collections, composite and collection views, regions and how to display content within templates.
Is not hard as you keep reading tutorials, I recommend reading lostechis.com which is a very educational blog from the creator of Marionette, Derick Bailey, also the Marionette Official website.
This is just about educating yourself doing tests and when some question comes to your mind search it and if not found dont doubt to ask it right here.
For the side bar and some other stuff you can just use JQuery-ui or Twitter bootstrap, it is very easy to integrate them with backbone/marionette views, but you just have to read to achieve that.
Which you luck.