AngularJS directive copying with getElementById(myID).innerHTML - javascript

I am trying to copy/display the content of one div (which contains directives) into another.
When I execute the below the directives show up fine but none of the functionality, e.g. ng-click, work.
The ng-click and other options work in the source Div just not when it is copied into the destination.
Also I am not getting any console errors.
Please advise.
var srcDiv = document.getElementById(id + '-content'); //contains directives + all working fine
var dstDiv = document.getElementById(id + 'x'); //contains directives but no functionality or errors
dstDiv.innerHTML = srcDiv.innerHTML;

You need to tell Angular to $compile the contents. Angular will not recognise bits of HTML being inserted into the DOM at arbitrary times.
Inject the $compile service to wherever you are doing your DOM manipulation.
var srcDiv = angular.element(document.getElementById(id + '-content'));
var dstDiv = angular.element(document.getElementById(id + 'x'));
dstDiv.html(srcDiv.html());
$compile(dstDiv)(scope);

try
...
dstDiv.innerHTML = srcDiv.innerHTML;
scope.$apply();

Related

Problems with forced escaping of characters when creating AngularJs element

I stumbled upon an issue with AngularJS and creation of elements.
In short: I need to create an element with html contents, but I do want to have the reference to that element at hand, so that I can perform some actions on it without actually rendering it in the browser.
I tried doing the following:
var template = angular.element('<div>' + templateString + '</div>');
$compile(template)($scope);
which kinda does the trick, but...
Some of these templates have logical expression in them. in example:
<div ng-if="a && b">something</div>
Unfortunately it seems that when I try to create such an element, regardless if I use $sce.trustAsHtml() with ng-bind-html or not the & characters within these conditions get escaped as &.
The html I get in the template looks like this:
<div ng-if="a && b">something</div>
Please take a look at the fiddle: https://jsfiddle.net/3uxdrp7b/1/
if this is a known issue - I'd be thankful for pointing me in the right direction, cause I've been banging my head against this for quite some time and I can't get it to work. Every example I looked at had a simple html inside the binding - it all works ok unless I use the cursed &.
The main issue is that the element is not updated right after compiling. It will contain the correct value after the current digest cycle is finished.
<!-- HTML -->
<div class="container" ng-app="app" ng-controller="DemoController">
<h3>Result:</h3> <span ng-bind-html="el"></span>
</div>
// js
var app = angular.module("app", ["ngSanitize"]);
app.controller("DemoController", ["$scope", "$compile", "$timeout",
function($scope, $compile, $timeout) {
$scope.a = true;
$scope.b = true;
let contents = "<div ng-if='a && b'>something</div>";
let compiled = $compile("<div>" + contents + "</div>")($scope);
$timeout(function(){
console.log(compiled.html());
$scope.el = compiled.html();
},0,true);
}]);
The fiddle: https://jsfiddle.net/vy9svmop/
It might be that in your case it is simpler to use the $interpolate service to recive the immediate result.

Displaying json data via angular

