How to can angularjs controller access functions in a non-angularjs iframe? - javascript

I'm trying to have an angularjs controller access javascript functions in a non-angularjs iframe. I don't know where to start. Looking for guidance.

normally to communicate with an iFrame is to use window.postMessage, which enables cross-origin communcation, and on the receiving end, in your case, the window which has your angular code, you can use angular.element($window).on("message", function() to do whatever you want.
angular.module('windowExample', [])
.controller('ExampleController', ['$scope', '$window', function($scope, $window) {
angular.element($window).on("message", function(e){
alert(e.data);
});
}]);
A sample js fiddle is the following. Obviously I cannot really use an iFrame, so i used a button to fake the message
http://jsfiddle.net/atozaxsv/22/
Let me know

Related

Change URL hash while scrolling

I've seen some jQuery plugins for this, but I was wondering if anybody did a service or a directive that does this. Google isn't helping.
Basically, what I'd like is for the URL to update every time I go over an element with (say, a directive and) an id so that if I go to another part of the application and click back I'm returned to the header that I was reading.
Use $anchorScroll and $location
These too services should give you the desired result.
The $location service allows you to change and get the # in the url
Where as the $anchorScroll lets you pin point places in the DOC.
here is a plunker demo
And here is the main controller
angular.module('anchorScrollExample', [])
.controller('ScrollController', ['$scope', '$location', '$anchorScroll',
function ($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();
};
}]);
})(window.angular);
Best of luck

Angularjs basic parameter access (beginner) [duplicate]

