Directive displaying Error: [$parse:lexerr] after moving template to templateURL - angularjs

I have a directive working without any issues when the HTML markup is written in the template section of the directive.
I've just moved the HTML markup in a .html file and on load of the page, i am seeing:
Error: [$parse:lexerr] http://errors.angularjs.org/1.3.14/$parse/lexerr?p0=Unexpected%20next%20character%20&p1=s%2016-16%20%5B%5C%5D&p2=option.name%20%3D%3D%3D%20%5C'choices%5C'
Original directive:
app.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
data: "="
},
template: '<p>' +
'<div ng-repeat="select in data.output">' +
'<div ng-if= "select.name === \'choices\'">' +
'<p ng-repeat="choice in select.value"><label><input type="radio" ng-model="data.input[0].value" ng-value="$index" >{{choice}}</label></p>' +
'</div>' +
'</div>' +
'</p>'
}
}
);
New:
app.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
data: "="
},
templateUrl: 'home/mydirective.html'
}
}
);
I page load i can see the http request for mydirective.html and the markup is correct, however the lexerr then appears in the console.

Your html should not contain concatenation that will mess while angular $compile that template. It should be plain html.
mydirective.html
<p>
<div ng-repeat="select in data.output">
<div ng-if="select.name === 'choices'">
<p ng-repeat="choice in select.value">
<label>
<input type="radio" ng-model="data.input[0].value" ng-value="$index">{{choice}}</label>
</p>
</div>
</div>
</p>