I have created a datatable showing a list of persons and their details. When the datatable is clicked, it has to show the entity of the single person with their details but my problem is, when I click the datatable it is opening a chat box showing the entity of the last clicked person changing all other chat box details.
1.How can I limit the append directive, that is, one chatbox for one id?
2.How to display the name of the particular person without changing the older chat box entity?
Here is the link to Plunker http://plnkr.co/edit/VTWcZQjlAda0KQ9sjFzI?p=preview
Actually i really think there is no need for a directive here.
It can simply be done by using ng-repeat and a collection.
See it working in this plunker
I added this in the controller :
$scope.bottomListCollection = [];
$scope.addToBottomList = function(artist) {
$scope.bottomListCollection.push(artist);
}
And this kind of ng-click on your rows :
ng-click="addToBottomList(item)"
Some advices to do things cleaner in angular :
Never use :
$compile.
$element.
$broadcast.
Jquery.
Take care of custom directives, theses are 90% miss-used.
Just a reminder : Directives are intended to add a behavior on an element. Not adding html.
Hope it helped.
Some tips that fix your issue
childScope = $scope.$new(true);
to ve an isolated scope you ve to use the first parameter of $new method
for more info look at the docs ($new(isolate, parent); --> https://docs.angularjs.org/api/ng/type/$rootScope.Scope)
then you need to add a control on the AppendText function like this to check if the same chat already exist
$scope.AppendText = function(idx) {
$scope.userInfo = $scope.artists[idx];
var chat = $('#chat_'+$scope.userInfo.shortname);
console.log(chat);
if ($scope.stage === 'Add' && chat.length==0 ) {
childScope = $scope.$new(true);
childScope.userInfo = $scope.userInfo; //<--- add old scope info to new scope
var compiledDirective = $compile('<div my-directive></div>');
var directiveElement = compiledDirective(childScope);
$('.my-directive-placeholder').append(directiveElement);
} else {
$scope.stage = 'Add';
}
}
working plunker http://plnkr.co/edit/TFjlN0U2i4irKtG2D5yu?p=preview
To answer the second part of your question : Create an isolate scope!
That can be done by passing true while creating a new scope: childScope = $scope.$new(true).
Once the isolate scope is created, you can do:
childScope.userInfo = $scope.userInfo;
PLUNK : http://plnkr.co/edit/IxPh4EmLpr8WAqRWtRlo?p=preview
Also, a hackish solution using one time databinding (not recommended):
http://plnkr.co/edit/RjZNOSyaemqg2eZ4Gma1?p=preview
To answer the first part: You could keep track of the id's that are passed to the $scope.AppendText function perhaps?
PLUNK: http://plnkr.co/edit/BCNju0rToyVYvNjqzVON?p=preview
Hope this helps! IMHO it would be much more simpler if you could just ng-repeat over your json data to generate the chatboxes.

handlebars include partial in index

I'm just trying to use handlebars in a project. So I have a PartialNavigation.handlebars and index.handlebars, and I just want to include the PartialNavigation to index with one parameter. I was checking the docs since a long time but didn't found what I want. And all I tried failed...
Thanks per advance!
PokeRwOw
In Handlebars, you can register your partial using the Handlebars.registerPartial() method.
Example:
var partialNavData = '<div id="navigation"></div>';
Handlebars.registerPartial( 'partialNavigation', partialNavData );
Registering multiple partials at once:
var partialNavData = '<div id="navigation"></div>';
var partialFooterData = '<footer>My Footer</footer>';
Handlebars.registerPartial({
partialNavigation: partialNavData,
partialFooter: partialFooterData
});
And finally, including your partials would be done like so:
Your index file:
{{>partialNavigation}}
{{>partialFooter}}
** You can use AJAX to grab the PartialNavigation template if it's too large to put into a string.
Example JSFiddle:
https://jsfiddle.net/richardgirges/Lkrn1qLL/3/
Read these reference docs for more info

Best way to detect that a string passed to $compile in angular is compile-able?

I am building a directive that takes a "message" that can also contain html and nested angular directives. Right now in my directive's contrller I do:
var compiled = $compile(message)($scope);
this.message = $sce.trustAsHtml(compiled.html());
But it won't compile if the message doesn't have a valid start and end tag. I was wondering if there is another approach or if angular has any way to do this without me using a regex.
I would definitely stay away from parsing or validating HTML. jQuery/jqLite already does that for us.
angular.element requires a root element. So, wrap your message in the root element, compile and link, then take the contents out:
link: function(scope, element){
scope.param = "test";
var message = "foo {{param}} bar";
var el = angular.element("<div>").append(message);
var compiled = $compile(el)(scope);
element.append(compiled.contents());
}

HTML templates inside JavaScript file... What am I doing wrong?

What I'm trying to achieve is to store HTML templates for everything that needs to be generated on the fly inside a separate js file (instead of rendering it in the page).
The buildHtml function how its currently setup works fine. Where I'm stuck is what if.. I've another variable inside the template object say 'input3' and its markup is something like <div>(exact markup from commonInput)</div>
I tried using it as input 3 : '<div>' + this.commonInput + '</div>' but it turns out you cannot refer object properties from inside using this (explained here).
I could create 'input3' with full html but for big html chunks this approach is not very useful.
Looking for
solution to this specific problem
reasons if this approach is a bad idea
better alternatives
$j(document).ready(function() {
var namespace = window.namespace || {};
namespace.feature = namespace.appName || {};
namespace.feature.templates = {
input1 : '<div>'+
'<p class="abc">Hey {username}</p>'+
'</div>',
input2 : '<div>'+
'<div class="description">{description}</div>'+
'</div>',
commonInput : '<div class="common">Common code</div>'
};
namespace.feature.module = function() {
var container = $j('#container'),
t = namespace.feature.templates;
var buildHtml = function(type) {
var tmpHtml = t.input1 + t.commonInput + t.input2;
container.append(tmpHtml);
}
var init = function() {
buildHtml();
}
return {
init : init,
};
}();
namespace.feature.module.init();
});
just on the top of my head.
You could write the templates as functions that build strings.
input3 : function(param) {
return '<div>' + param + '</div>'
}
then:
var tmpHTML = t.input1 + t.input2 + t.input3(t.commonInput);
Also, i like to build my own DOM objects when building HTML. and avoid the use of hardcoded strings.
http://mg.to/2006/02/27/easy-dom-creation-for-jquery-and-prototype
I'm not sure what your exact question is, but as far as better alternatives go,
I would suggest using jQuery templates.
Here is a benchmarking page for all the different templating engines and their performance:http://jsperf.com/dom-vs-innerhtml-based-templating
You can look at the revisions to find different combinations of engines as well as run them on different browsers to see speed differences.
Update: The original jquery template project is no longer active. This is the new home for the old project: https://github.com/BorisMoore/jquery-tmpl
I would no longer recommend jquery-templates since there are much better alternatives now. The last time I checked, dustJs by linkedIn fork seems to be the most promising one.

Categories

Resources