how to idividually append directive to div in angularjs - javascript

HTML:
<div ng-app = "myApp" ng-controller = "someController as Ctrl">
<div class = "clickme" ng-repeat = "elems in Ctrl.elem" ng-click ="click()">
{{elems.title}}
click me
<div id="container">
</div>
</div>
angularjs
var Elems = [{title : "1"},
{title : "2"},
{title : "3"}];
var myApp = angular.module('myApp', []);
myApp.controller('someController', function($scope) {
var self = this;
self.elem = Elems;
$scope.click = function(){
//append directive <test-Input></test-Input>
};
});
myApp.directive('testInput',function(){
return {
restrict: 'E',
replace: false,
transclude: false,
template: '<div>asdasdasdasd</div>',
controller: function($scope) {
}
};
});
how to append directive to individual div when it clicked? I know it will be very easy with Jquery but it's hard to do in angular way.

You do not want to do it this way, one of Angulars advantages is that you do not have to deal with changing the HTML yourself, but use data-binding and templates instead, establishing an MVC-pattern. You are still thinking in jQuery ;-)
Change only your data on click and use ng-repeat to make angular append your template for you.
P.S.: Why do you use $scope in your controller? You are using controller as-sntax (which is good), you can simply bind the click()-funciton to the controller instead of $scope

Try this..
var modalElementTemplate = angular.element(Your template);
var linkFn = $compile(modalElementTemplate);
var modalElement = linkFn($scope);
$("#WheretoAppend").append(modalElement);

Instead of doing a huge DOM alteration put the testInput directive in the ngRepeat loop and add a check at the top of the directive whether the div container has been clicked or not (need scope isolation).

Related

AngularJs Directive update

I want to update my directive content only at some desired places, but not at others. I have simulated my problem here:
http://jsfiddle.net/Lvc0u55v/2945/
The problem is, I have an 'editor' directive which is applied in two places:
<span class="editor1" editor ></span>
<span class="editor2" editor ></span>
I want to update the content of span class="editor1" on button click.
How do I do it?
Why not go with a relatively Angularesque approach by isolating the scope of the directive and to a maximum extent, avoid jQuery in your logic.
So you could have your directive defined as such:
.directive('editor', function() {
return {
scope: {
upd : '=',
editordata : '=data'
},
template: '<div>{{editordata}}</div>',
controller: function($scope, $rootScope, $element) {
$rootScope.$on('update', function(evt, data) {
if(data.upd === $scope.upd){
$scope.editordata = data.txt;
}
})
},
link: function(scope, el, attr) {}
}
})
Here, you are passing the required information which the editor directive depends upon through its scope, namely upd (which I suppose is how you want to uniquely identify the items by) and the text data.
Meanwhile, you can define a list of the editor items in the common parent controller MyCtrl and iterate over them in the DOM with ng-repeat.
// MyCtrl controller
$scope.list = [
{upd: 'editor1', data: 'original data for editor1'},
{upd: 'editor1', data: 'original data for editor2'}
]
<!-- HTML -->
<div ng-repeat="item in list" upd="item.upd" data="item.data"></div>
Demo
You can check if the current directory has the "editor1" class and if so complete your logic.
You can look at this example :
element[0].querySelector('.editor1') !== undefined'

angularjs $compile service and ng-repeat

Basically what I'm trying to do is build a directive that would take array as an isolate scope object. Build html iterating through the array with ng-repeat and compiling using $compile service against the directive's scope which would then be pushed into the content attribute of the popover. It works fine when ng-repeat is applied to the immediate parent node of the references. Fails when not. Can someone enlighten why it wouldnt work. Thanks in advance
Plunkr url: http://plnkr.co/edit/i5DlOWgHbyC8YovgKvt6?p=info
HTML
<a working data-names="['cat','dog','mouse']">Click to get a basic popover - working</a>
<br/>
<a not-working data-names="['cat','dog','mouse']">Click and you will get nothing</a>
JAVASCRIPT
app.controller('MainCtrl', function($scope) {
}).directive("working", function($log,$compile,$http){
return {
restrict: "A",
scope:{
names:'='
},
link: function(scope, elem, attrs){
$log.log(scope.names);//Logs Names
var html = "<p><a ng-repeat='name in names'>This is a {{name}}</a></p>";
var popOverContent = $compile(html)(scope);
$log.log(popOverContent);//Logs p.ngscope properly
var options = {
content: popOverContent,
placement: "top",
html: true
};
$(elem).popover(options);
}
};
}).directive("notWorking", function($log,$compile,$http){
return {
restrict: "A",
scope:{
names:'='
},
link: function(scope, elem, attrs){
$log.log(scope.names);//Logs Names
var html = "<p ng-repeat='name in names'><a>This is a {{name}}</a></p>";
var popOverContent = $compile(html)(scope);//Logs only a comment
var options = {
content: popOverContent,
placement: "top",
html: true
};
$(elem).popover(options);
}
};
});
apprently has to do with the popover structure not the scopes since both directives keep their ngrepeat in the same scope level, it appears that the popover needs to have only one root element in its template in the second example you are building several root elements
the only update was tha instead of
var html = "<p ng-repeat='name in names'><a>This is a {{name}}</a></p>";
i used
var html = "<div><p ng-repeat='name in names'><a>This is a {{name}}</a></p></div>";
http://plnkr.co/edit/eMGQFykGjEImzXFA0ffh?p=preview

