Passing ng-directive to Leaflet L.popup().setContent() - angularjs

Thank you for giving my question a look.
Goal: Pass an Angular directive to the .setContent() method of L.popup()
The Problem:
I need to run $compile on the directive in order for it to enter ng. But something like
.setContent($compile('<new_marker_form></new_marker_form'))
yields a
Failed to execute 'appendChild' on 'Node': The new child element is null.
as I bet the ng is trying to compile before leaflet has actually appeneded any HTML.
Not sure if this is better suited for Stack Overflow. Please let me know if I ought to move it.

The closing > is missing in your example, not sure if it is just a typo in the question.
And you haven't linked the compiled element to any scope, the result of $compile is not an element, but a linking function. You have to call the function by passing a scope object that you want the element to bind with (or a least an empty object).
var linkFn = $compile('<new_marker_form></new_marker_form>');
var element = linkFn({}); // or pass any scope object, may be $rootScope.$new() if you do not have one.
L.popup().setContent(element[0]); // use element[0] to pass a DOM instead of jQuery/jqLite object.
Or a one liner ..
L.popup().setContent($compile('<new_marker_form></new_marker_form>')({}));
Hope this helps.

Related

Dynamically created element not firing click event

$scope.addNew = function(){
$('.thumb-img-gallary').append("<li><span class='img-del' ng-click='delThumbImgGallaryPhoto($event)'>X</span><img class='thumb-img' src='data:image/jpeg;base64,"+imageData+"'/></li>");
}
I am calling this function to add element dynamically. But then delThumbImgGallaryPhoto() is not getting called.
you cannot just append an element with a ng-click or any other directive, and expect it to work. it has got to be compiled by angular.
explenation about compilation from angular docs:
For AngularJS, "compilation" means attaching directives to the HTML to make it interactive
compelation happens in one of two cases:
When Angular bootstraps your application, the HTML compiler traverses the DOM matching directives against the DOM elements
when you call the $compile service inside an angular context
so what you need to do, is first to compile it(remeber to inject the $compile service in your controller), and then append it:
$scope.addNew = function(){
var elem = $compile("<li><span class='img-del' ng-click='delThumbImgGallaryPhoto($event)'>X</span><img class='thumb-img' src='data:image/jpeg;base64,"+imageData+"'/></li>")($scope);
$('.thumb-img-gallary').append(elem);
}
BTW, remember it is prefable not to have any DOM manipulations done in the controller. angular has directives for that.
You have to compile it with scope
try like this
var item="<li><span class='img-del' ng-click='delThumbImgGallaryPhoto($event)'>X</span><img class='thumb-img' src='data:image/jpeg;base64,"+imageData+"'/></li>"
$('.thumb-img-gallary').append(item);
$compile(item)($scope);
angular doesn't know anything about your newly added element. You need to compile your newly added element using $compile. and you should better use directive for this task.
It is a bad habit to access ui elements from controller.
edit: it would be best using ng-repeat for this task. lets say you have a thumb-gallery directive which is repeated using ng-repeat by thumbs array.
when you need to add a new image you only need to add it to your thumbs array.
it is simple and straightforward
Your html would look like
<thumb-gallery ng-repeat="gallery in galleries"></thumb-gallery>
and your js would look like
var gallery = {};
$scope.galleries.add(gallery);

$compile inside link function in angularjs directive not work as expected

