outside controller communication - angularjs

How can a html element outside of a controller communicate with a given controller ?
The situation is as following:
<button name="search">Search</button> --> in an existing layout provided by an existing framework
<div ng-app ng-controller="overviewCtrl">
<div ng-view>/div> --> this one gets a specific controller
</div>
<button name="search">Search</button> --> in an existing layout provided by an existing framework
I don't have control on the location of the button outside of the controller.
I can put attributes on it and I want to put an ng-click attribute.
It also fall outside the ng-app.
I could put a controller on it. But then I need a way to have a reference to the same controller.
What is the best way to do this ?

One way to do this is to use JQuery inside the Angular controller to bind the element outside the ng-app to a scope function through JQuery selector and click event.

You don't have control over its position, but you do have control over its rendering in some way? You say you could put an ng-click on it?
I ask because there's an easy dodge here if that's true. You could make a simple directive that's basically a "smart ng-click". Your issue with ng-click is really that it's hard to route its call into your controller because the button is elsewhere. What you need, then, is just a way to bridge the two.
It would look something like this:
angular.module('myApp', [])
.controller('MyController', ['$scope', '$rootScope', function($scope, $rootScope) {
$rootScope.$on('SearchButtonClicked', function() {
// I will be called whenever the search button is clicked...
// and I am in my controller!
});
}])
.directive('notifySearchClick', ['$rootScope', function($rootScope) {
return {
restrict: 'A',
link: function($scope, iElement) {
iElement.bind('click', function() {
$rootScope.$emit('SearchButtonClicked');
});
}
};
}]);
Working Plunkr demonstrating this concept here:
http://plnkr.co/edit/dKuDl5?p=preview
The jQuery way is also perfectly valid but then you're back to the "what mystery event handlers are on THIS element" situation. It works, and it's reliable... but if you want to keep to the AngularJS philosophy, using a directive like the one above gives you a lot of flexibility.
Note that in the Plunkr reference above I added a tiny bit of sophistication. In that example I made the message name an attribute. You could thus use a single directive for any other cases like this in the future. Just put the message name into the attribute, like this:
<button notify-click="MyButtonNameClick"></button>

Related

Preload dataset to the controller AngularJS

Hello Angular experts,
I have to preload a certain dataset (factory call to the database) to the controller. I don't use angular views so stateProvider or routeProvider cannot be used to resolve. Basically I need the dataset readily available before loading the controller.
Is there a way to achieve this?
I have a controller and a view. The view also has a widget. The widget has an attribute that expects a dataset. By the time the controller is done fetching data the view is already rendered so the widget input parameters are empty. So I need the widget dataset to be filled much before getting to the controller. By the way the app.run solution doesn't work as there is a promise involved.
You can't say as before loading controller, i correct it with before binding controller
angularjs has app.run and i think you know it, it work just when application run (first time) and every time you refresh it.
var app = angular.module("app", []);
app.run(function($http, $rootScope) {
$http.get("url").then(function(response){
console.log(response.data)
$rootScope.data = response.data; // as global scope
})
})
app.controller('ctrl', function($scope) {
$scope.$watch('data', function(newValue, oldValue) {
if(newValue){
console.log(newValue) // you will get `rootscope.data` when it's ready
}
})
})
You can add factory to the app run too.
please fill free to ask question.
Based on your comment, what you are trying to do shouldn't require any changes to the Controller.
What you are actually trying to do is delay rendering of the widget component until you have data that the widget needs. This is commonly handled by ng-if in your HTML.
For Example (pseudocode):
<div ng-if="myData">
<Widget input="myData"></Widget>
</div>
Angular will not render this div until myData has a value, and all your Controller has to do is make sure myData is added to $scope.

Angular. Execute a angular function from a popup window

