I'm trying to convert a string date so that it works on a html input with the type set to 'date'.
So, I have the following angular app:
(function() {
var app = angular.module('test', []);
app.controller('MainCtrl', function($scope) {
$scope.load = function() {
$scope.message='2017-12-23T00:00:00Z';
};
});
app.directive('convertDate', function() {
return {
restrict: 'A',
scope: {
ngModel: '='
},
link: function (scope) {
console.log(scope);
console.log(scope.ngModel);
if (scope.ngModel) scope.ngModel = new Date(scope.ngModel);
}
};
});
})();
Then my html is as follows:
<div ng-controller='MainCtrl'>
<input type="date" convert-date ng-model="message">
<button ng-click="load()">load</button>
</div>
When I click on the load button I get the following error:
Error: [ngModel:datefmt] http://errors.angularjs.org/1.6.4/ngModel/datefmt?p0=2017-12-23T00%3A00%3A00Z
I understand the error is because it's a string and I need a date, which its the reason for my directive.
But even with the directive I still get the error.
What am I missing?
Thanks
Colin
You can change your directive to following:
angular.module('app').directive('convertDate', function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
if (!ctrl) return;
ctrl.$parsers.push(function(date) {
if (angular.isDate(date))
return new Date(date);
})
}
}
})
take a look at this working plunkr without error
https://plnkr.co/edit/8aSR1dlsRfDMrM7GfQQa?p=preview
It is because you are using same variable in ng-model for converting. So it encounters an error before your directive converts it.
According to me, you should convert it first and then assign to the ng-model variable in your controller.
Like this,
(function() {
var app = angular.module('test', []);
app.controller('MainCtrl', function($scope) {
$scope.load = function() {
var dateString = '2017-12-23T00:00:00Z';
$scope.message=new Date(dateString);
};
});
})();
No need to use directive
Related
I am currently working on integrating a plugin in my angular application and I'm trying to convert it into a directive. I have fiddled around with the events and methods of the plugin but failed to get the desired results. here's my code:
HTML
<div class="input-daterange datepicker full" rangepicker ng-model="packer.selected.date">
<i class="fa fa-calendar"></i>
<div class="inputs datepicker">
<input
ng-model="packer.selected.date.start"
name="start"
value="<% packer.initData.dateStart %>">
<span class="add-on">-</span>
<input
ng-model="packer.selected.date.end"
name="end"
value="<% packer.initData.dateStart %>">
</div>
</div>
Javascript:
Application.directive('rangepicker', function() {
return {
restrict: 'A',
link: function(scope, element, attrs, ngModel) {
$(element).datepicker({
format: 'yyyy-mm-dd'
});
$(element).on('changeDate', function(){
/*
$(element).find('input').each(function(){
$(this).trigger('input');
}) */
})
}
};
});
Application.controller('PackingStatisticsController', ['$scope', '$http', 'initData', function($scope, $http, initData) {
var packer = this;
packer.initData = initData;
packer.selected = {
date : {
start : "",
end : ""
},
user : ""
}
packer.log = function()
{
console.log(packer.selected);
}
}]);
I've read anything I thought was relevant to my issue but I haven't managed to shed the veil of confusion. The commented code is supposed to trigger the input value change event which I hoped would update the model. I fail to understand where the model I designate in the html meets my directive's data.
https://bootstrap-datepicker.readthedocs.org/en/latest/index.html this is the plugin I'm working with.
you can pass as an attribute the model you want to modify from the controller's scope like this (look at the scope property):
Application.directive('rangepicker', function() {
return {
restrict: 'A',
scope : {
model : '='
}
link: function(scope, element, attrs, ngModel) {
$(element).datepicker({
format: 'yyyy-mm-dd'
});
$(element).on('changeDate', function(){
/*
$(element).find('input').each(function(){
$(this).trigger('input');
}) */
})
}
};
});
and in the html:
<div class="input-daterange datepicker full" model="myModelVar" rangepicker ng-model="packer.selected.date">
now the myModeVar is two way data bindable. once you change it in the directive it changes in the controller's scope.
in the controller:
Application.controller('PackingStatisticsController', ['$scope', '$http', 'initData', function($scope, $http, initData) {
$scope.myModelVar = ...;
}]);
Alas, i have done it!
Application.directive('rangepicker', function() {
return {
restrict: 'A',
require: 'ngModel', // added the ngmodel requirement
scope : {
ngModel: "="
}, // also added this, which if i understood well
// makes the 2 way data binding possible
link: function(scope, element, attrs) {
$(element).datepicker({
format: 'yyyy-mm-dd'
});
$(element).on('changeDate', function(){
var values = $(element).find('input');
var interval = {
start : values[0].value,
end : values[1].value
}
scope.$apply(function(){
// and here i update the given model scope (packer.selected.data)
scope.ngModel = interval;
});
})
}
};
});
I am trying to implement the directive, in the directive, I want to $eval the values which contains the function name and parameter value:
Html page:
<select mydirective="action('pValue')">
AngularJS directive code:
app.directive('mydirective', function ($timeout) {
return {
restrict: 'A',
link: function ($scope, element, attr) {
$timeout(function () {
$scope.$eval(attr.mydirective);
});
}
}
What I am expected is it will invoke the action function define in scope and pass the pValue as function parameter. How can I make it work please?
What you want happen automatically, the function will invoke with the value, this is the purpose of eval:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.action = function(val) {
alert(val);
}
});
app.directive('mydirective', function($timeout) {
return {
restrict: 'A',
link: function($scope, element, attr) {
$timeout(function() {
$scope.$eval(attr.mydirective);
});
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.21/angular.min.js"></script>
<div ng-app="plunker" ng-controller="MainCtrl">
<select mydirective="action('pValue')"></select>
</div>
For those whom looking for a way to pass $event info to custom directive method see example below:
TEMPLATE:
<div on-touch-end="onTouchEnd( 'some data' )">
TOUCH ME!
</div>
CONTROLLER:
$scope.onTouchEnd = function( data ) {
console.log("onTouchEnd event with data", data, event );
};
DIRECTIVE:
.directive('onTouchEnd', function() {
return {
restrict : 'A',
link : function( $scope, $element, $attr ) {
$element.on('touchend', function( event ) {
$scope.$apply(function() {
$scope.$eval( $attr.onTouchEnd );
});
});
}
}
})
I am facing a small problem during the compilation of html code in angularjs. Here is the brief description of my problem :
$scope.report= reportdata;
reportdata is the html code that contains angularcontents like : {{name}} , {{firstname}} etc.
so, I am searching for a function that can compile the above html in my controller just like this :
$scope.compiledReportdata = function() {
$scope.compildeHtml = somefunction(reportdata);
}
Is there any function that can do the trick for me , Please suggest.
This is what i have tried i works for HTML but not for Controller
angular.module('myapp')
.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
ele.bind('blur', function () {
scope.$apply(attrs.uiBlur);
debugger
});
scope.$watch(attrs.dynamic, function (html) {
ele.html(html);
var data1 = ele.html(html);
var data2 = $compile(ele.contents())(scope);
$compile(ele.contents())(scope);
});
}
};
});
You can use the $interpolate service in the controller to interpolate the string...
var app = angular.module('app', ['ngSanitize']);
app.controller('controller', function ($scope, $interpolate) {
$scope.name = 'Costanza';
$scope.firstname = 'George';
$scope.report = '<strong>{{name}}</strong> , {{firstname}}';
$scope.compiledReportdata = function () {
return $interpolate($scope.report)($scope);
};
});
And you can use ngBindHtml with ngSanitize to display it...
<div ng-app="app" ng-controller="controller">
<div ng-bind-html="compiledReportdata()"></div>
</div>
JSFiddle
I'm adding the attribute ng-bind='data' to an element through a directive
myApp.directive('myDiv', function() {
return {
restrict: 'E',
link: function($scope, element, attrs) {
element.html('<div ng-bind="data">me</div>');
} }; });
function MyCtrl($scope) {
$('#click').click(function() {
$scope.data = 'change';
}); }
but the ng-bind isn't working as expected.
http://jsfiddle.net/HB7LU/3427/
To answer the main question your issue here is that if you want to include bindings in your template you need to compile the element. The syntax for that is something like:
$compile(angular.element("my html"))(scope)
In your case that actually ends up looking like:
myApp.directive('myDiv', function($compile) {
return {
restrict: 'E',
link: function(scope, element, attrs) {
// here adding the ng-bind dynamically
element.html($compile(angular.element('<div ng-bind="data">me</div>'))(scope));
}
};
});
To see it working checkout the updated fiddle here: http://jsfiddle.net/CC8BK/.
One other note is you are using jQuery's "click" event to change scope values. When working with angular you need to start by trying not to use jQuery and instead using the angular directives for whatever you can. In your case ng-click is the directive you should be using. I inserted this in your html so you could see what it would look like.
Hope this puts you on the right track. Best of luck!
As #drew_w said you have to compile element using $compile if you need to apply from link,
or else you can use template in directure like
template: '<div ng-bind="data"></div>'
I mostly prefer template
Also don't use jquery function like
$('#click').click(function() {
$scope.data = 'change';
});
instead you can use
$scope.change = function()
{
$scope.data = 'change';
}
or
ng-click="data = 'change'"
as #drew_w said
Take a look the full code
Working demo
html
<div ng-controller="MyCtrl">Hello, {{name}}!
<button id='click' ng-click="change()">click to 'change'</button>
<my-div>watch, this doesn't change!!!???</my-div>
</div>
script
var myApp = angular.module('myApp', []);
myApp.directive('myDiv', function ($compile) {
return {
restrict: 'E',
template:'<div ng-bind="data"></div>'
};
});
myApp.controller('MyCtrl', function ($scope) {
$scope.data = "me";
$scope.name = 'Superhero';
$scope.change = function () {
$scope.data = 'change';
}
});
here's a variation of the above answer using the Template property and using a click function:
myApp.directive('myDiv', function() {
return {
restrict: 'E',
template:'<div ng-bind="data"></div> me'
};
});
and on the controller:
$scope.click = function() {
$scope.data = 'change';
};
and on the View
<button ng-click="click()">click to 'change'</button>
http://jsfiddle.net/HB7LU/3446/
I'm trying to use prettyprint plugin for my angularjs app.
But cannot make it works. I create a simple directive and call method prettyPrint(), but the code is not formatted.
FIDDLE: http://jsfiddle.net/Tropicalista/yAv4f/2/
App.directive('test', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
$(element).prettyPrint()
}
};
});
I modified your code and i'll update here:
http://jsfiddle.net/yAv4f/6/
html:
<div ng-app="Knob" ng-controller="myCtrl">
<pre class="prettyprint linemus"></pre>
<pre class="prettyprint linemus"><!DOCTYPE html><html lang="en"></html></pre>
</div>
javascript:
var App = angular.module('Knob', []);
App.controller('myCtrl', function($scope) {
$scope.dom = '<!DOCTYPE html><html lang="en"></html>'
})
App.directive('prettyprint', function() {
return {
restrict: 'C',
link: function postLink(scope, element, attrs) {
element.html(prettyPrintOne(scope.dom));
}
};
});
Basically, you need to use the file prettify.js to control the execution of the prettify() function, with prettyPrintOne() you can execute it in a specific html text.
And to simplify the use of the directive, like prettify stlyle, i'll suggest restric to 'C' a class and change the the directive name to 'prettyprint'
I've expanded on the previous answers and created a jsfiddle with a working directive that responds in realtime to model changes:
http://jsfiddle.net/smithkl42/cwrgLd0L/27/
HTML:
<div ng-app="prettifyTest" ng-controller="myCtrl">
<div>
<input type="text" ng-model="organization.message" />
</div>
<prettify target="organization"><pre><code class="prettyprint">console.log('{{target.message}}');
</code>
</pre>
</prettify>
</div>
JS:
var App = angular.module('prettifyTest', []);
App.controller('myCtrl', function ($scope) {
$scope.organization = {
message: 'Hello, world!'
};
});
App.directive('prettify', ['$compile', '$timeout', function ($compile, $timeout) {
return {
restrict: 'E',
scope: {
target: '='
},
link: function (scope, element, attrs) {
var template = element.html();
var templateFn = $compile(template);
var update = function(){
$timeout(function () {
var compiled = templateFn(scope).html();
var prettified = prettyPrintOne(compiled);
element.html(prettified);
}, 0);
}
scope.$watch('target', function () {
update();
}, true);
update();
}
};
}]);
h/t to #DanielSchaffer (see Template always compiles with old scope value in directive).
Angular already has this filter built-in for JSON:
<pre>
{{data | json}}
</pre>
If you want to make your own directive, you can use the JSON object directly:
app.filter('prettyJSON', function () {
function syntaxHighlight(json) {
return JSON ? JSON.stringify(json, null, ' ') : 'your browser doesnt support JSON so cant pretty print';
}
return syntaxHighlight;
});
With markup
<pre>
{{data | prettyJSON}}
</pre>
I would like to make a small addition to the directive by #carlosmantilla
You can achieve the same thing without creating the scope variable. I have added this correction on github
This should work properly I assume.
http://jsfiddle.net/yAv4f/143/
var App = angular.module('Knob', []);
App.controller('myCtrl', function($scope) {
$scope.text = "function f1(){int a;}";
})
function replaceText(str)
{
var str1 = String(str);
return str1.replace(/\n/g,"<br/>");
}
app.directive('prettyprint', function() {
return {
restrict: 'C',
link: function postLink(scope, element, attrs) {
element.html(prettyPrintOne(replaceText(element.html()),'',true));
}
};
});
I struggled with this issue for quite a while and wanted to chime in here, albeit much later than everyone else (for real though, who's still using AngularJS in late 2017? This guy.) My specific use-case was where I have code (xml) being dynamically loaded on the page which needed to be pretty printed over and over again.
This directive will take in your code as an attribute, remove the prettyprinted class that's added to the element right after you run prettyPrint(). It will watch for changes on the inputted code from the parent's scope and run the code again when changes occur.
Only dependency is that you have Google's code-prettify. I had it self-hosted, hence the PR.prettyPrint() (as instructed in the docs as of sept 2017).
The directive fully encapsulates the needed Google code-prettify functionality for dynamic content.
angular.module('acrSelect.portal.directives')
.directive('prettyPrint', ['$timeout', function($timeout) {
return {
restrict: 'E',
scope: {
'code': '=',
},
template: '<pre ng-class="{prettyprint: code}">{{ code }}</pre>',
link: function (scope, element, attr) {
scope.$watch('code',function(){
$timeout(function() {
//DOM has finished rendering
PR.prettyPrint();
element.find(".prettyprint").removeClass("prettyprinted");
});
});
}
}
}
]);
The html in the parent template might look
<pretty-print code="selectedCode" ng-show="codeIsSelected"></pretty-print>
Hope this helps another poor soul!