How to update the scope of an appended html element? - angularjs

I would like to interact with a scoped inside an appended html element on the page, can someone please show me how to update that scope?
var overlay = angular.element('<div id="flyout-overlay" class="page-overlay global" ng-show="testScope"></div>');
mainContent.append(overlay);
$timeout(function(){
$scope.testScope = true; // how?
},500);

use $compile service
$compile(overlay);
here is the documentation
don't forget to add $compile dependency in the controller
doc says,
apply to your case,
'<div id="flyout-overlay" class="page-overlay global" ng-show="testScope"></div>'
1 : compile - $compile collect all the directives, for ex, it will collect ng-show directive
2: link - combine the directive with a scope.. , for ex, it will bind the ng-show="testScope" directive with the scope.

#Kalhano gave the perfect answer but since you don't know about the $compile service, here is the code for you. Just a small change....
var overlayTmpl = angular.element('<div id="flyout-overlay" class="page-overlay global" ng-show="testScope"></div>');
var overlay = $compile(overlayTmpl)($scope);
mainContent.append(overlay);
$timeout(function(){
$scope.testScope = true; // how?
},500);
in nutshell, $compile service compiles your html and links it to the scope you provided,
for better understanding read the angular documentation.

Related

angularjs-- declenching events

Please, how can I change this code into angularJs
$('a.product_add').on('click', function(event){
event.preventDefault();
var collectionHolder = $('#task_tags');
var prototype = collectionHolder.attr('data-prototype');
form = prototype.replace(/__name__/g, collectionHolder.children().length);
collectionHolder.append(form);
});
First of all you need to show us what you've tried, but I'll write something here to help you
You should make a directive because you're using jquery code. Read more about directives here
AngularJS directives are extended HTML attributes with the prefix ng-.
The ng-app directive initializes an AngularJS application.
The ng-init directive initializes application data.
The ng-model directive binds the value of HTML controls (input,
select, textarea) to application data.
Example of a directive
app.directive('myDirective', function(){
function link($scope,$elem,$attrs){
$elem.on('click', function(event){
// click event code here
});
}
return {
link:link,
scope:{},
restrict:'A'
}
})
Example of usage for myDirective:
<a class='product_add' my-directive>link</a>
We can use angular custom directives.
Now you can access the element in the directive and do the same operations in the directive.
<directive-element ng-click=appendFunction()></directive-element>

Jasmine unit test for Angular directive