I have an angluar module.
In that module I have a controller with a function.
I need to execute that function in popup on href click.
As the popup content is not initially part of DOM or angular context, how can make a angular function to be executed by that generated code?
Here is what I try to code: codepen
app.controller('AppCtrl', ['$scope', 'myPopup', function($scope, myPopup) {
...
var infoWindow = new google.maps.InfoWindow({
content: "<a href='#' onclick='myPopup.show();'>this is my link</a>"
});
To retain is that I don't try to make execute exactly this code, I just search a way to execute an angular service function from a generated "on-fly" html code (a dynamic popup content, in my case).
First of all here's the working codepen: http://codepen.io/anon/pen/vKRWYy?editors=1010
So, the solution(just A solution. Not THE solution) is to add modal open function on window or global scope. And invoke that function on clicking the map.
window.showPopup = function(){
myPopup.show();
};
<a href='#' onclick='showPopup();return false;'>this is my link</a>
On a side note, it's generally not good practice to use non angular code with angular. It might be worth looking at an angular google maps implementation like: http://angular-ui.github.io/angular-google-maps/#!/. Maybe?

AngularJS load tabbed content directive dynamicly

I have a tabbed navigtion in my webapp that looks like this
Now I want to Change the directive each time the user clicks on one of the Navigation points. My Idea was to init the page with the first template.
$scope.currentDirective = $compile('<div order-Sale></div>');
Then when the user clicks on a tab, I wanted to change and compile the content again with a new directive in it. But for some reason this is not working. How would you proceed in order to archive this dynamic content loading? I really want to only load the content on necessary need and not just to show or hide it. I think using directives is the right way to go for it, but I'm a but stuck at the implementation... Someone any pointer ? (I don't want to use any jQuery)
What I tried [Edit]:
The controller.js
app.controller('pageController',['$scope','$compile', function($scope, $compile){
var templates = ['<div first-template></div>','<div second-template></div>'];
$scope.currentTemplate = $compile(templates[0]);
$scope.changeTemplate = function(id) {
$scope.currentTemplate = $compile(templates[id]);
};
}]);
The HTML
<div ng-controller="pageController">
<li>
<a ng-click="changeTemplate('1')">Change Template</a>
</li>
{{currentTemplate}}
</div>
UPDATE
$compile returns a linking function not a value, you cannot just bind it to your template with interpolation.
You should use ngBindHtml instead of regular bindings ( ngBind & {{ }} ).
ngBindHtml does compiling, linking and watching all out-of-the-box.
With ng-bind-html-unsafe removed, how do I inject HTML?
Here is a plunker
app.controller('pageController',['$scope','$compile','$sce', function($scope, $compile, $sce){
var templates = ['<div>first-template</div>','<div>second-template</div>'];
$scope.currentTemplate = $sce.trustAsHtml(templates[0]);
$scope.changeTemplate = function(id) {
$scope.currentTemplate = $sce.trustAsHtml(templates[id]);
};
}]);
The markup:
<div ng-controller="pageController">
<button ng-click="changeTemplate('1')">Change Template</button>
<div ng-bind-html="currentTemplate"></div>
</div>
For more robust dynamic content loading you have two good alternatives:
ngRoute from angular team.
ui-router from angular-ui team.
If you want to change and compile the content again, well that's exactly what ng-view/ ui-view directives already do for you.
Why not just use a directive:
You probably need to load a different template (html partial) for each tab.
You probably need to change the url based on the tab (and vice versa)
You probably need to instantiate a different controller for each tab.
ngRoute and ui-router come with their own directives.
You can implement your own route module if you want but that's more than just a directive.

Binding to events in a directive which is inside a ng-view doesn't work

I have an AngularJS application, which hasn't been using the built in routing before now. I am refactoring the site, so it will be a SPA.
To do this I have change the app to use ng-view to switch between the different pages, instead of just having the server serve the different controllers
After this is done my endless scroll suddenly stopped working.
I have a directive which looks like this:
directiveModule.directive('whenScrolled', ['$window', function($window) {
return function(scope, element, attr) {
var raw = element[0];
angular.element($window).bind('scroll', function() {
console.log('test');
scope.$apply(attr.whenScrolled);
});
};
}]);
But now the scroll event is never fired.
If I take angular.element($window).bind('scroll', function() {..}); out and uses it in the controller instead it works fine, but it just seems like a hack.
Is there any way to bind to the page scroll event inside a directive, which is inside a controller, which is inside a ng-view?
Are you sure the element with the directive which is inside the controller and then the ng-view is added to the DOM?
More code (A Fiddle) would help.

How to include one partial into other without creating a new scope?

I've this routes.
// index.html
<div ng-controller="mainCtrl">
<a href='#/one'>One</a>
<a href='#/two'>Two</a>
</div>​​​​​​​​​
<div ng-view></div>
And this is how I'm loading the partials into my ng-view.
// app.js
​var App = angular.module('app', []);​​​​​​​
App.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/one', {template: 'partials/one.html', controller: App.oneCtrl});
$routeProvider.when('/two', {template: 'partials/two.html', controller: App.twoCtrl});
}]);
When I click the links, it shows me the appropriate markup inside the ng-view. But when I try to include partials/two.html inside partials/one.html using ng-include, it shows it properly but creates a different scope so I'm not able to interact with it.
// partials/two.html - markup
<div ng-controller="twoCtrl">I'm a heading of Two</div>
// partials/one.html - markup
<div ng-controller="oneCtrl">I'm a heading of One</div>
<div ng-include src="'partials/two.html'"></div>
​
How do I resolve this problem? Or Is there any other way to achieve the same result?
You can write your own include directive that does not create a new scope. For example:
MyDirectives.directive('staticInclude', function($http, $templateCache, $compile) {
return function(scope, element, attrs) {
var templatePath = attrs.staticInclude;
$http.get(templatePath, { cache: $templateCache }).success(function(response) {
var contents = element.html(response).contents();
$compile(contents)(scope);
});
};
});
You can use this like:
<div static-include="my/file.html"></div>
The documentation for ngInclude states "This directive creates new scope." so this is by design.
Depending on the type of interaction you are looking for you may want to take a look at this post for one way to share data/functionality between the two controllers via a custom service.
So this isn't an answer to this question but i made it here looking for something similar and hopefully this will help others.
This directive will include a partial without creating a new scope. For an example you can create a form in the partial and control that form from the parent controller.
Here is a link to the Repo that i created for it.
good luck :-)
-James Harrington
You can actually do this without using a shared service. $scope.$emit(...) can dispatch events to the $rootScope, which can listen for them and rebroadcast to the child scopes.
Demo: http://jsfiddle.net/VxafF/
Reference:
http://www.youtube.com/watch?v=1OALSkJGsRw (see the first comment)

Resources