In my case this was related to using single quote with a backslash (\') in the .html-file. It was working well when the html was put directly inside the directive using template.
But it threw an error when it was put in a separate .html-file and using templateUrl. So changing ng-class="{\'something\'}" to ng-class="{'something'}" fixed it for me. Hope this will save someone else couple of hours.

Related

Ng-Transclude not working for ng-include

I have created a ng-transclude sample which is not working when I use ng-include inside directive template property.Below are the code I have tried
angular.module("app",[]);
angular.module("app").controller('appController', function ($scope) {
$scope.message="Message from controller";
});
angular.module("app").directive('myFrame', function () {
return {
restrict: 'E',
template : '<div ng-include=\"getTemplateUrl()\"></div>',
controller:function($scope){
$scope.hidden=false;
$scope.close=function(){
$scope.hidden=true;
}
$scope.getTemplateUrl=function(){
//dynamic url
return "frame.html";
}
$scope.message="message from directive";
},
transclude:true,
}
});
angular.module("app").directive('mychildFrame', function () {
return {
restrict: 'E',
templateUrl:"childframe.html",
controller:function($scope){
},
}
});
childFrame.html
<h2>I am from Child</h2>
<div></div>
frame.html
<div class="well" style="width:350px;" ng-hide="hidden">
<div style="float:right;margin-top:-15px">
<i class="glyphicon glyphicon-remove" ng-click="close()" style="cursor:pointer"></i>
</div>
<div ng-transclude>
/*frame content goes here*/
</div>
</div>
index.html
<my-frame>
<mychild-frame></mychild-frame>
</my-frame>
https://plnkr.co/edit/O58voI?p=preview
When I change to the property template to templateUrl="frame.html" it's working fine. But the problem is, this HTML page inside the template is dynamic. So I end up with ng-include.
Could you please provide any possible workaround?
When using the templateUrl property you can also pass it a function to dynamically load a template.
There's no need to put the function in a controller, especially since it doesn't really belong there anyway: the controller is supposed to contain view logic, not a function to load the view itself.
I added the function in the templateUrl instead in your plunkr:
templateUrl : function() {
return 'frame.html';
}
https://plnkr.co/edit/HQHI9hkTojkZFK2Gjxfw?p=preview
As you can see this gives you the desired behavior

AngularJS Directive - Template replacements inside ng-repeat

I have the following directive code (AngularJS 1.4.8). The issue I have is using template replacements inside the ng-repeat.
(function() {
'use strict';
angular.module('app').directive('formGroup', formGroup);
var template = '<div class="form-group">' +
'<label class="control-label">{{label}}</label>' +
'<div data-ng-transclude></div>' +
'<span class="text-danger field-validation-error" data-ng-repeat="errorMessage in {{form}}.$error.{{model}}.messages">' +
'{{errorMessage}}' +
'</span>' +
'</div>';
function formGroup () {
return {
restrict: 'A',
require: '^form',
transclude: true,
replace: true,
scope: {
label: '#',
model: '#'
},
template: template,
link: link
};
}
function link(scope, element, attrs, ctrl) {
scope.form = element.closest('form').attr('name');
}
})();
It is used in HTML in the following way (server-validate is another directive that puts in-line validation errors returned from the server into the ng-repeat in the directive).
<form name="myForm" data-ng-submit="submit()">
<div class="col-lg-4 col-md-6">
<div data-form-group data-label="Memeber number" data-model="memberNumber">
<input type="text"
class="form-control"
data-ng-model="model.memberNumber"
data-server-validate />
</div>
</form>
I want it to produce the following output HTML.
<form name="myForm" data-ng-submit="submit()">
<div class="form-group">
<label class="control-label">Member number</label>
<div>
<input type="text"
class="form-control"
data-ng-model="model.memberNumber"
data-server-validate" />
<span class="text-danger field-validation-error"
data-ng-repeat="errorMessage in myForm.$error.memberNumber.messages">
{{errorMessage}}
</span>
</div>
</div>
</form>
However, it gets the following error.
[Error] [$parse:syntax] Syntax Error: Token '{' invalid key at column 2 of the expression [{{form}}.$error.{{model}}.messages] starting at [{form}}.$error.{{model}}.messages]
Since form controller is injected as required, using the formcontroller, the error messages can be handled like below.
function link(scope, element, attrs, ctrl) {
var unbinder = scope.$watch(function () {return ctrl[scope.model] }, function(value) {
if (!value) return;
scope.errorMessagesList = ctrl[scope.model].$error;
unbinder();
})
}
The complete working sample is availabe in the fiddle:
http://jsfiddle.net/acb3c8n7/5/
In the link function use:
function link(scope, element, attrs, ctrl) {
scope.formString = element.closest('form').attr('name');
scope.form = scope.$eval(scope.formString);
}
In the template use:
ng-repeat="errorMessage in form.$error[model].messages"
The $eval method evaluates the string as an Angular expression and will return a reference to the myForm object.
closest() is not present in the elementargument (check this).
Also you have several errors, by example you don't provide a name attribute in the input, this causes that the scope.myForm object don't recognize the model string provided how a property.
<input type="text" name="memberNumber"
class="form-control"
data-ng-model="model.memberNumber"
data-server-validate" />
In your ng-repeat you shouldn't provide a expression like this:
{{form}}.$error.{{model}}.messages
you should be create a property to attach the form object and the use some like this --> http://codepen.io/gpincheiraa/pen/eZGZPX

AngularJS custom directive - map isolate scope to new child scope