Getting ng-repeat to work inside of AngularJS's $interpolate service

I am using the AngularJs-UI components for Bootstrap. I would like to insert a filled out template into one of the data elements for the popover feature. This works find for all elements not inside of a ng-repeat. How do I get the ng-repeat elements to work inside of a interpolated template?
I have a plunker at http://plnkr.co/edit/Cuku7qaUTL1lxRkafKzv Its not working because I don't know how to get Angular-UI-bootstrap to in plunker.
<div data-popover="{{createHTML()}}">some content</div>
My local scope has the function createHTML() that looks something like this.
angular.module('myApp', ['ngSanitize'])
.controller("myController", function(myService){
$scope.createHTML = function() {
var thingy = { blah: "foobar", collection: [ "1", "2", "3" ] };
return myService.html_for(thingy);
}
});
And the service is
angular.module('myApp')
.service('myService', function($templateCache, $interpolate, $sanitize, $log) {
"use strict";
function html_for(thingy) {
var template = $templateCache.get('thingyTemplate.html'),
link = $interpolate(template),
html = link(thingy),
unsafe = $sanitize(html);
return unsafe;
}
return {
html_for: html_for
}
});
Templates:
<script type="text/ng-template" id="thingyTemplate.html">
<div>
<div><strong>Blah:</strong> {{blah}}</div>
<div data-ng-repeat="foo in collection"><strong>Collection:</strong> {{foo}}</div>
<div><strong>Collection[0]:</strong> {{collection[0]}}</div>
<div><strong>Collection[1]:</strong> {{collection[1]}}</div>
<div><strong>Collection[2]:</strong> {{collection[2]}}</div>
</div>
</script>
<script type="text/ng-template" id="template/popover/popover.html">
<div class="popover {{placement}}" data-ng-class="{ in: isOpen(), fade: animation() }">
<div class="arrow"></div>
<div class="popover-inner">
<h3 class="popover-title" data-ng-bind="title" data-ng-show="title"></h3>
<div class="popover-content" data-ng-bind-html="content"></div>
</div>
</div>
</script>
$interpolate doesn't handle directives like ngRepeat (
Difference between parse, interpolate and compile ). $interpolate:
Compiles a string with markup into an interpolation function. This
service is used by the HTML $compile service for data binding.
To handle ngRepeat and other directives you want $compile. But for your use case $compile is going to result, unfortunately, in a number of changes because:
It needs a scope to compile against rather than just a context like $interpolate. Moreover it needs the scope thingy is on.
This means we'll need to reference your properties like so {{thingy.blah}} instead of {{blah}} inside your template.
The compile needs to happen when the popup is on the dom.
The popup is only on the dom when it's open.
So we can't just replace $interpolate with $compile inside your service.
One approach is to replace data-ng-bind-html with the following directive that acts like an ng-bind-html that has a built in $compile (clearly you should only use this with html that you know is safe).
.directive('compile', function($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
return scope.$eval(attrs.compile);
},
function(value) {
var result = element.html(value);
$compile(element.contents())(scope.$parent.$parent);
}
);
};
});
Used like so (with compile replacing ng-bind-html:
<div class="popover-content" compile="content"></div>
One issue is that we need thingy to be in scope. There's a few of ways of handling that- but for demonstration purposes I've manually gone back up to the scope the popover is called from - which is 2 scopes up thus the scope.$parent.$parent.
Using this compile directive you no longer $interpolate or $sanitizeso the function in your service can shrink down to just returning the appropriate template:
function html_for() {
var template = $templateCache.get('thingyTemplate.html');
return template;
}
demo fiddle

Testing a directive that depends on another directive - Angularjs

I have a directive that contains another directive. Here is the directive:
<div class="chat-container">
<h5 class="chat-header">
<span class="patient-name-container">{{encounter.patient.firstName }} {{encounter.patient.lastName}}</span></h5>
<div ng-show="showMessages">
<div messages-container messages="encounter.comments"></div>
</div>
</div>
And here is my test:
var element;
beforeEach(module('app/views/chat.container.html'));
beforeEach(inject(function($templateCache, $compile, $rootScope) {
var chatTemplate = $templateCache.get('app/views/chat.container.html');
$templateCache.put('views/chat.container.html', chatTemplate);
var directive = angular.element('<div chat-container max-chat-count="800" class="pull-left"></div>');
element = $compile(directive)($rootScope);
$rootScope.$digest();
}));
it('the remaining count to be 800', function() {
expect(element.find('#counter').text()).toBe('800');
});
});
My error is Error: Unexpected request: GET views/messages.container.html. It is looking for the html to create the messages-container directive, but it cannot find it. I have tried adding the other directive like this right after the first one:
var messagesTemplate = $templateCache.get('app/views/messages.container.html');
$templateCache.put('views/messages.container.html', messagesTemplate);
but I still get the same error, so I have 2 questions.
First - How do I test this?
Second - I am now creating my test with a dependency on another directive, what is the right way to handle that?
The problem was me not adding the template with the module function before I tried to access it. Here is the code that fixed the issue:
var element, scope;
beforeEach(module('app/views/chat.container.html'));
* beforeEach(module('app/views/messages.container.html'));
beforeEach(inject(function($templateCache, $compile, $rootScope) {
var chatTemplate = $templateCache.get('app/views/chat.container.html');
$templateCache.put('views/chat.container.html', chatTemplate);
var messagesTemplate = $templateCache.get('app/views/messages.container.html');
$templateCache.put('views/messages.container.html', messagesTemplate);
var directive = angular.element('<div chat-container max-chat-count="800" class="pull-left"></div>');
element = $compile(directive)($rootScope);
$rootScope.$digest();
scope = $rootScope;
}));
I did not have the line with the * before. Now everything works perfectly.

