Calling a view controller from index.html - angularjs

I have an index.html that has a top bar with links and a side bar with links. I want the center to be replaced with content (ng-view). Let's say the top bar is Pictures, Stats, Chat. The side bar is Tigers, Lions, Bears.
These never change. This is why index.html holds them. The top bar of Pictures, Stats, Chat I want to actually route to different views/controllers (replaces ng-view on index.html). Easy.
However, when inside a given view controller (let's say Pictures) I now want to know what side bar was clicked (I would like to default to a given side bar animal when Pictures is first clicked in the app since that would be required) and I'd also like to be able to copy the link directly to there (ie Pictures/Bears (but this always just goes to the Pictures controller but inside there I know Bears was clicked so I can act accordingly)).
So if I click Tigers, the controller will know this and I can code it to get pictures of Tigers.
I don't want to put that side bar into each page itself since it's always the same. I just want it on index.html to avoid repeating myself even though this would be more direct in terms of coding each top bar view. This would be something I'd fall back to in the case of what I'm asking to do isn't possible.
I'm also wondering if it's possible to maintain the current side bar value when they click on a different top link animal. So if I was in Pictures/Lions and they click Bears, it would know to route Pictures/Bears. Since it's staying in Pictures would it really even need to route? Can it route and then call a method on the Pictures controller passing in the animal so it can do what it needs to with that information?

Try making a service like animalService that keeps track of the selected animal. This will act like your own route handler for just that side menu.
The controller for the side menu would then be able to call animalService.selectAnimal(selectedAnimal) to update the selection.
Each main page controller could then watch the value of the selected animal from animalService. There are a few ways to do this, but it would probably be easiest to use a subscription based approach:
Each controller would call animalService.subscribeToChange('controllerName', callback) on initialization and then on destroy it would call animalService.unsubscribeFromChange('controllerName'). The animalService would keep a list/map of all of the subscribed controllers, so it can execute the callback every time the selectAnimal function is called and detects a change.
If you provide a code sample I could try to give a more detailed solution.
Alternatively
You could take advantage of $rootScope. I find this a bit more hacky, but it would be probably quicker to implement. The side menu can access a root scope variable via something like $root.currentAnimal, and it can both get and set that value. Then each controller can use $rootScope.$watch('currentAnimal', function() {}) to trigger something on change ($rootScope is a service).

Related

Cordova - Angular - <select> tag open a new view

I have an Angular/Cordova app and I'm trying to figure out how should I handle the HTML SELECT tag. What I would like to do is to open a new window with all the options in a list, the user picks one and returns with that value.
The problem is when I do that I lose all the data I had in the first screen as I am closing it when I move to the second one.
I am using Angular's UI.ROUTER. One thing, which I am not too convinced to do, is to save all data entered into StateParams, and when I return, place it back.
What would be the best approach?
It really depends on the use case. If you need to be able to "deep link" to the view where a link loads the view with the pop-up active then using ui-router and stateparams makes the most sense. If deep linking isn't a concern and the user must always select something then you can just use a service/factory/value/provider in order to share the data between the controllers during the lifetime of the app.

Call function when a tab in tabbar is first opened (onsen-ui)

I need to load a specific JavaScript function after one of the tabs in the tabbar finishes loading (after it is clicked for the first time). I don't really understand how to set up an event listener with the onsen-ui tabbar (ons-tabbar), and the docs were not very clear.
Basically, what I am trying to do, is create a few graphs on one of the pages. This page is loaded up when a ons-tab is selected and the partial loads. However, I need the JavaScript that creates the graphs to only load after the page is loaded (the JavaScript looks for the elements to position the graph in the partial, hence my problem right now is that the JS is loading first and not finding the html elements in the partial because they have't been loaded yet). How do I create an event listener to detect when one of the tabs are selected to run a specific code of JavaScript?
To be a little bit more clear, I am using the persistent attribute on this specific tab, so essentially I only need this event listener to run the JS code once, when the tab is first opened. However, I don't understand how to get the once event listener to work...
For the reference: http://onsen.io/reference/ons-tabbar.html#methods-summary
There are three tabbar methods related to events: on, once and off.
That means the way to use them is myTabbar.on(...), for example. You can see in that link their parameters as well. Also, right below there are the three tabbar events. In this case I think you want to use postchange, right?
In your HTML you create a tabbar and assign it to a variable with var attribute, as I am sure you already know:
<ons-tabbar var="myTabbar">
<ons-tab page="tab0.html" label="Tab0" active="true"></ons-tab>
<ons-tab page="tab1.html" label="Tab1"></ons-tab>
<ons-tab page="tab2.html" label="Tab2"></ons-tab>
</ons-tabbar>
Now, you have to wait until the app is initialised and Onsen UI is loaded in order to set the event listeners, and you can use ons.ready() for that.
Going back to the tabbar events method, since you only need to trigger the functionality with a specific tab you need to filter the tabs inside the listener. Therefore, if you use myTabbar.once('postchange', ...) the listener itself will be triggered only once regardless the filter you set inside, so perhaps it will be triggered with a different tab and then deleted for the tab you want. You cannot use it in this case.
Instead of that, you can set a listener with myTabbar.on('postchange', ...) that triggers always, then filter the tabs inside and remove the listener only when you want with myTabbar.off('postchange'). Something like this:
ons.ready(function() {
myTabbar.on('postchange', function(event) {
// Only with the tab we want, we can use the index or the name
if (event.index === 2) {
console.log('postchange and do stuff with the tab');
console.log(event.tabItem);
// Delete this listener so it's only triggered the first time
myTabbar.off('postchange');
}
});
Working here: http://codepen.io/frankdiox/pen/QbYEaq
In case you have other listener over postchange, you can use the second parameter of myTabbar.off(...) to remove the exact listener you want and not the others.
Hope it helps!

Where to put a function to create a pdf

I have an application which deals with projects evolving according to a process defined by a series of status transitions. I have a button to set a project to the next status. The button calls a function in the ProjectsController. This function calls another function in the Project model, where I search for the correct transition, depending on the current status, the user_group and some other parameter, and set the new status. After everything successfully accomplished, I return to the original page with 'return $this->redirect($this->referer());' from the controller.
Some of the transitions have side effects. One is to create a PDF, save it on the server and add a new entry to the 'documents' table referenced to the current project.
Problem: where should I put the function to create the PDF? I would like to put it to the Model. But I need some View Files to first render a html page and then convert it to the PDF. I could put it to the Controller. But then I have a controller function which should not be called directly an doesn't render a view.
The functions all work, but where to put them? Is there another possibility? Would it be possible to use a component?
Implement it as PdfView that will render any (pdf) view for you as Pdf. There is already a plugin that does this, search for CakePdf on Github.
A component is clearly the wrong place. A Pdf is output, so it's a kind of view. Model gets you the data, controller sets it, view renders it.

How do I get /mylink#sectionid to work in angularjs?

If I wasn't using angular, then the route mylink would be loaded, then the browser would scroll down to the sectionid section.
In Angular it doesn't scroll. I read some completely crazy whacky solutions involving injecting multiple modules and having crazy unique URLs. I refuse to do things like this.
I want my href values to remain standard. Is there any way in Angular to do this?
Keep in mind, if "mylink" was already loaded, then the links work fine, but if I'm on a different page, say "home", then I navigate to mylink#sectionid, then the scrolling won't occur.
(I mean... if Angular can't do this, I would consider that a bug. It'd be absurd to not support a regularly used syntax since the 90s that is still used today)
EDIT: I think the issue may be the amount of AJAX on this website.
It is certainly possible, you will need to inject in $anchorScroll into your controller
The example from the angular site:
function ScrollCtrl($scope, $location, $anchorScroll) {
$scope.gotoBottom = function (){
// set the location.hash to the id of
// the element you wish to scroll to.
$location.hash('bottom');
// call $anchorScroll()
$anchorScroll();
};
}
From anther route you could handle this via parameter being passed into the route and scroll upon initialization based upon the route param.
I'm not a big fan of my solution, but I listen to onRouteChange, then inject anchorScroll and simply call anchorScroll after a 1000 ms timeout and because the hash is already set nothing more needs to be done. [giving time for all angular stuff to work its self out (the site I'm working on has entirely too much AJAX, but I don't have control of the data yet, so there is nothing I can do about that)]
Anywho, manually initiating anchor scroll works. If anyone knows a better way to do this, that'd be swell.

AngularJS jquery.flot chart directive DOM collision

i'm working with angular js now for about ke 4 months and despite all the "first step failures" like not emphasising the async way anuglar thinks, I'm facing a problem I don't really understand. It's not that easy to describe.
I have a provider which registrates directives within the routeprovider's resolve function - during config phase. To compile programmatically preconfigured directives I create them on the specific controller call of each route. Acutally the directive I'm adressing here is a complex flotchart directive. It retrieves data from a rest api, transformes the retrieved data and prepares different kinds of option setups like proper stacked line charts or simple piecharts. Every single step takes its time, so I introduced promises to be sure that everthing is at it's right place before I finally call something like "$.plot".
So now I have the following situation: Imagine a singlepage app with two tabs. Each tab - like a first class menu item - refers to a new page with a new controller to process and new partials to render. For each page i have beside other directives one of these heavy flotchart directives to render. actually it takes about 5 seconds to render the chart. So we assume that we really start the app from beginning - like pressing F5. Now I enter the first page the first time and within the mentioned 5 seconds I switch tabs to enter the next page. I get to the next page, see different partials, layouts and stuff and a loading chart - but actually the directive of the first page is still bound to its link phase of that heavy flotchart directive (still preparing options for flot and calculating data to output graphically).
My problem is that this link phase actually really ends within a completely different template/route/controller context and gets stuck. It crashes with a console "replace" error from jquery.flot. I think this error means that flot tries to plot into a div which does not exist anymore. But that error occures just when I switch tabs during the link phase of the first page's heavy flot directive. It doesn't happen when the first page's chart is fully rendered and doesn't happen when the first page's directive hasn't entered its link phase (or am i missing something??). I tried placing some console.logs directly BEFORE hitting the jquery "$.plot" - remember only of the first page chart directive to dive into what's acutally happening. And the strange thing is when I manage to switch tabs within these magic 5 seconds, I still get the console log entries from the first page entry although I'm on a different page. And now guess what. That's strange - acutally two directive link phases are running side by side and one of them on a completely different view (or isn't it completely different, because its a singel page app?). Imagine I plot ("render") the chart in exactly the same div id - like $('#flot-chart'). so I have html parts containing id="flot-chart" on the first page AND on the second. when I now switch from the first to the second page (not finishing the first chart) I get the chart from the first page rendered in the #flot-chart div of the second page and like half a second later the actually correct chart rendered in that same div. So actually the link phase of the first page's chart directive ends in a completely different page in a way showing 2 charts consecutively. I know jquery.flot depends on DOM manipulation via jquery and that might be the problem (perhaps THAT'S the only real explanation for my problem), because jquery DOM manipulation is out from the angular way of life.
Or are there other explanations? I acutally solved the problem via $routeChangeStart listening and killing the $.plot process, but are there some hints, suggestions, explanations for that behaviour?
Plunker flot chart directive DOM collision
I have prepared a plunker which shows kind of a similar behaviour. i've delayed the creation of the directive and the directive's async data and option retrieval methods to somehow mock the behaviour of my app. this is non production code but describes simplified the way my problems occure. when you "fast click" the menu item one after the other many times, you can sometimes force angular to show 2 charts in one page. acutally one directive is linking and doing stuff in a different partial ? i know i'm missing something in my mind ... please give me a hint.
i used chrome for reproducing the error. stop the the plunker and press 'run'. directly after pressing 'run' click as fast as you can both links a couple of times.
Thanks a lot!

Resources