Call angular factory with pure javascript - javascript

I struggled to title this question but basically I'm just starting off with angular and am using ngMaterial. I have a toast created by using an angular factory
app.factory('notify', ['$mdToast', '$animate', function($mdToast, $animate) {
return {
showToast: function(msg) {
var toast = $mdToast.simple()
.content(msg)
.action('Close')
.highlightAction(false)
.position('top right');
$mdToast.show(toast).then(function() {
//
});
}
}
}]);
This works great if I have a button on the page that activates the toast however I have socket.io running as well with node monitoring redis for updates to pop up that notification. However I can't get it to work as I'm not quite sure how I can call that factory from within here
socket.on('notification.new', function (data) {
//call factory here to show toast
console.log(data);
});
I know if I have it on a controller I can do it by using
angular.element().scope().showToast(data)
But I don't want to create an element just to house the controller to call the function.

What I did to solve this issue is I attached one of the functions inside the controller to the window object. So something like
window.showToast = function(msg) {
var toast = $mdToast.simple()
.content(msg)
.action('Close')
.highlightAction(false)
.position('top right');
$mdToast.show(toast).then(function() {
//
});
Then, from within the socket io listener (or any raw javascript for that matter):
socket.on('notification.new', function (data) {
//call factory here to show toast
window.showToast(data); //or however you'd like
});
And that kind of worked for me. I know this is not the best approach but since this question didn't have any answers at all I thought I'll post a workaround that at least does the job done.

You need to get hold of Angular services to use Angular in a raw Javascript.
Refer to Call Angular JS from legacy code
angular.element(domElement).scope() to get the current scope for the element
angular.element(domElement).injector() to get the current app injector
angular.element(domElement).controller() to get a hold of the ng-controller instance.
In your case use injector service to get hold of your factory.
Update 1
$injector reference

Related

In Protractor, is it possible to run an Angular modules function?

I'm very new to Javascript and Protractor. Still trying to get my head around simple syntax so forgive me if I'm way off base here.
So our angular app, has a module with a factory that generates toast messages. I'd like to disable all toast messages during my E2E testing. We have a function within the factory to disable toasts. Here's some simplified code.
//the module
var module = angular.module('toast',[]);
//the factory
module.factory('tf',[function tf(){
//factory code
//the function within the module's factory
moduleFactory.enable = function(enable){
isEnabled = enable;
};
}]);
My question is, can I access that function in protractor to turn that to false?
I've been searching around and it seems that mocking is how to do it. Something similar to how you disable angular animations.
// Disable animations so e2e tests run more quickly
var disableNgAnimate = function() {
angular.module('disableNgAnimate', []).run(['$animate', function($animate) {
$animate.enabled(false);
}]);
};
browser.addMockModule('disableNgAnimate', disableNgAnimate);
However, I'm struggling with the syntax on accessing the factory's function within the module...Any help would be appreciated.
I believe I've found the solution for anyone else that may have a similar issue.
Using the executeScript function of protractor.
browser.executeScript(function()
{
return angular.element(document).injector().get('toastFactory').enableToasts(false);
});

Angular Translate async timing issue with $translateProvider.useStaticFilesLoader

I am using the excellent Angular Translate ($translate) directive/service to deal with multiple Locale Languages and since I have multiple locale files I use the convenient $translateProvider.useStaticFilesLoader to load my translation files through a structure of localeAbbr.json, for example en.json, es.json, etc... I built a Plunker to show my open source project and that project uses the locale through Git raw files (pointing to the actual Github repository, meaning not local to the plunker demo). My project is built as a Directive and a Service, I made a small Plunker to show my timing issue with the JSON file loading.
All that to say that it seems $translateProvider.useStaticFilesLoader works asynchronous while I would really need it to be synchronous because by the time the plunker runs, the JSON files are not yet parsed while I already called a $translate.instant() on my messages.
I have a Plunker showing the problem.
And here is part of my quick Service demo:
app.factory('validationService', ['$filter', '$translate', function ($filter, $translate) {
var service = this;
var validationSummary = [];
var errorMessages = [
'INVALID_ALPHA',
'INVALID_ALPHA_SPACE',
'INVALID_ALPHA_NUM',
'INVALID_BOOLEAN'
];
//var $translate = $filter('translate');
for(var i=0, ln=errorMessages.length; i < ln; i++) {
validationSummary.push({
field: i,
message: $translate.instant(errorMessages[i])
});
}
// attach public functions
service.getValidationSummary = getValidationSummary;
return service;
// function declaration
function getValidationSummary() {
return validationSummary;
}
}]);
The $translateProvider configuration
app.config(['$translateProvider', function ($translateProvider) {
$translateProvider.useStaticFilesLoader({
prefix: 'https://rawgit.com/ghiscoding/angular-validation/master/locales/validation/',
suffix: '.json'
});
// load English ('en') table on startup
$translateProvider.preferredLanguage('en').fallbackLanguage('en');
}]);
Call my Service through the Controller:
app.controller("TestController", function($scope, validationService) {
var vm = this;
vm.displayValidationSummary = true;
vm.validationSummary = validationService.getValidationSummary();
});
and finally the HTML using the controller:
<div class="alert alert-danger alert-dismissable" ng-show="vm.displayValidationSummary">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true" ng-click="displayValidationSummary = false">×</button>
<h4><strong>{{ 'ERRORS' | translate }}!</strong></h4>
<ul>
<li ng-repeat="item in vm.validationSummary">{{item.field }}: {{item.message}}</li>
</ul>
</div>
Since I'm using AngularJS 1.3+, I also found that $translate only gets translated once, so the author suggest to use translateFilter.$stateful = true; and I tried but that doesn't seem to help.
Again here is the Plunker
I have been spending weeks on trying to find and code all kind of solution but I never got it to work and I'm really sad of seeing my raw translation code :(
Please Help!!!
EDIT
I realized that my question was not covering everything related to my problem. On top of the translation delay problem, I also have to pass extra arguments and that is a huge problem passing them to the translation anonymous function. By the time the promise is finished, the state of my arguments have already changed. For example:
$translate(validator.message).then(function(translation) {
// only log the invalid message in the $validationSummary
addToValidationSummary(formElmObj, translation);
// error Display
if(!isValid) {
updateErrorMsg(translation, isValid);
}else if(!!formElmObj && formElmObj.isValid) {
addToValidationSummary(formElmObj, '');
}
}, function(data) {
throw 'Failed to translate' + data;
});
When working with AngularJS, or JavaScript for that matter you really need to embrace the asynchronous paradigm. In order to make dealing with asynchronous code less cumbersome you can employ the use of Promises. Angular gives you a service called $q which does the heavy lifting for you
https://docs.angularjs.org/api/ng/service/$q
getting ones head around Promises can take time but well worth the effort in the long run.
Essentially what you need to do with your validationService is to make use of $translate's promise api which will give you the translation you require based on the supplied key when it is in a position to do so. What this boils down to is that you ask $translate for all of the translationId's you wish to get a translation for and when all have been fetched you populate the validationSummary array with your messages.
app.factory('validationService', ['$q', '$translate', function ($q, $translate) {
var translationsPromises = [],
validationSummary = [],
errorMessages = [
'INVALID_ALPHA',
'INVALID_ALPHA_SPACE',
'INVALID_ALPHA_NUM',
'INVALID_BOOLEAN'
];
angular.forEach(errorMessages, function(val, key) {
translationsPromises.push($translate(val));
});
$q.all(translationsPromises)
.then(function(translations) {
angular.forEach(translations, function(val, key) {
validationSummary.push({
filed: key,
message: val
});
});
})
.catch(function (err) {
console.error('Failed to translate error messages for validation summary', err);
});
// function declaration
function getValidationSummary() {
return validationSummary;
}
return {
getValidationSummary: getValidationSummary
};
}]);
I've forked your plunker and modified it to include the above sample
http://plnkr.co/edit/7DCwvY9jloXwfetKtcDA?p=preview
Another observation is that you are using the translate filter in the HTML. Please be aware that this can prove to be expensive if you have a large DOM as Angular will make the call to translate each key on every digest. An approach to consider would be to provide your vm with a labels object and use the $filter service to populate them upon controller instantiation.
I found out the answer to my problem of passing extra arguments to the anonymous function of the promise is to use Closures, in this way the variables are the same before the promise and inside it too. So I basically have to wrap my $translate call into the closure, something like the following:
(function(formElmObj, isValid, validator) {
$translate(validator.message).then(function(translation) {
message = message.trim();
// only log the invalid message in the $validationSummary
addToValidationSummary(formElmObj, message);
// error Display
if(!isValid) {
updateErrorMsg(message, isValid);
}else if(!!formElmObj && formElmObj.isValid) {
addToValidationSummary(formElmObj, '');
}
}, function(data) {
throw 'Failed to translate' + data;
});
})(formElmObj, isValid, validator);
and now finally, my variables are correct and keep the value at that point in time :)
While it is true that $translateProvider.useStaticFilesLoader does not return a promise, I looked inside the $translate service and found that it provides a handy callback onReady() which does return a promise. This callback is invoked when the $translate service has finished loading the currently selected language, and is useful for making sure that instant translations will work as expected after page initialization:
$translate.onReady(function () {
// perform your instant translations here
var translatedMsg = $translate.instant('INVALID_ALPHA');
});