I created a custom directive for bootstrap alerts. My alerts display fine (hard code and data bind). Based on the alert type, I want display a unique header into my alert message based on the returned scope values (success, info, warning, danger). Currently I'm passing the type into <h1> but I don't want those values, they need to be custom.
<!-- data binding example -->
<trux-alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</trux-alert>
<!-- hard coded example -->
<trux-alert close="close" type="warning">This is an important warning message!</trux-alert>
Inside my directive, the scope is isolated using scope: '#' (one-way)
.directive('truxAlert', function() {
return {
scope: {
type: '#',
close: '&'
},
replace: true,
transclude: true,
template:
'<div class="alert" '+
'ng-class="[\'alert-\' + (type || \'warning\'), closeable ? \'alert-dismissible\' : null]" '+
'role="alert">'+
'<button ng-show="closeable" '+
'type="button" class="close" '+
'ng-click="close({$event: $event})" '+
'data-dismiss="alert" aria-label="Close">'+
'<span aria-hidden="true">×</span>'+
'<span class="sr-only">Close</span>'+
'</button>'+
'<h1>{{type}}</h1>'+
'<div ng-transclude></div>'+
'</div>',
link: function (scope, element, attrs) {}
}
});
This would be easier if all my values were pulled through data binding, but I need to allow for manual hard coded option. I know with one-way isolated scopes '#' I can't change these values though DOM manipulation. I can't use '=' or '&' for two-way because the values are strings.
How do I solve for this problem?
My recommendation is have one attribute for the control the open/closed state of the directive's alert and another attribute for the dismiss handler function.
angular.module("myApp").directive('truxAlert', function() {
return {
scope: {
type: '#',
dismissHandler: '&',
title: '#',
open: '='
},
replace: true,
transclude: true,
template:
'<div class="alert" ng-show="open" '+
'ng-class="[\'alert-\' + type]" '+
'role="type">'+
'<button type="button" class="close" '+
'ng-click="truxClose($event)" '+
'data-dismiss="alert" aria-label="Close">'+
'<span aria-hidden="true">×</span>'+
'<span class="sr-only">Close</span>'+
'</button>'+
'<h1>{{title+" "+type}}</h1>'+
'<div ng-transclude></div>'+
'</div>',
link: function (scope, element, attrs) {
console.log("truxAlert linking");
if (!scope.type) { scope.type="warning" }
scope.truxClose = function(event) {
console.log("truxClose "+event);
if (attrs.dismissHandler) {
scope.dismissHandler({$event: event});
return;
}
scope.open = false;
};
}
};
});
The linking function of the directive determines if the dismiss-handler attribute exists and either invokes the dismiss handler or directly closes the alert.
The DEMO PLNKR show the directive being used both with an ng-repeat directive and in a standalone manner.
Maybe, i don't understand you question.
You want to do so jsfiddle?
<form name="ExampleForm" id="ExampleForm">
<span simple="{{val}}">{{val}} - value from data-binding </span>
<br>
<span simple="valueTwo">valueTwo - hard code value</span>
</form>
And js controller
.controller('ExampleController', function($scope, $rootScope, $alert) {
$scope.val = "valueOne";})
And js directive
.directive('simple', function() {
return {
restrinct: 'A',
scope: {
simple: "#"
},
link: function(scope) {
console.log(scope.simple, typeof(scope.simple));
}
}})
UPDATED
angular.module('ExampleApp', ['use', 'ngMessages'])
.controller('ExampleOneController', function($scope) {
$scope.val = "valueOne";
$scope.$on('pass.from.directive', function(event, value) {
$scope.valFromDirective = value;
});
})
.controller('ExampleTwoController', function($scope) {
$scope.val = "valueTwo";
$scope.$on('pass.from.directive', function(event, value) {
$scope.valFromDirective = value;
});
})
.controller('ExampleThreeController', function($scope) {
$scope.val = "valueThree";
$scope.$on('pass.from.directive', function(event, value) {
$scope.valFromDirective = value;
});
})
.directive('simple', function($interval) {
return {
restrinct: 'A',
scope: {
simple: "#"
},
link: function(scope) {
var i = 0;
$interval(function() {
i++;
scope.$emit('pass.from.directive', scope.simple + i);
}, 1000);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular-messages.min.js"></script>
<script src="https://cdn.rawgit.com/Stepan-Kasyanenko/use-form-error/master/src/use-form-error.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleOneController">
<h3>
ExampleOneController
</h3>
<form name="ExampleForm" id="ExampleForm">
<div simple="{{val}}">{{val}} - value from scope </div>
<div>{{valFromDirective}} - value from directive </div>
</form>
</div>
<div ng-controller="ExampleTwoController">
<h3>
ExampleTwoController
</h3>
<form name="ExampleForm" id="ExampleForm">
<div simple="{{val}}">{{val}} - value from scope </div>
<div>{{valFromDirective}} - value from directive </div>
</form>
</div>
<div ng-controller="ExampleThreeController">
<h3>
ExampleThreeController
</h3>
<form name="ExampleForm" id="ExampleForm">
<div simple="{{val}}">{{val}} - value from scope </div>
<div>{{valFromDirective}} - value from directive </div>
</form>
</div>
</div>

ngTransclude doesnt render content correctly

Trying to dynamically pass error/feedback messages to a directive through Transclude. However the transcluded content doesnt redner corrrectly either due to the tag span or the innerForm.inputField.$error.....
EDIT: text between brackets [text] is test that needs to be translated. Dont be confused by that.
Cant find info about limitations about transcluding content. Hope to get some pointers here.
The Directive tag in the DOM
<input-validation-below
place-holder="[E-mail address]"
identifier="forgotten_email"
ng-model="email"
type="email">
<span data-ng-show="innerForm.inputField.$error.required">[E-mail address] [is required]</span>
<span data-ng-show="innerForm.inputField.$error.email">[This is not a valid] [E-mail address]</span>
</input-validation-below>
The Directive
angular.module('inputValidationBelow', []).directive('inputValidationBelow', function () {
return {
scope: {
login: '=ngModel',
placeHolder: '#',
identifier: '#',
type: '#'
},
templateUrl: 'scripts/directives/inputValidationBelow/inputValidationBelow.html',
restrict: 'E',
transclude: true,
require: ['ngModel', '^form'],
link: function (scope, element, attrs, controllers) {
scope.type = attrs.type;
var ngModelController = controllers[0];
ngModelController.$render = function () {
if (typeof ngModelController.$viewValue !== 'undefined') {
scope.login = ngModelController.$viewValue;
}
};
}
};
});
The Directive template
<ng-form name="innerForm"
<label class="full-width" for="inputField" data-ng-class="{ error: innerForm.inputField.$dirty && innerForm.inputField.$invalid }">
<!--[if lt IE 9]><p>{{placeHolder}}:</p></p><![endif]-->
<input class="form full-width" type="{{type}}" name="inputField" placeholder="{{placeHolder}}" id="inputField" data-ng-model="login[identifier]" required />
</label>
<div class="alert alert-input" data-ng-show="innerForm.inputField.$dirty && innerForm.inputField.$invalid">
<span class="alert alert-input" ng-transclude></span>
</div>
</ng-form>
--EDIT--
adding this line to the Link function $compile(element.contents())(scope); "fixes" the issue but gives me an error:
Error: [ngTransclude:orphan] Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element: <span class="alert alert-input" ng-transclude="">

How to move DOM to rootElement in directive (angularjs)

I have a directive which has its template but part of it should always be appended to $rootElement. So wherever user put this directive in app's html part of it must reside as the last child of $rootElement.
This is a special case for transclusion I guess but I wonder if someone has already been done anything like this.
Example - let's say I have my-modal-dialog directive and user wants to use two or more dialogs inside each other. So I'd have this html:
<my-modal-dialog id="dlg1">
<div>...</div>
<my-modal-dialog id="dlg1-1">
...
</my-modal-dialog>
</my-modal-dialog>
But the resulting html should be like this:
<div ng-app>
...
<div id="dlg1" class="modal">
...
</div>
<div id="dlg2" class="modal">
...
</div>
</div>
Any idea of how this could be done?
You could probably use compile method for this. Here is an example in fiddler - http://jsfiddle.net/EyDPh/
myApp.directive("myModelDialog", function () {
return {
replace: true,
restrict: 'E',
scope: {
'value': '='
},
compile:function(element, attrs){
var newElement = angular.element('<div id=' + attrs.id + '> Your content is - ' + element.html() + '</div>');
element.replaceWith(newElement);
}
};
});

Resources