Angularjs/Jquery data()

How to attach arbitrary data to an html element declaratively, and retrieve it.
Please see the code. http://plnkr.co/edit/sePv7Y?p=preview
Angular has the jQuery data() support.
So, I want to attach data to each li element (say _data = node ) in the template, and later on to retrieve it using
var li = elm[0]....
console.log('li-', li.data('_data'))
li - {id:1}
Code:
'use strict';
var app = angular.module('Directives', []);
app.controller('MainCtrl', function ($scope) {
$scope.data = [
{id:1}, {id:2}, {id:3}
];
});
app.directive('test', function ($timeout) {
return {
template: '<li class="ch" ng-repeat="node in data">' +
'<span class="span2">' + 'id - {{node.id}}' + '</span>' +
'</li>',
restrict: 'A',
link: function (scope, elm, attrs) {
console.log(elm[0].children);
}
};
});
Edit:
Updated the code with how I like to set data.
template: '<li class="ch" ng-repeat="node in data" data-node="node">' +
couldn't select the li element properly now to see whether it is working
tried,
elm[0].children[0].data()
elm.children[0].data()
etc..
First of all, if it were some third party lib that you are trying to integrate with angular, that might be ok, but now you're generating DOM with angular and embedding data in the DOM. This is very strange.
Second, your test directive template uses ngRepeat, which creates isolate scope and you won't be able to access li items declaratively. You will have to use DOM traversal, which is also not very angular-way-ish.
Third, your view should be bound to model by angulars two-way bindings. Do not try to simulate opposite behaviour on top of that. Either you should not use angular or you should change your approach to your problem, because it will be pain to develop and maintain otherwise.
I would provide a real answer if you could describe what are you trying to achieve and why exactly do you need that model in data. Now the easiest solution would be ditching test directive and rewriting it as such:
controller's template:
<ul>
<li ng-repeat="node in data" model-in-data="node">
<span class="span2">id - {{node.id}}</span>
</li>
</ul>
directive modelInData
.directive('modelInData', function($parse) {
return {
restrict: 'A',
link: function($scope, $element, $attrs) {
var model = $parse($attrs.modelInData)($scope);
$attrs.$set('data', model);
}
}
});
Here each li element adds it's model to the data attribute.

Categories