How to get access to AngularJS in Jquery?

Recently I have begun to integrate AngularJS in project.
Before I have written many prototypes and single functions in JavaScript, for example (Node.js + socket.io functionality).
Today I a have trouble communicating AngularJS with clear JavaScript:
JS:
socket.on('message', function (data) {
showSystemMessage();
}
function showSystemMessage(message) {
// Increment here AngularJS $scope.messagesCnt++ ?;
}
AngularJS:
.controller('MessagesController', ['$scope', function($scope) {
$scope.messagesCnt = 0;
}])
HTML:
<div>{{messagesCnt}}</div>
Please, attention to method: showSystemMessage()
Ideally, You should follow what #Yaseer has said, but i see, that you're using Socket IO native implementation [No angular Wrapper around].
Hence, you have two solutions :
Use Angular Socket IO
Access Angular Controller from the native javascript :
For this you must have an element with an identifier ID. then you can use the id to access the associated scope.
angular.element("#your-identifier-id-here").scope().messagesCnt++;
Write your socket listener code inside controller
function appcontroller($scope){
socket.on('message', function (data) {
showSystemMessage();
}
function showSystemMessage(message) {
$scope.$apply(function(){
// Increment here AngularJS $scope.messagesCnt++ ?;
});
}
}
If you function showSystemMessage is outside the controller scope, there is no way you can update the scope variable messagesCnt. (There are but not straight forward.)
Instead this function of yours should reside in an angular service instead, and then on whatever event you want you could broadcast a message from your service to your controller to update its count value.

Restangular ignore setRequestInterceptor once

In the setup of my app, I use Restangular.setRequestInterceptor() to call a function that shows a loading screen anytime I make a request with Restangular.
However, there is one spot in my app where I don't want it to call that function. How can I tell Restangular to ignore the setRequestInterceptor function for this one call?
For anyone else that runs into this problem, it turns out Restangular lets you create a separate Restangular service with different configuration options than the global one. This example from the Restangular GitHub shows how:
// Global configuration
app.config(function(RestangularProvider) {
RestangularProvider.setBaseUrl('http://www.google.com');
RestangularProvider.setRequestSuffix('.json');
});
// Restangular service that uses Bing
app.factory('BingRestangular', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('http://www.bing.com');
});
});
// Let's use them from a controller
app.controller('MainCtrl', function(Restangular, BingRestangular) {
// GET to http://www.google.com/users.json
// Uses global configuration
Restangular.all('users').getList()
// GET to http://www.bing.com/users.json
// Uses Bing configuration which is based on Global one, therefore .json is added.
BingRestangular.all('users').getList()
});