I try do something with directive in angular, but I've some problem with $compile function in programmatically html element, call here "phe".
var phe = angular.element('<div style="background-color:orange">{{value}}</div>');
When I append "phe" after or before the directive's element, it work like a charm...
var $phe = $compile(phe)(scope);
element.after($phe);
but if I wrapped the directive element with this "phe" the $compile not work.
element.wrap($phe);
Somebody have some idea?
I have made a plunker http://plnkr.co/edit/0x2MmQ7WYmiNog0IEzTj?p=preview
it works if you change the compilation sequence... compile the element before placing it in the dom
var phe_b = angular.element('<div style="background-color:orange"> b {{value}}</div>');
var $phe_b = $compile(phe_b)(scope);
element.before($phe_b);
do same for after...
The reason it doesn't work with wrap is because wrap clones the DOM element. In other words, if you did:
var wrapper = angular.element("<div>");
element.wrap(wrapper);
console.log(wrapper[0] !== element[0].parentNode); // this would output "true"
So, the element that you compiled/linked is not the same that ends up in the DOM.
You could, of course, get the wrapping element (it's the return value of wrap) and $compile it, but you need to be careful not to re-compile/re-link certain directives that were applied on the current element (including the very same directive) and its children.

Is there a way to get a scope of a DOM element when debug info is disabled?

I'm writing an directive which need to retrieve a scope of current DOM element. using the non public api angular.element().scope();
It works well until angular 1.3 introduces a new feature $compileProvider.debugInfoEnabled(false); which mainly aims to improve performance to avoid bind data in DOM element. But when debugInfoEnabled() is set to false, angular.element().scope() will return undefined. So I must find another way to get the scope of an DOM element or I have to redesign my code logic.
Is there a way to make this possible?
I just faced a similar problem in our application after compiling our app with $compileProvider.debugInfoEnabled(false);. I needed to later access some of our directive's isolate scope but couldn't use the isolateScope() method. To get around the problem, I created a helper function in a Utils service that looks like this:
this.setElementIsolateScope = function(element, scope) {
element[0].isolateScope = function() {
return scope;
};
};
Then inside any directive where I needed to be able to later access the isolate scope I called this function inside the link() function: Since element is a jqLite object, you need to set the isolateScope() function on element[0]. You should already have the jqLite wrapped element and scope already passed into your link function, which you then just pass to your service method.
Utils.setElementIsolateScope(element, scope);
To then access the isolate scope later, you would get a reference to your element and then do this (assuming child_element is the reference to your element/directive):
var child_iso_scope = _.isFunction(child_element.isolateScope) && child_element.isolateScope();
Depending on how you are getting the reference to your element, you may need to wrap it a jqLite wrapper like this:
child_element = angular.element(child_element);
And then just use the same way as above to get the isolate scope. Hope this helps!

How can I assign a scope to a DOM element from the link function?

I am trying to implement a directive that that functions in a similar but not identical manner to ng-repeat. The details are not important, but at the end of the process I am attempting to pass a child scope to newly created DOM elements.
// Clone template element
var newItem = template.clone();
// Create new scope for element
var childScope = scope.$new();
// Pass relevant file into scope
childScope[indexStr] = member;
// Push new scope into element. <----- How to do this bit?
// Push element onto parent
template.parent().append(newItem);
// Clean up a bit
Naivly I tried:
newItem.scope = childScope;
but that doesn't work. (It just overrides the newItem.scope function to the best of my knowledge)
I also tried
newItem.scope() = childScope;
but apparently that is an illegal construct in JS.
The documentation is somewhat sparse on this, but I will gladly RTFM if someone can point me in the right direction.
I think you are looking for angular $compile functionality. You can use $compile provider to assign scope to a DOM element.
In your case do:-
$compile(newItem)(childScope);
Compiles an HTML string or DOM into a template and produces a template function, which can then be used to link scope and the template together.

How do I access the $scope variable in browser's console using AngularJS?

I would like to access my $scope variable in Chrome's JavaScript console. How do I do that?
I can neither see $scope nor the name of my module myapp in the console as variables.
Pick an element in the HTML panel of the developer tools and type this in the console:
angular.element($0).scope()
In WebKit and Firefox, $0 is a reference to the selected DOM node in the elements tab, so by doing this you get the selected DOM node scope printed out in the console.
You can also target the scope by element ID, like so:
angular.element(document.getElementById('yourElementId')).scope()
Addons/Extensions
There are some very useful Chrome extensions that you might want to check out:
Batarang. This has been around for a while.
ng-inspector. This is the newest one, and as the name suggests, it allows you to inspect your application's scopes.
Playing with jsFiddle
When working with jsfiddle you can open the fiddle in show mode by adding /show at the end of the URL. When running like this you have access to the angular global. You can try it here:
http://jsfiddle.net/jaimem/Yatbt/show
jQuery Lite
If you load jQuery before AngularJS, angular.element can be passed a jQuery selector. So you could inspect the scope of a controller with
angular.element('[ng-controller=ctrl]').scope()
Of a button
angular.element('button:eq(1)').scope()
... and so on.
You might actually want to use a global function to make it easier:
window.SC = function(selector){
return angular.element(selector).scope();
};
Now you could do this
SC('button:eq(10)')
SC('button:eq(10)').row // -> value of scope.row
Check here: http://jsfiddle.net/jaimem/DvRaR/1/show/
To improve on jm's answer...
// Access whole scope
angular.element(myDomElement).scope();
// Access and change variable in scope
angular.element(myDomElement).scope().myVar = 5;
angular.element(myDomElement).scope().myArray.push(newItem);
// Update page to reflect changed variables
angular.element(myDomElement).scope().$apply();
Or if you're using jQuery, this does the same thing...
$('#elementId').scope();
$('#elementId').scope().$apply();
Another easy way to access a DOM element from the console (as jm mentioned) is to click on it in the 'elements' tab, and it automatically gets stored as $0.
angular.element($0).scope();
If you have installed Batarang
Then you can just write:
$scope
when you have the element selected in the elements view in chrome.
Ref - https://github.com/angular/angularjs-batarang#console
This is a way of getting at scope without Batarang, you can do:
var scope = angular.element('#selectorId').scope();
Or if you want to find your scope by controller name, do this:
var scope = angular.element('[ng-controller=myController]').scope();
After you make changes to your model, you'll need to apply the changes to the DOM by calling:
scope.$apply();
Somewhere in your controller (often the last line is a good place), put
console.log($scope);
If you want to see an inner/implicit scope, say inside an ng-repeat, something like this will work.
<li ng-repeat="item in items">
...
<a ng-click="showScope($event)">show scope</a>
</li>
Then in your controller
function MyCtrl($scope) {
...
$scope.showScope = function(e) {
console.log(angular.element(e.srcElement).scope());
}
}
Note that above we define the showScope() function in the parent scope, but that's okay... the child/inner/implicit scope can access that function, which then prints out the scope based on the event, and hence the scope associated with the element that fired the event.
#jm-'s suggestion also works, but I don't think it works inside a jsFiddle. I get this error on jsFiddle inside Chrome:
> angular.element($0).scope()
ReferenceError: angular is not defined
One caveat to many of these answers: if you alias your controller your scope objects will be in an object within the returned object from scope().
For example, if your controller directive is created like so:
<div ng-controller="FormController as frm">
then to access a startDate property of your controller, you would call angular.element($0).scope().frm.startDate
To add and enhance the other answers, in the console, enter $($0) to get the element. If it's an Angularjs application, a jQuery lite version is loaded by default.
If you are not using jQuery, you can use angular.element($0) as in:
angular.element($0).scope()
To check if you have jQuery and the version, run this command in the console:
$.fn.jquery
If you have inspected an element, the currently selected element is available via the command line API reference $0. Both Firebug and Chrome have this reference.
However, the Chrome developer tools will make available the last five elements (or heap objects) selected through the properties named $0, $1, $2, $3, $4 using these references. The most recently selected element or object can be referenced as $0, the second most recent as $1 and so on.
Here is the Command Line API reference for Firebug that lists it's references.
$($0).scope() will return the scope associated with the element. You can see its properties right away.
Some other things that you can use are:
View an elements parent scope:
$($0).scope().$parent.
You can chain this too:
$($0).scope().$parent.$parent
You can look at the root scope:
$($0).scope().$root
If you highlighted a directive with isolate scope, you can look at it with:
$($0).isolateScope()
See Tips and Tricks for Debugging Unfamiliar Angularjs Code for more details and examples.
I agree the best is Batarang with it's $scope after selecting an object (it's the same as angular.element($0).scope() or even shorter with jQuery: $($0).scope() (my favorite))
Also, if like me you have you main scope on the body element, a $('body').scope() works fine.
Just assign $scope as a global variable. Problem solved.
app.controller('myCtrl', ['$scope', '$http', function($scope, $http) {
window.$scope = $scope;
}
We actually need $scope more often in development than in production.
Mentioned already by #JasonGoemaat but adding it as a suitable answer to this question.
Inspect the element, then use this in the console
s = $($0).scope()
// `s` is the scope object if it exists
You can first select an element from the DOM that's within the scope you want to inspect:
Then you can view the scope object by querying the following in the console:
angular.element($0).scope()
You can query any property on the scope, e.g.:
angular.element($0).scope().widgets
Or you can inspect the controller attached to the scope:
angular.element($0).scope().$myControllerName
(Another option that can work is to put a breakpoint in your code. If the $scope is currently in the current 'plain old JavaScript' scope, then you can inspect the value of $scope in the console.)
I've used angular.element($(".ng-scope")).scope(); in the past and it works great. Only good if you have only one app scope on the page, or you can do something like:
angular.element($("div[ng-controller=controllerName]")).scope(); or angular.element(document.getElementsByClassName("ng-scope")).scope();
I usually use jQuery data() function for that:
$($0).data().$scope
The $0 is currently selected item in chrome DOM inspector.
$1, $2 .. and so on are previously selected items.
Say you want to access the scope of the element like
<div ng-controller="hw"></div>
You could use the following in the console:
angular.element(document.querySelector('[ng-controller=hw]')).scope();
This will give you the scope at that element.
At the Chrome's console :
1. Select the **Elements** tab
2. Select the element of your angular's scope. For instance, click on an element <ui-view>, or <div>, or etc.
3. Type the command **angular.element($0).scope()** with following variable in the angular's scope
Example
angular.element($0).scope().a
angular.element($0).scope().b
Chrome's console
This requires jQuery to be installed as well, but works perfectly for a dev environment. It looks through each element to get the instances of the scopes then returns them labelled with there controller names. Its also removing any property start with a $ which is what angularjs generally uses for its configuration.
let controllers = (extensive = false) => {
let result = {};
$('*').each((i, e) => {
let scope = angular.element(e).scope();
if(Object.prototype.toString.call(scope) === '[object Object]' && e.hasAttribute('ng-controller')) {
let slimScope = {};
for(let key in scope) {
if(key.indexOf('$') !== 0 && key !== 'constructor' || extensive) {
slimScope[key] = scope[key];
}
}
result[$(e).attr('ng-controller')] = slimScope;
}
});
return result;
}
in angular we get jquery element by angular.element()....
lets c...
angular.element().scope();
example:
<div id=""></div>
For only debugging purposes I put this to the start of the controller.
window.scope = $scope;
$scope.today = new Date();
And this is how I use it.
then delete it when I am done debugging.
Also, we can access the scope by name of HTML element like this:
angular.element(document.getElementsByName('onboardingForm')[0]).scope()
Just define a JavaScript variable outside the scope and assign it to your scope in your controller:
var myScope;
...
app.controller('myController', function ($scope,log) {
myScope = $scope;
...
That's it! It should work in all browsers (tested at least in Chrome and Mozilla).
It is working, and I'm using this method.

Resources