I want to create a directive which allow me to generate validation message near input - based on ngMessages(as in example).I have this working HTML example:
<div class="field">
<div class="ui right icon input">
<input type="email" name="email" ng-model="vm.user.email" placeholder="E-mail" required>
<i class="at icon"></i>
</div>
<div ng-messages="vm.signUpForm.email.$error" ng-show="vm.signUpForm.$submitted">
<div ng-messages-include="shared/validation/formErrorMessages.html"> </div>
</div>
</div>
My current directive:
var app = angular.module('app.directives', []);
app.directive('formError', function() {
return {
restrict: 'AE',
replace: 'false',
scope: {
statement: '#',
error: '#'
},
template: '<div ng-messages="error" ng-show="true"><div ng-messages-include="shared/validation/formErrorMessages.html"></div></div>'
};
});
And how I tried to run it:
<div form-error error="{{ vm.signUpForm.email.$error }}" statement="{{ vm.signUpForm.$submitted }}"></div>
It's not working - message won't appear - without any error. On message show I will also want to add class 'error' to 'div.field', but it should be easy.
Any idea how to make this directive work or maybe how to handle this in another, more comfortable way?
You made a a mistake, you should pass attributes to directive with
scope: {
statement: '=',
error: '='
},
# biding is for passing string values, not objects and error is an object, so passing it that way will not work as expected. Of course you could use attr.$observe and JSON.parse, but that is not what you wanna do here.
https://plnkr.co/edit/iRRPqLpmqdQltNjw35Nb?p=preview
Related
I have the following code in my html.
<div id="section">
<div new-exercise ng-repeat="section in sections track by $index">
</div>
</div>
<div class="add_exercise add_exercise_btn">
<a class="add_exercise_link" ng-click="addExercise()">
<span>+ Add an Exercise</span>
</a>
</div>
the method addExercise() adds a new element to the variable sections, hence updating the html with another template (represented by directive new-exercise).
i.e.
$scope.addExercise = function(){
$scope.sections.push({
title: "hello",
content: "fsa"
});
}
The directive new-exercise:
.directive('newExercise', function () {
return {
templateUrl: '/templates/exercise-form.html',
replace: true,
}
})
The template exercise-form.html:
<div class="add_exercise" id="add_exercise_form" data-section-id="{{id}}">
<form class="new_section">
<div class="input-box">
<input id="title" type="text" name="title" class="form-control" value="{{section.title}}" ng-model="section.title">
<label for="title">Exercise Name</label>
<span class="help-block"></span>
<span>{{ section.title }}</span>
</div>
</form>
</div>
I expect the template exercise-form.html to update the value inside input to be hello but the scope is empty.
However, if I remove the directive and add the template html under ng-repeat it works as I expect. I feel that the scope is lost due to directive, but not so sure about the exact reason. Can anyone explain me the reason and how to resolve?
Thanks.
Remove the replace: true in your directive.
Corrected directive given below:
.directive('newExercise', function () {
return {
templateUrl: '/templates/exercise-form.html'
}
})
I am new to angularJs. I am trying to create new directive which contains input element and a button. I want to use this directive to clear input text when button is clicked.
When I use my directive in html I am getting below error :
Error: [$compile:tplrt] Template for directive 'cwClearableInput' must have exactly one root element.
html:
<div class="input-group">
<cw-clearable-input ng-model="attributeName"></cw-clearable-input>
</div>
clearable_input.js:
angular.module('cw-ui').directive('cwClearableInput', function() {
return {
restrict: 'EAC',
require: 'ngModel',
transclude: true,
replace: true,
template: '<input type="text" class="form-control"/><span class="input-group-btn"><button type="button" class="btn" ng-click="" title="Edit"><span class="glyphicon-pencil"></span></button></span>',
controller: function( $scope ) {
}
};
});
I am not able to figure it out how to achieve this.
Well, the error is pretty self-explanatory. Your template needs to have a single root and yours has two. The simplest way to resolve this would be to just wrap the whole thing in a div or a span:
template: '<div><input type="text" class="form-control"/><span class="input-group-btn"><button type="button" class="btn" ng-click="" title="Edit"><span class="glyphicon-pencil"></span></button></span></div>',
Before:
<input type="text" class="form-control"/>
<span class="input-group-btn">
<button type="button" class="btn" ng-click="" title="Edit">
<span class="glyphicon-pencil"></span>
</button>
</span>
After:
<div> <!-- <- one root -->
<input type="text" class="form-control"/>
<span class="input-group-btn">
<button type="button" class="btn" ng-click="" title="Edit">
<span class="glyphicon-pencil"></span>
</button>
</span>
</div>
This error will also occur if the path to the template is incorrect, in which case the error is everything but explanatory.
I was referring the template from within templates/my-parent-template.html with the (incorrect)
template-url="subfolder/my-child-template.html"
I changed this to
template-url="templates/subfolder/my-child-template.html"
which solved it.
Just wrap your template in something:
template: '<div><input type="text" class="form-control"/><span class="input-group-btn"><button type="button" class="btn" ng-click="" title="Edit"><span class="glyphicon-pencil"></span></button></span></div>',
If you don't want to make one root element you can instead set replace to false. In your directive you are setting it to true, which replaces the directive tag "cw-clearable-input" with the root element of your directive.
If you have a comment in your template alongside the root element in the directive template, you will see the same error.
Eg. If your directive template has HTML like this (with "replace" set to true in the directive JS), you will see the same error
<!-- Some Comment -->
<div>
<p>Hello</p>
</div>
So to resolve this in scenarios where you want to retain the comments you will need to move the comment so that it is inside the template root element.
<div>
<!-- Some Comment -->
<p>Hello</p>
</div>
When none of this worked for me, prefixing a / to the template path fixed the issue in my case.
function bsLoginModal() {
return {
restrict: 'A',
templateUrl:'/app/directives/bootstrap-login-modal/template.html',
link: linkFunction,
controller: SignInCtrl,
controllerAs: 'vm'
}
}
instead of
templateUrl:'app/directives/bootstrap-login-modal/template.html
Good Luck.
Check your template or templateUrl
templateUrl: 'some/url/here.html'
template: '<div>HTML directly goes here</div>'
My problem was that webpack's HtmlWebpackPlugin was appending a script tag to the directive content. So angular was seeing:
<div>stuff</div>
<script type="text/javascript" src="directives/my-directive"></script>
The solution is to use HtmlWebpackPlugin's inject option in your webpack.config.js:
new HtmlWebpackPlugin({
template: './src/my-directive.html',
filename: './src/my-directive.html',
inject: false
}),
I know it might be a simple question, but I'm frustrated here, and I can't make it work. I'm new to AngularJS, and I'm trying to implement a modal dialog (or find one) with these conditions:
Dialog content might come from anywhere—a string template, a script template, or a template from a URL
Dialog title and actions will come from the caller, not the callee. In other words, the parent scope decides the title and which action buttons should exist in the modal dialog (many dialogs I found encapsulate the title and action buttons in the template itself, for example this one)
Content of the template should be totally independent from parent scope (caller). In fact, it might not even be written in AngularJS. It might use jQuery.
In case the loaded template is in AngularJS, it should encapsulate its controller. For example, ng-include doesn't like <script> tags.
There is a workaround for it (here, here and here) but the idea of decorating a script tag with text/javascript-lazy is very smelly and dirty, let alone that I want the content HTML to be self-contained and executable in case it's not loaded as the content of an AngularJS modal dialog.
Communication between the parent scope and the content should be done via a common contract (JavaScript events come to my mind)
I've tried ngDialog, but the problem is that the container should pass the controller to the loaded template. That's not what I want.
In Bootstrap dialog also it seems that you have to pass the controller from the parent scope to the dialog content. This breaks the very notion of encapsulation. It's not desirable. Also, it's dependent on dialog result, which is not desirable either.
I recommend use Angular-UI library. You can easy create any dialog a-la "Twitter Bootstrap":
Include js in your page head.
<script src="/desktop/libs/angular-bootstrap/ui-bootstrap.js"></script>
<script src="/desktop/libs/angular-bootstrap/ui-bootstrap-tpls.js}"></script>
Include modules at app initialization.
var Application = A.module('MyApp', [ 'ui.bootstrap', 'ui.bootstrap.modal' ]);
Inject in jour controller $modal:
(function (A){
"use strict";
A.module("MyApp").controller("OpenDlg", [ "$scope", "$modal", function($scope, $modal){
$scope.openDlg = function(){
$modal.open({
controller : 'CategoryAddController',
templateUrl : '/admindesktop/templates/category/add/'
}).result.then(function(modalResult){
console.log(modalResult);
});
};
} ]);
}(this.angular));
For example, simple template for dialog:
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title text-center">Создание новой категории</h4>
</div>
<form class="modal-body form-horizontal" name="categoryForm">
<div class="form-group">
<label for="name" class="control-label col-xs-3">Название</label>
<div class="col-xs-9">
<input name='name' type="text" class="form-control" ng-model="category.name" maxlength=50 required ng-required="true"/>
</div>
<div class="row has-error" ng-show="errors.name">
<p ng-repeat="error in errors.name">{{ error }}</p>
</div>
</div>
<div class="container-fluid" ng-show="errors.length > 0">
<div class="row">
<p class="text-center text-danger" ng-repeat="error in errors">{{ error }}</p>
</div>
</div>
</form>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="save()" ng-disabled="categoryForm.$invalid">Сохранить</button>
<button class="btn btn-default" ng-click="cancel()">Отмена</button>
</div>
</div>
Main: controller for modal window:
(function(A) {
"use strict";
var app = A.module('MyApp');
app.controller('CategoryAddController', [ '$scope', '$modalInstance', 'Category', 'growl', function($scope, $modalInstance, Category, growl) {
$scope.save = function() {
var category = new Category($scope.category);
category.$save(function() {
growl.success('Категория успешно создана');
$modalInstance.close(true);
}, function(response) {
$scope.errors = response.data;
});
};
$scope.cancel = function() {
$modalInstance.close(false);
};
} ]);
}(this.angular));
I use Service for data changing between modal controller and parent scope:
(function(A){
"use strict";
A.module("MyApp").service('Storage', function(){
return {
storedData: undefined
};
});
}(this.angular));
In parent scope:
Storage.storedData = ...; //For example, selected row of table
In modal controller:
$scope.item = Storage.storedData; //Selected row of table
Also angular have special module type, value.
I'm trying to make an planification app with AngularJS. The main feature is to create a Task.
I want to put the source of the task in a directive :
<section id="runningTasks" ng-controller='RunningTaskCtrl as ctrl'>
<task class="task" ng-repeat='task in ctrl.tasks'></task>
</section>
For each task, I add it in the div.
Here is my directive definition :
.directive('task', function(){
return {
restrict: 'EA',
replace:'true',
templateUrl: '/Planificator/directives/task/task.html',
link : function(scope, element, attrs){
var date = $(element).find(".datepicker");
date.datepicker();
date.datepicker("option", "dateFormat", "dd-mm-yy");
}
};
})
And the content of task.html :
<div class="task" ng-click="task.editting = true" task>
<h1>{{ task.title }}</h1>
<p>
{{ task.comment }}
</p>
<div class="edit-task" ng-show="task.editting">
<form ng-submit="ctrl.propose(task)">
... form stuff ...
</form>
</div>
</div>
My problem is when I run my page, I get an error :
Error: [$compile:multidir] http://errors.angularjs.org/1.2.26/$compile/multidir?p0=task&p1=task&p2=tem…3D%20true%22%20task%3D%22%22%20ng-repeat%3D%22task%20in%20ctrl.tasks%22%3E
at Error (native)
(the clean link : Angular error generator)
I already had this problem before and I just put the content of the template in the ngRepeat and doesn't think anymore, but this time I would like to be things in the good way.
Thank you for the answers !
Your problem is:
<div class="task" ng-click="task.editting = true" task>
Since this is part of the template created from the task directive you are trying to add the task directive over and over.
Change to:
<div class="task" ng-click="task.editting = true">
I am using directives to try to replace some of the often-reoccurring template code that i must write with something simpler.
lets say I have the following original markup:
<!-- section with repeating stuff in it -->
<div some-attributes etc="etc" very-long-tag="true">
<p class="lead">Some description text</p>
<div class="row section short" ng-repeat="row in things">
<div class="col-sm-6 col-md-4" ng-repeat="app in row.col">
<div class="thumbnail">
<img ng-src="im/things/{{app.image}}" alt="..." class="img-circle" width="250">
<div class="caption">
<h3>{{app.name}}</h3>
<p>{{app.desc}}</p>
</div>
</div>
</div>
</div>
</div>
and I want to simplify it by doing something like this:
<!-- section with repeating stuff in it -->
<xx title="Some description text">
<!-- this innerHTML gets passed to the directive -->
<div class="row section short" ng-repeat="row in things">
<div class="col-sm-6 col-md-4" ng-repeat="app in row.col">
<div class="thumbnail">
<img ng-src="im/things/{{app.image}}" alt="..." class="img-circle" width="250">
<div class="caption">
<h3>{{app.name}}</h3>
<p>{{app.desc}}</p>
</div>
</div>
</div>
</div>
<!-- end of innerHTML -->
</xx>
...where there are a several attributes that can be used to shorten the overall block, the directive is currently written this way:
_d.directive('xx', function() {
return {
scope: {
'color': '=',
'option': '=',
'title': '=',
'image': '=',
'image-pos': '=',
'image-size': '='
},
restrict: 'E',
transclude: false,
template: function(element, scope) {
var inside = 'x';
var content = element[0].innerHTML;
var title = scope.title;
var color = scope.color ? 'style="background-color: '+scope.color+'"' : "";
var title = scope.title ? '<h2 class="centertext marginBottom20">'+scope.title+'</h2>' : '';
return ['<div class="section row short" '+color+' ng-transclude>',
title,
content, //this may contain {{template code}}, but it always gets omitted
'</div>'
].join("\n");
},
};
});
The problem is that the existing HTML always gets omitted if it contains any {{angular template code}}.
How do I write the directive so that it still honors the template code?
Ive successfully fixed the issue with the directive, but it took several steps.
Use the correct scope properties. instead of using '=', I used '#'
That was based on the following link: What is the difference between '#' and '=' in directive scope in AngularJS?
The thing to note about scope isolation using #, =, and & affects the way you must refer to the variable in the template. for example, using = means that I would refer the variable without brackets while using # would refer to the variable with {{brackets}}.
Like I mentioned in the first point, after adjusting the scope properties, i needed to go back and refer to the variables in the correct way depending on how the scope was defined.
ng-transclude when used with {...transclude: true,...} requires that I actually put a container somewhere in the template for that transcluded content. Here's an example of that:
return ['<div class="section row short" '+color+' ng-transclude>',
title,
'<div ng-transclude>', //this is the container for the original innerHTML, transcluded
content, //this may contain {{template code}}, and gets transcluded
'</div>
'</div>'
].join("\n");
Only then did the directive work as expected. Also, props to #rob for providing me with this introductory link, https://egghead.io/lessons/angularjs-components-and-containers.