I have a angular app where half is angular js and half is plane js like ajax. So how do i use $location inside that function without passing it as a function parameter.
function x() {
$location.path('/error').replace();
}
Now i wish to know how to use $location inside a normal javascript function. Basically i have a function that is being called from at least 20 places. In that function i wish to do replacestate.
You need use angular.injector that will provide and access to provider from outside.
angular.injector(['app']).get('$location')
For more information Refer this SO Answer
Update
You could directly apply injector on element, that will give access to all the service present inside that module.
angular.element('body').injector().get('$location'); //body can replace by your element.
I would recommend wrapping that function as a service. And change myFunction and x to a sensible name. Change app to whatever your app name is.
function x($location) {
$location.path('/error').replace();
}
x.$inject = ['$location'];
angular.module('app').function('myFunction', x);
Related
My goal is to create an Angular module that displays popup dialog messages. This module contains a directive (HTML, CSS and JavaScript) containing the internal logic (and markup and styles). Plus there's a service (factory) which acts as an API that can be used by other services.
Now this service of course has an openDialog() function which should insert the dialog directive into the DOM and present it to the user.
All solutions to this problem I have found so far make use of the $compile function. But it needs scope as a parameter. In a service where there's no scope though. They only exist in controller or link functions.
The reason I chose this implementation is for separation of concerns (directive's link and controller for internal usage, factory for external usage because it can be dependency injected). I know I could pass the scope when calling the function like this:
popupDialogService.openDialog({ /* options */ }, $scope);
But I don't see the point. It doesn't feel right. What if I call that function from inside another service which doesn't use scope either?
Is there a way to easily put the directive into the DOM from inside the service function or is there a better way to solve this problem?
Another solution I'm thinking about is calling a function of the directive's controller from inside the directive's factory. Is that possible?
Code
popupDialog.directive.js
angular.module('popupDialog').directive('popupDialog', directive);
function directive() {
return { ... };
}
popupDialog.service.js
angular.module('popupDialog').factory('popupDialogService', factory);
function factory() {
return { openDialog, closeDialog }; // *ES2015
function openDialog(options) {
// this function should put the `popupDialog` directive into the DOM
}
function closeDialog() {
// and this one should remove it
}
}
some.random.service.js
angular.module('myApp').factory('someRandomService', factory);
factory.$inject = ['popupDialogService'];
function factory(popupDialogService) {
return { clickedButton };
function clickedButton() {
popupDialogService.openDialog({ /* options */ });
// Sample implementation.
// It shouldn't matter where this function is beeing called in the end.
}
}
I know I could pass the scope when calling the function ... And it doesn't feel right.
Well you anyway need scope for dialog HTML content, Angular needs to compile and render it in some scope, right? So you have to provide scope object for your template somehow.
I suggest you to take a look at popular modal implementations like they do it, for example Angular UI Bootstrap's $modal or this simple one I was creating for my needs. The common pattern is passing scope parameter with modal initialization or use new child scope of the $rootScope for dialog. This is the most flexible way that should work for your both cases.
After all, it's not necessarily has to be real scope instance. You can even make your service accept plain javascript object and use it to extend new $rootScope.$new() object with.
In an effort to avoid repeating code I found it useful to have helper functions that could be called from within a foo.rendered function (for instance). Why is this possible in 0.9.3 of Meteor, but throws an error in 1.0 ?
Template.foo.helpers({
'fooFn' : function(){
return "something"
}
});
Template.foo.rendered = function(){
var something = Template.foo.fooFn();
}
Should I change the syntax in foo.rendered (am I calling it wrong?) or maybe use a different approach entirely (set up functions outside of the helpers({}) and rendered() and call those? or set this up as a registered helper function?
It looks like it is possible as of Meteor 1.0.3.1 to find and call helper functions, although it is clear it's not supposed to be used like this.
Still it can be done:
Template.foo.__helpers[" fooFn"]()
Please notice the leading space for the function name.
The other way of dealing with this is attaching a function to a global namespace, then calling that from somewhere else in your code, as user3557327 mentioned.
Additionally you can use:
Template.registerHelper('myHelper', function (){return 'Look At Me!'})
to register a global helper, and call it explicitly using:
UI._globalHelpers['myHelper']()
I think this would be a better method: How to use Meteor methods inside of a template helper
Define a function and attach it to the template. Call that function from rendered, as well as your template helper. Like MrMowgli said, you probably aren't "supposed" to call template helpers from within the .js file, only from the ...that could probably break in the future.
For example define a function and attach it to the tamplate:
Template.Play.randomScenario = function () { // HACK HACK HACK }
and then call it from your lifecycle method
Template.Play.created = function () {
Template.Play.randomScenario();
};
scenario: function () {
return Template.Play.randomScenario();;
},
I had the same problem and this is the solution I used. Hope that helps.
What I want is very simple, I want the Expand All button to be auto clicked when I open this pluralsight course page. Its HTML is:
<a id="expandAll"
ng-click="expandAllModules()"
ng-hide="allModulesExpanded()">
Expand All
</a>
So it seems easy and we just need to call the function expandAllModules(). However I don't know why it give me undefined when I check its type:
typeof expandAllModules
=> "undefined"
Generally typeof a function should give me "function" like this:
function a(){}
=> undefined
typeof a
=> "function"
Since the function expandAllModules() is not available, I can't call it. Anyone can give me a hand on this issue?
Edit
Perhaps I need to elaborate on my question. I'm not the author of that page. I just want to make a simple greasemonkey or tempermonkey script and expand the modules automatically when I enter the page.
The Problem
The reason calling just expandAllModules() doesn't work is because this function belongs to one of Angular's scopes and isn't a method assigned to window. This function is defined in Plural Sight's table-of-contents-controller-v9.js like so:
"use strict";
pluralsightModule
.controller("TableOfContentsController", ['$scope', ..., function ($scope, ...) {
...
$scope.expandAllModules = function() {
_.each($scope.courseModules, function (module) { module.visible= true; });
};
...
}])
The Solution
In order for us to call this function ourselves, we have to go through this scope.
scope is an object that refers to the application model. It is an execution context for expressions. Scopes are arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can watch expressions and propagate events. – AngularJS: Developer Guide
The scope is part of the element which triggers the function. We can access this particular scope by passing the element's id attribute into angular.element(), then calling scope() on that object:
angular.element('#expandAll').scope()
This will give us the following data, where we can see the expandAllModules() function:
Unfortunately AngularJS doesn't let us simply execute scope().expandAllModules(); instead we have to go through it's $apply and $eval methods:
var scope = angular.element('#expandAll').scope();
scope.$apply(function() {
scope.$eval(scope.expandAllModules())
});
We can now also collapse the modules as well by calling:
scope.$apply(function() {
scope.$eval(scope.collapseAllModules())
});
I apologize if I am off-base here. Are you trying to "link" into that page and 'force' the page to "expand all", or do you have access to the page, and want to trigger the click with some code on the page, by you inserting the code? Just doing something like this seems to work from commandline.
jQuery(function(){
jQuery('#expandAll').trigger('click');
});
Since I do not know your need, my thought is that this is a bit simplistic and not what you are looking for. From the responses of others, it appears you want to create your own directive to initiate the click?
I might have some typos -- but the idea is there.
angular.element(document.body).ready(function() {
var el = angular.element( document.getElementById('expandAll') );
var scope = el.scope();
scope.expandAllModules();
scope.$digest(); <--- might not be needed, but when i check your site, it needs to have this
});
updates
if it was just 'onclick' instead of 'ng-click', you do not need to get the scope; and just call the function directly.
updates
I have tried this on your site, you need to have scope.$digest(). When I tried it, i was using the developer console.
see the developer console below
I was playing with it on your site.
UPDATE: The back-end service was powered by an ASP.Net AJAX Web Service proxy.
1) The main page has two global objects, one for the back end connections (Svc) and another for handling the DOM (Main). It also dynamically loads documents into an iframe.
2) These iframes need to access services provided by Svc, and also supply a callback function.
3) The problem - passing a function created in the iframe to the parent frame, it's treated as an object not a function and cannot be invoked.
Can anyone provide a better solution than what I've got currently in the iframe:
var Main = parent.Main,
Svc = parent.Svc;
Svc.method(data, Main.createCallback(
function(response) {}
));
and in the parent frame:
Main.createCallback = function(func) {
return function() {
func.apply(func, arguments);
}
}
if you override the iFrame's function from the main, the main scope will then be used.
The inverse problem can be seen here, in your case, you just override the frame's function itself i.e:
document.getElementById('yourFrameID').contentWindow.targetFunctionInFrame = targetFunctionInMain;
Bonus: if you can modify the iFrame's code, I would suggest to:
In the frame:
make a placeholder function callbackParent() {}
add a call to this function into your iframe code, so that you just have to override the callbackParent from your main.
In the main:
make the function which should be invoked function doStuff() {}
override the function as described above document.getElementById('yourFrameID').contentWindow.callBackParent = doStuff;
I use iframes to modularize my app too.They are a kind of includes embedding all CSS, HTML and JS for a module.
My first attempts were by returning a function too, but then I found it quite hard for sharing scopes.
Now I make directly a reference to the main parent object in the iframe.
eg:
var Svc = parent.Svc, JSON = parent.JSON, $ = parent.$;
Svc.module1 = {
method1:function(arg){
...
},
...
}
The global var JSON and jQuery references are here to have them available inside the methods.
My guest is that Svc.method is making some checks to see if the callback has some criteria before calling it. This criteria might be that the callback function must created by the same framework (here it's ASP.Net). You have to find what that criteria is. if "Main.createCallback" works, it's because it's meeting that criteria.
Sorry but your all wrong... add this....
const event = new CustomEvent('MSGSent', { detail: "fff variable" });
Call it like this....use a global variable for detail... like an array []
window.dispatchEvent(event);
Now after the iframe loads add this code and you get an Object back in the main page....
iframe.onload = function() {
try {
iframe.contentWindow.addEventListener('MSGSent',function(e){
alert(e.detail);
});
} catch (error) {
}
};
The problem is ASP.Net AJAX Web Service proxies, which don't appear to support calling the web service from an iframe with an inline callback function.
I want to do something that in a classical object oriented language like Java, C# etc. is very easy to do. I simply want to access a property of an instantiated object. The object is globally scoped in the browser's window object, and provided by the twitter #anywhere API.
For my code examples, assume you have already logged the user in.
If I were using java for instance, I would say (assuming all fields were public:
twttr = new twtter();
String screenName = twtter.currentUser.data('screen_name');
For some reason, this is way hard in Javascript. I've gotten a workaround working where inside the anonymous method that the twitter anywhere API is using, I set the value I want to a DOM element, and fish it out later. This is ugly though. I just want to access it directly.
Here's what I have so far, which doesn't even pass syntax checks in eclipse:
function AnywhereFacade()
{
var twitterReference;
window.twttr.anywhere
(
return function(T)
{
twitterReference = T;
};
)
getValue(propertyToGet)
{
return twitterReference.currentUser.data(propertyToGet);
}
};
var anywhereFacade = AnywhereFacade();
var screen_name = anywhereFacade.getValue("screen_name");
alert("screen name is: " + propertyGetter);
Please help! Why is Javascript so hard to use anyway? What I'm trying to do is use a closure I think.
Thanks!
I have done something similar in my app since I am using the Facebook JavaScript SDK and Twitter SDK and want to provide a consistent interface to access both. So I namespace the variables under App. For twitter anywhere, this is how the variable is captured.
window.App = {};
twttr.anywhere(function(T) {
App.Twitter = {
getValue: function(property) {
return T.currentUser.data(property);
},
getPublicTimeline: function() {
return T.Status.publicTimelime();
}
};
});
We are calling the anywhere function and passing it a callback function. The callback function is needed because the anywhere library might not be loaded at this point. By passing the entire function, we are saying that this function should be executed whenever the anywhere library is loaded.
Now when the library does load, this function will execute, define the App.Twitter property which contains a getValue function. The anywhere or T object is captured in the closure.
If you now call,
App.Twitter.getValue("screen_name");
the actually anywhere object (T), will be used to get the screen_name property.
this is all I needed to do.
document.getElementById('messagePanel').innerHTML = "loading...";
window.twttr.anywhere(function(T)
{
document.getElementById('messagePanel').innerHTML = "screen_name: " + T.currentUser.data('screen_name');
});
this made me realize my issue was just that I had to use a callback for when twitter returned from the async call. that helped me solve my initial problem of how to wrap it for gwt.