I have the following angular directive, which adds a tooltip when I hover over a span.
angular.module('mainMod')
.directive('toolTip', [function() {
return {
restrict: 'A',
scope: {
theTooltip: '#toolTip'
},
link: function(scope, element, attrs){
element.tooltip({
delay: 0,
showURL: false,
bodyHandler: function() {
return jQuery('<div class="hover">').text(scope.theTooltip);
}
});
}
}
}])
;
<span ng-show="data.tooltip" class="icon" tool-tip="{{data.tooltip}}"></span>
I'm looking to write a unit test for this directive, atm I can't use jasmine-jquery.
I'm fairly new to writing unit tests, could anyone possibly help me out?
Give me some pointers or point me towards some helpful resources?
Any advice or suggestions would be greatly appreciated.
What I have atm isn't much...
describe('Unit testing tooltip', function() {
var $compile;
var $rootScope;
// Load the myApp module, which contains the directive
beforeEach(module('mainMod'));
// Store references to $rootScope and $compile
// so they are available to all tests in this describe block
beforeEach(inject(function(_$compile_, _$rootScope_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
it(' ', function() {
// Compile a piece of HTML containing the directive
FAILS HERE --> var element = $compile("<span class='icon' tool-tip='{{data.tooltip}}'></span>")($rootScope);
$rootScope.$digest();
});
});
It's failing with a message of
TypeError: undefined is not a function
I think it's being caused by the ($rootScope) at the end of the line I've specified above.
You have to wrap your DOM content with angular.element first before compiling it. I am not sure what the tooltip module you are using but I used the jQuery UI tooltip instead.
//create a new scope with $rootScope if you want
$scope = $rootScope.$new();
var element = angular.element("<span class='icon' tool-tip='This is the tooltip data'></span>");
//use the current scope has just been created above for the directive
$compile(element)($scope);
One more thing, because you are using isolate scope in your directive, to get the current scope from your directive, you need to call
element.isolateScope()
based on this reference : How to Unit Test Isolated Scope Directive in AngularJS
For a working fiddle, you can found it here : http://jsfiddle.net/themyth92/4w52wsms/1/
Any unit test is basically the same - mock the environment, construct the unit, check that the unit interacts with the environment in the expected manner. In this instance, you'd probably want to mock the tooltip creator
spyOn(jQuery.fn, 'tooltip');
then compile some template using the directive (which you're already doing), then simulate the hover event on the compiled element and then check that the tooltip creator was called in the expected manner
expect(jQuery.fn.tooltip).toHaveBeenCalledWith(jasmine.objectContaining({
// ... (expected properties)
}));
How you simulate the event depends on how the element.tooltip is supposed to work. If it really works the way you're using it in the question code, you don't need to simulate anything at all and just check the expected interaction right after template compilation.

ngRepeat in compiled by $compile does not work

I need to dynamically compile html and pass it from function as text.
So, I have this code (simplified version for debugging purpose):
angular.module('app', [])
.run(function($rootScope, $compile){
var data = ["1","2"];
var html =
'<div>' +
'<ul>' +
'<li ng-repeat="score in data">{{score}}</li>' +
'</ul>'+
'</div>';
var el = angular.element(html);
$rootScope.data = data;
var result = $compile(el)($rootScope);
console.log(result.html());
})
The result is only:
<ul><!-- ngRepeat: score in data --></ul>
So, it looks like ngRepeat does not "repeat" "li" element.
Why?
JsFiddle: http://jsfiddle.net/yoorek/K4Cmk/
(I know DOM manipulation should be in directive etc. and I know how to do it other way but I need to understand why this does not work)
If you look at the angular source code, ngRepeat will manipulate the DOM and "repeat" the elements within the $watchCollection handler:
ngRepeat Link Function
When you manually compile and link the element from the run block, the $watch handlers for your element have been set up but the $digest phase has not happened yet. It is the $digest phase where the scope examines all of the $watch expressions and executes their corresponding watch handlers.
If you want to inspect the element after the $digest (render) phase you can use $timeout:
$timeout(function() {
console.log(el.html());
alert(el.html());
});
Demo Fiddle
I was wondering why the answer above does not work with $scope.$evalAsync() instead of $timeout.
I believe these explain:
1. http://www.bennadel.com/blog/2605-scope-evalasync-vs-timeout-in-angularjs.htm
2. AngularJS : $evalAsync vs $timeout

Why $rootScope.$new does not let template into the directive?

I'm building unit testing using Karma and Mocha. Testing my directives, and using html2js (It converts the htmls to cached strings in $templateCache).
Interestingly, when using $rootScope.$new() in my test, the template html will not get into the directive . Here's the code:
it('should show a thumb name', function() {
inject(function($compile, $rootScope,$controller) {
var scope = $rootScope;//.$new() ($new not working. Why?)
var linkFn = $compile('<thumb></thumb>');
var element = linkFn(scope);
scope.$digest(); // <== needed so that $templateCache will bring the html
// (that html2js put in it)
console.log(element.html());// correctly returns thumb's directive templateUrl content
}));
...
However, if I use scope = $rootScope.$new(), the element.html() will return an empty string
any ideas?
many thanks
Lior
According to the docs for $digest (http://docs.angularjs.org/api/ng.$rootScope.Scope), this will only process watchers etc for the current scope and its children.
This suggests to me that when you set scope = $rootScope and then $digest you will be processing watchers etc on the $rootScope, I think this is where promises will be resolved too, releasing your templates. When you do scope = $rootScope.$new() and call $digest on that, I expect anything that should happen from the $rootScope doesn't happen.
So, does this work if you change scope.$digest() to $rootScope.$digest() or scope.$apply()?

angularjs mocking element for directive to add features

I use angular js 1.0.3 and I try to test my directive.
we use jQuery that is loaded automatically by angular and is accessible as angular.element that is passed to directive.
how can I add properties to the element before directive is linked with scope???
var def = '<input data-my-directive="" />';
var scope = $rootScope.$new();
var linked = $compile(def);
// do something to add property something that jq is adding
var directive = linked(scope);
my directive is something like
return function(scope, element, attrs) {
element.jq-plugin-method();
}
and my target is element passed to directive after linkage.
thanks for help
To answer my own question. it is sufficient to add
var jqLite = angular.element;
jqLite.prototype.jq-plugin-method = function(c) {...};
before
linked = $compile(definition);
I was blind or something yesterday or maybe I was adding this line after compile and it was too late.

Resources