I'm using angular-translate for i18n in an AngularJS application.
For every application view, there is a dedicated controller. In the controllers below, I set the value to be shown as the page title.
Code
HTML
<h1>{{ pageTitle }}</h1>
JavaScript
.controller('FirstPageCtrl', ['$scope', '$filter', function ($scope, $filter) {
$scope.pageTitle = $filter('translate')('HELLO_WORLD');
}])
.controller('SecondPageCtrl', ['$scope', '$filter', function ($scope, $filter) {
$scope.pageTitle = 'Second page title';
}])
I'm loading the translation files using the angular-translate-loader-url extension.
Problem
On the initial page load, the translation key is shown instead of the translation for that key. The translation is Hello, World!, but I'm seeing HELLO_WORLD.
The second time I go to the page, all is well and the translated version is shown.
I assume the issue has to do with the fact that maybe the translation file is not yet loaded when the controller is assigning the value to $scope.pageTitle.
Remark
When using <h1>{{ pageTitle | translate }}</h1> and $scope.pageTitle = 'HELLO_WORLD';, the translation works perfect from the first time. The problem with this is that I don't always want to use translations (eg. for the second controller I just want to pass a raw string).
Question
Is this a known issue / limitation? How can this be solved?
Recommended: don't translate in the controller, translate in your view
I'd recommend to keep your controller free from translation logic and translate your strings directly inside your view like this:
<h1>{{ 'TITLE.HELLO_WORLD' | translate }}</h1>
Using the provided service
Angular Translate provides the $translate service which you can use in your Controllers.
An example usage of the $translate service can be:
.controller('TranslateMe', ['$scope', '$translate', function ($scope, $translate) {
$translate('PAGE.TITLE')
.then(function (translatedValue) {
$scope.pageTitle = translatedValue;
});
});
The translate service also has a method for directly translating strings without the need to handle a promise, using $translate.instant():
.controller('TranslateMe', ['$scope', '$translate', function ($scope, $translate) {
$scope.pageTitle = $translate.instant('TITLE.DASHBOARD'); // Assuming TITLE.DASHBOARD is defined
});
The downside with using $translate.instant() could be that the language file isn't loaded yet if you are loading it async.
Using the provided filter
This is my preferred way since I don't have to handle promises this way. The output of the filter can be directly set to a scope variable.
.controller('TranslateMe', ['$scope', '$filter', function ($scope, $filter) {
var $translate = $filter('translate');
$scope.pageTitle = $translate('TITLE.DASHBOARD'); // Assuming TITLE.DASHBOARD is defined
});
Using the provided directive
Since #PascalPrecht is the creator of this awesome library, I'd recommend going with his advise (see his answer below) and use the provided directive which seems to handle translations very intelligent.
The directive takes care of asynchronous execution and is also clever enough to unwatch translation ids on the scope if the translation has no dynamic values.
Actually, you should use the translate directive for such stuff instead.
<h1 translate="{{pageTitle}}"></h1>
The directive takes care of asynchronous execution and is also clever enough to unwatch translation ids on the scope if the translation has no dynamic values.
However, if there's no way around and you really have to use $translate service in the controller, you should wrap the call in a $translateChangeSuccess event using $rootScope in combination with $translate.instant() like this:
.controller('foo', function ($rootScope, $scope, $translate) {
$rootScope.$on('$translateChangeSuccess', function () {
$scope.pageTitle = $translate.instant('PAGE.TITLE');
});
})
So why $rootScope and not $scope? The reason for that is, that in angular-translate's events are $emited on $rootScope rather than $broadcasted on $scope because we don't need to broadcast through the entire scope hierarchy.
Why $translate.instant() and not just async $translate()? When $translateChangeSuccess event is fired, it is sure that the needed translation data is there and no asynchronous execution is happening (for example asynchronous loader execution), therefore we can just use $translate.instant() which is synchronous and just assumes that translations are available.
Since version 2.8.0 there is also $translate.onReady(), which returns a promise that is resolved as soon as translations are ready. See the changelog.
EDIT: Please see the answer from PascalPrecht (the author of angular-translate) for a better solution.
The asynchronous nature of the loading causes the problem. You see, with {{ pageTitle | translate }}, Angular will watch the expression; when the localization data is loaded, the value of the expression changes and the screen is updated.
So, you can do that yourself:
.controller('FirstPageCtrl', ['$scope', '$filter', function ($scope, $filter) {
$scope.$watch(
function() { return $filter('translate')('HELLO_WORLD'); },
function(newval) { $scope.pageTitle = newval; }
);
});
However, this will run the watched expression on every digest cycle. This is suboptimal and may or may not cause a visible performance degradation. Anyway it is what Angular does, so it cant be that bad...
To make a translation in the controller you could use $translate service:
$translate(['COMMON.SI', 'COMMON.NO']).then(function (translations) {
vm.si = translations['COMMON.SI'];
vm.no = translations['COMMON.NO'];
});
That statement only does the translation on controller activation but it doesn't detect the runtime change in language. In order to achieve that behavior, you could listen the $rootScope event: $translateChangeSuccess and do the same translation there:
$rootScope.$on('$translateChangeSuccess', function () {
$translate(['COMMON.SI', 'COMMON.NO']).then(function (translations) {
vm.si = translations['COMMON.SI'];
vm.no = translations['COMMON.NO'];
});
});
Of course, you could encapsulate the $translateservice in a method and call it in the controller and in the $translateChangeSucesslistener.
What is happening is that Angular-translate is watching the expression with an event-based system, and just as in any other case of binding or two-way binding, an event is fired when the data is retrieved, and the value changed, which obviously doesn't work for translation. Translation data, unlike other dynamic data on the page, must, of course, show up immediately to the user. It can't pop in after the page loads.
Even if you can successfully debug this issue, the bigger problem is that the development work involved is huge. A developer has to manually extract every string on the site, put it in a .json file, manually reference it by string code (ie 'pageTitle' in this case). Most commercial sites have thousands of strings for which this needs to happen. And that is just the beginning. You now need a system of keeping the translations in synch when the underlying text changes in some of them, a system for sending the translation files out to the various translators, of reintegrating them into the build, of redeploying the site so the translators can see their changes in context, and on and on.
Also, as this is a 'binding', event-based system, an event is being fired for every single string on the page, which not only is a slower way to transform the page but can slow down all the actions on the page, if you start adding large numbers of events to it.
Anyway, using a post-processing translation platform makes more sense to me. Using GlobalizeIt for example, a translator can just go to a page on the site and start editing the text directly on the page for their language, and that's it: https://www.globalizeit.com/HowItWorks. No programming needed (though it can be programmatically extensible), it integrates easily with Angular: https://www.globalizeit.com/Translate/Angular, the transformation of the page happens in one go, and it always displays the translated text with the initial render of the page.
Full disclosure: I'm a co-founder :)

Calling controller function from jquery

I have this controller attached to a div:
.controller('ShowInverterConnectController', ['$scope', '$location', function($scope, $location) {
....
}])
I would like to use the $location argument of it.
I am currently doing this:
angular.element('.ng-scope').scope().$apply(function() {
console.log('test:', angular.element('.ng-scope').scope().$location);
});
But $location is coming out undefined, is there anyway to use $location?
Thanks
Not sure what you are trying to do with Angular service outside of the app, but I assume you have a good reason, because in many cases this will be considered bad practice. Anyway.
$location is not a property of the scope, so you can't get it like you are trying. However you can use $injector service to retrieve other services like $location:
angular.element('.ng-scope').scope().$apply(function() {
console.log('test:', angular.element('.ng-scope').injector().get('$location'));
});

Right place to detect browser and show/hide a div

I want to show a div (using ng-if or ng-show/hide) if the user opens the page in a certain browser (in this case, chrome/chormium), and I'm not really sure where is the best place.
The javascript code looks like this: /chrome/i.test( navigator.userAgent ), but where is the best place to place it? In a filter? In a controller? In a directive?
Check the below detailed browser detection script
http://www.quirksmode.org/js/detect.html
I will suggest you to use "Directive" or "Filter"
Working Demo
Below is the filter example code:
angular.module('myApp', []).filter('checkBrowser', function(){
return function(){
return /chrome/i.test(navigator.userAgent)
}
})
Template Code:
<div ng-show="{{''|checkBrowser}}">Hello</div>
You could assign your condition to a scoped variable inside the controller:
angular.module('myApp', []).controller('MyCtrl', ['$scope', function( $scope ) {
$scope.isChrome = /chrome/i.test(navigator.userAgent);
});
Then inside your view:
<div data-ng-show="isChrome">You're running Chrome!</div>
If you need a variable that's available to any controller, use a Service:
angular.module('myApp', []).factory('UAService', function() {
return {
isChrome: /chrome/i.test(navigator.userAgent)
};
});
Then from a page-level controller (i.e., one assigned to a top-level element such as <body>) or any other controller, inject your service:
angular.module('myApp', []).controller('MyCtrl', ['$scope', 'UAService', function( $scope, UAService ) {
$scope.UAService = UAService;
});
Your service would now be accessible from any scope within the scope created by your page-level controller:
<div data-ng-show="UAService.isChrome">You're running Chrome!</div>

If body has class, then redirect with angular JS

I am pretty new to AngularJS.
What would be the best way to detect if body has class "lt-ie9", and if this is true, then redirect the user to different subpage?
PS: Somebody told me to use use the " .run" method. What do you think about it?
The .run() method is fired when all modules have finished loading. It's the closest thing to a 'main' method that AngularJS provides. It wouldn't be unreasonable to handle this scenario here.
angular.module("MyApp", []).
run(function ($document, $location) {
if ($document.find("body").hasClass("lt-ie9")) {
$location.path("...");
}
});

Categories

Resources