I am using angular-translate on an app. It uses tabs that can switch its contents dynamically with the help of angular-ui, I get the content through a custom service that uses $http.
The template is then sanitised and pushed into an array for later rendering:
var addNewTab = function(template, tabClass) {
var id = $scope.tabs.length + 1;
tabClass = tabClass || '';
template = $sce.trustAsHtml(template);
template = $compile(template)($scope);
$scope.tabs.push({
id: id,
name: 'PLACEHOLDER TITLE',
active: true,
content: template,
tabClass: tabClass
});
};
In the view it is rendered with {{tab.content}}, everything up to this point works quite well, the problem I have is getting angular-translate to translate the newly inserted template.
Inside the template I am using the translate directive to translate things but since the template is outside the $scope of wherever translations happen, its texts are not being rendered.
I thought I could get it to work using $compile but I don't quite get how to use it.
Any ideas would be greatly appreciated.
Related
I want to dynamically add Angular custom Directives, but the directive resulting from $compile(directive) doesn't have the 2-ways binding.
Here's my simplified problem: I am using MapBox, and I want to use Directives for the the markers' popup to show, for example, the markers' title. MapBox wants HTML as a String to put inside the popup, so my idea was to pass a $compiled directive, something like $compile('<myDirective></myDirective>')($scope).html().
It replace the directive with its template, but {{values}} are not solved.
I have something like this to create the popup
map.featureLayer.on('layeradd', function(e)
{
var marker = e.layer;
var popupContent = ctrl.createPopup(marker);
// popupContent should be HTML as String
marker.bindPopup(popupContent);
});
ctrl.createPopup(marker) call a function of the controller, that does:
this.createPopup = function(marker)
{
var popup = "<mapbox-marker-popup"
+" title = "+marker.feature.properties.title
+"</mapbox-marker-popup>";
// should return HTML as String
return ($compile(popup)($scope).html());
}
where mapbox-marker-popup is a directive specified as follow:
/* ===== MARKER POPUP DIRECTIVE=========== */
.directive('mapboxMarkerPopup', function() {
return {
restrict: 'E',
template: [
"<p>{{title}}</p>",
].join(""),
scope:
{
title: '#'
}
}
})
Anyway... mapboxMarkerPopup is not working. title is shown as {{title}}
[UPDATE2 - {{title}} not solved]
Here's the JSFiddle
You need to return the compile angular element instead of returning html of that element. Only returning the html will never carry the angular two way binding. By using compiled object you can keep your binding working.
Code
this.createPopup = function(marker) {
var popup = "<mapbox-marker-popup" +
"title = '" + marker.feature.properties.title + "'"
+ "</mapbox-marker-popup>";
return ($compile(popup)($scope)[0]);
};
Working Fiddle
Update
$compile
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.
Take a look at this link will give you more idea
Question
In AngularJS, is there a way to convert a string template into markup without using scope or a directive?
Explanation
I have a service which allows me to create new angular apps dynamically. It builds up the DOM for the new app, then runs angular.boostrap on the element.
Currently, the DOM is created like this:
var element = document.createElement('div');
element.setAttribute('app', '');
element.setAttribute('size', 'small');
...
element.className = 'app layout--relative';
There are many attributes, classes, child elements, etc, so creating the markup in this way is not ideal. It would be better to use a template.
Normally I would use $compile to convert a string template into markup, but because I have not run angular.bootstrap yet, there is no scope in order to use $compile(template)(scope);
What I have tried
Create a div, then replace the innerHTML with the template string
This works, but all of the attributes and classes on the root element need to be added separately.
var element = document.createElement('div');
element.innerHTML = template;
Remove scope after the template has compiled
This works, but I would prefer to avoid scope altogether:
var scope = $rootScope.$new();
var element = $compile(template)(scope);
scope.$destroy();
You could use the $interpolate service for string substitution like this:
var template = '<div app size="{{size}}" class="{{className}}">' +
'<span>Hello {{name}}!</span>' +
'</div>';
$interpolate(template)({
size: 'small',
className: 'app layout--relative',
name: 'World'
});
and the result would be like this:
<div app size="small" class="app layout--relative">
<span>Hello World!</span>
</div>
Hope this helps.
I'm using kendo ui in my MVC project. So, I have this simple directive that executes when it's not rendered by kendo.
.directive('okok', ['$log', function($log){
return {
link: function (scope, elm) {
$log.log('directive okok!!');
}
};
}])
The directive executes in this line:
<h2 okok>Hello??</h2>
But it does not execute when razor generates the html. Here
#(Html.Kendo().Grid(Model.CoolModel)
.Name("CoolGrid")
.Columns(cols => {
cols.Bound(c => c.StatusDescription).Title("This is my test")
.ClientTemplate("<div okok></div>");
})
/* Mode code :) */
)
Please note the line: .ClientTemplate("<div okok></div>");
Not sure if the output is handle as a string an I have to do something else. Help is appreciated!
I manage this scenario by compiling the element that wraps the grid when the content is bound.
Kendo has an example in its Q&A page where they show how to embed a menu into a grid.cell and the script that enables the submenu has to be executed after the content is render. It kinda lame.
So, I have a function in my controller that compiles an element in the scope.
$scope.rebindElm = function(elm){
$compile(elm)($scope);
}
And in the view a script is executed when kendo.grid.OnContentBound (or something like that)
function bindContent(e){
var elm = angular.element('gridName');
var scope = elm.scope();
if (scope != null) {
scope.rebindElm(elm);
}
}
Yes, it feels like a hack but that's the only way I found to get directives to execute when they are generated with kendo.mvc
Trying to get angular to render a list of directives after a drop-down is toggled in bootstrap.
Ideally, I'll $scope in the main view Ctrl:
$scope.directiveList = ['messages', 'events', 'cart'];
and in the html
<div ng-repeat="directive in directive-list">
<{{directive}}/>
</div>
the results i'm getting are plain text, i.e. . I've tried to modify another helper directive I learned that renders normal html elements fine but doesn't render custom expressions. Here's that directive code:
.directive("notify", function(){
return {
restrict:"EA",
scope:{element:"="},
link:function(scope, iElem) {
var domElement = document.createElement(scope.element);
iElem.append(domElement);
}
};
});
This code renders the element in plain text as well tho it doesn't show in the view. Any help, as always, is much appreciated! Thanks in advance!
What I am trying to do is after clicking on a buddy in the buddy list, load a chat dialog template HTML file without disturbing other elements in current DOM just like chatting in facebook.
My problem is that after loading the html template file the scope variables such as {{contact.jid}} are not properly rendered, and the controller for the dialog is not even called.
How can I force a rerender or a call on the controller so that those variables are properly renderred? Or should I not use the jQuery.load function to do this? I can't figure out any other way.
Thank you.
Code of the controllers:
// Controller for the chat dialog
ctrl.controller('ChatCtrl',function($scope){
$scope.test = "Test"
});
// Controller for the buddy list
// $scope.toggleDialog is called when a buddy is clicked
ctrl.controller('ContactCtrl',function($scope){
$scope.contacts = window.contactList;
$scope.toggleDialog = function(to){
$.("#chatarea").load("chatdialog.html")
};
});
The controller function of chat dialog is not called after loading chatdialog.html which has an attribute of ng-controller, so the {{test}} variable is not set.
You need to wrap your content that will be compiled inside a directive.
This directive receives the variables and the HTML that will be compiled.
You can wrap this directive in the element that you like:
http://plnkr.co/edit/RYVCrlDmgVXT3LszQ9vn?p=preview
For example, given the following html and variables:
// In the controller
$scope.controllerHtml = '<span>{{variables.myVariable}}</span>';
$scope.controllerVariables = {myVariable: 'hello world'};
<!--In the HTML-->
<div compile-me html-to-bind="controllerHtml" variables="controllerVariables"></div>
You will get the following:
<div>
<span>hello world</span>
</div>
You are loading the external HTML via jQuery and Angular has no way of knowing how to use it. There are two ways to solve this issue:
use ngInclude to load the template from the server, angular will load it from the server and compile it for you to use.
continue to use jQuery load the HTML from the server and use the $compile service to teach angular how to use it
I would highly suggest using method #1 to load your external template.
I suppose the:
$.("#chatarea").load("chatdialog.html")
is the jQuery .load, or something similar. I would get the template via ngInclude, checking if test is setted or not; html:
<div id="chatarea" ng-if="test">
<div ng-include="'chatdialog.html'"/>
</div>
controller:
ctrl.controller('ContactCtrl',function($scope){
$scope.contacts = window.contactList;
$scope.test = '';
var callbackFunction = function(data) {
$scope.test = data.test;
};
$scope.toggleDialog = function(to){
AjaxRequestToGetBuddyInfoAndMessages(to, callbackFunction);
};
});
Obviously test will be a more complex object, so the ngIf test will be different (and you will need to take into account the fact that:
$scope.test = data.test
if they are objects, they will lose the reference and have an unwanted behaviour).