Ember JS how to set up Application

I am an Ember noob and am trying to get it to work; however I am confused about the App.initialize() method.
It throws errors (it can't find the object App) if I use the following code:
App = Ember.Application.extend()
App.initialize()
However if I use the following code; it says initialize is being called twice.
App = Ember.Application.create()
App.initialize()
What is the best way to do this?
The Application no longer provides the initialize method. Instead you should use Application#deferReadiness and Application#advanceReadiness combined.
Example extracted from Ember's source code:
App = Em.Application.create();
App.deferReadiness();
jQuery.getJSON("/auth-token", function(token) {
App.token = token;
App.advanceReadiness();
});
Additionally, check the sample in jsfiddle:
window.App = Em.Application.create();
App.deferReadiness();
window.setTimeout(function() {
Em.TEMPLATES["application"] = Em.Handlebars.compile('<h1>App</h1> this is a template');
App.advanceReadiness();
}, 1500);
First, You have to understand the difference between create() and extend(). Easy way to understand is extend() method just extends the class of Ember.Application but create() method creates the instance of Ember.Application(). While creating the instance it runs the constructor. There are 3 ways to create the Ember.App and run it.
1
var App= Ember.Application.extend()
App.initialize()
2.
var App = Ember.Application.create()
This initialises as soon as u create object.
3
var App= Ember.Application.extend()
App.create()
To understand Ember Objects more go through this link. Understanding Ember.Object
Just create your application and let Ember initialize it.
All you need to do is:
App = Ember.Application.create()
The App will not be initialized immediately. It waits, at least, for DOM readiness and for the rest of your classes to be defined (by waiting until control is returned to the browser from the currently executed JavaScript).
If you want to defer it for other reasons, do something like this:
App.deferReadiness();
$.getJSON("/boot", function() { App.advanceReadiness(); });
This will wait to boot the app until the /boot Ajax call returns.
Just have a look here how to do this stuff:
http://emberjs.com/documentation/#toc_creating-a-namespace
How to bootstrap:
window.App = Ember.Application.create();
Without ever using ember.js, I would suggest that create and initialize both do initialization, that's why you get the latter error telling you it's inited twice.
And your first version is trying to extend the Application object, that is you create new functionality.
Ember "create" method accepts either no arguments, or an object containing values to initialize the newly instantiated object with, so you might also go like this below:
var appConfig = {
Token: token;
};
App = Ember.Application.create(appConfig);

Categories

Resources