I try to put an HTML code including Angular material input component (which is included in my project) in ng-sweet-alert dialog:
var strVar="";
strVar += "<div> <md-input-container style=\"padding-left:0px;\" flex=\"\">
<label>Titel<\/label> <input ng-model=\"WBQuery.Titel\">
<\/md-input-container> <\/div>";
swal({
title: 'HTML example',
html: strVar });
}
But what is shown is just the standard input field of HTML. Is there any (hacking) way to use angular material components in that dialog?
I've not tried it but you can try this. After you have invoked the swal function, try the following:
(Assuming you are doing it in a directive or a controller)
var element = angular.element(document.querySelector(".sweet-alert"));
$compile(element.contents())($scope);
If that doesn't work, try to put the above code in a $timeout service:
$timeout(function() {
var element = angular.element(document.querySelector(".sweet-alert"));
$compile(element.contents())($scope);
}, 2000);
Related
I want to render a list of widgets on a page from my controller. Each widget has its own render function that returns a safe HTML string.
My first part of my ng-repeat (outside layer) looks like this:
$scope.renderWidgets = function() {
var html = "";
html += "<div ng-repeat='widget in widgets'>";
html += "<div ng-bind-html='widget.render()'></div>"
html += "</div>";
return $sce.trustAsHtml(html);
}
As you can see I have ng-bind-html inside my ng-repeat which is calling a function named .render() located inside widget:
this.render = function() {
var html = "";
html += "<div ng-repeat='choice in widget.choices'>";
html += "{{choice.name}}";
html += "</div>";
return $sce.trustAsHtml(html);
}
I also have a directive that I use to $compile the above HTML string into AngularJS.
If I run the above code the widget.render() function gets called just fine but the output on the page will look like this: {{choice.name}} and not the value inside choice.name.
As you can see I am trying to do another ng-repeat inside ng-bind-html with the widget object from my first ng-repeat.
Is this even possible what I am trying to do here (I am new to AngularJS)? If yes then what am I doing wrong? Or is there another way to resolve my problem?
I am using the latest AngularJS (v. 1.7.9).
UPDATE
I think I know the issue. When I call my first function $scope.renderWidgets() the returned HTML will be automatically compiled(the first ng-repeat) at which point the ng-bind-html gets triggered returning a non-compiled HTML string. Now I have to figure out a way to $compile the returned HTML from my ng-bind-html directive.
Am I on the right track?
The ng-bind-html directive only binds HTML. It does not compile HTML. This was a deliberate decision by the AngularJS team to avoid security problems.
You state that you have a directive that compiles HTML. Let's assume the directive is:1
app.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamic, function(html) {
ele.html(html);
$compile(ele.contents())(scope);
});
}
};
});
Then use it in your nested code:
$scope.renderWidgets = function() {
var html = "";
html += "<div ng-repeat='widget in widgets'>";
̶h̶t̶m̶l̶ ̶+̶=̶ ̶"̶<̶d̶i̶v̶ ̶n̶g̶-̶b̶i̶n̶d̶-̶h̶t̶m̶l̶=̶'̶w̶i̶d̶g̶e̶t̶.̶r̶e̶n̶d̶e̶r̶(̶)̶'̶>̶<̶/̶d̶i̶v̶>̶"̶
html += "<div dynamic='widget.render()'></div>"
html += "</div>";
return $sce.trustAsHtml(html);
}
For more information, see
Compiling dynamic HTML strings from database — this answer
I'm creating inputs inside a form dynamically. I created this directive for the purpose:
// Generate inputs on the fly using BE data.
.directive('inputGenerator', ['$compile', function ($compile) {
return {
restrict: 'E',
scope: {
id: '#',
type: '#',
name: '#',
attributes: '=',
ngModel: '=',
ngDisabled: '='
},
link: function (scope, iElement, iAttrs) {
// Get attributes
var id = iAttrs.id,
type = iAttrs.type,
name = iAttrs.name;
scope.ngModel[name] = {};
var extended_attributes = {
"type": type,
"id": id,
"data-ng-model": 'ngModel.' + name + '[\'value\']',
"name": name,
"data-ng-disabled": "ngDisabled"
};
if ( ! scope.attributes) {
scope.attributes = {};
}
// Append extra attributes to the object
angular.extend(scope.attributes, extended_attributes);
// Generate input
var input = '<input ';
angular.forEach(scope.attributes, function (value, key) {
input += key + '="' + value + '" ';
});
input += '/>';
// Compile input element using current scope (directive) variables
var compiledElement = $compile(input)(scope);
// Set the file selected name as the model value
compiledElement.on('change', function () {
if (this.files && this.files[0].name) {
var that = this;
scope.$apply(function () {
scope.ngModel[name] = {};
scope.ngModel[name]['value'] = that.files[0].name;
});
}
});
// Replace directive element with generated input
iElement.replaceWith(compiledElement);
}
};
}]);
This html line will trigger the directive:
<input-generator data-name="{{ item.name }}" data-ng-model="inputs.sources" data-attributes="item.attrs" data-type="{{ item.type }}" data-id="inputFile_{{ $index }}" data-ng-disabled="inputs.sources[item.name].selected" />
I'm running on Angular 1.4.3.
Problem
The model and pretty much everything works fine in the directive, but for some reason the form remains valid when the input added is invalid as you can see in this image.
I already tried:
Any of the Angular features of form validation works
I debugged Angular and seems to be that the input attached to the form is different from the input compiled inside the directive.
I already called formName.$setPristine() after each input was created, but it didn't work.
I couldn't access the form from the directive, but I think is not a good idea either.
I already wrapped the input with a ng-form tag, but nothing useful comes out of that.
I tried to use the directive compile method, but this is just triggered once when the app loads and I've a select input that loads different inputs on change.
Any help on this is much appreciated! :)
Thank you to everyone for contribute anyways!!
You should definitely take a look at my Angular-Validation Directive/Service. It as a ton of features and I also support dynamic inputs validation, you can also pass an isolated scope which helps if you want to not only have dynamic inputs but also have dynamic forms, also good to use inside a modal window.
For example, let's take this example being a dynamic form and inputs defined in the Controller:
$scope.items.item1.fields = [
{
name: 'firstName',
label:'Enter First Name',
validation:"required"
},
{
name: 'lastName',
label: 'Enter Last Name',
validation:"required"
}
];
$scope.items.item2 = {
heading:"Item2",
formName:"Form2"
};
$scope.items.item2.fields = [
{
name: 'email',
label:'Enter Email Id',
validation:"required"
},
{
name: 'phoneNo',
label: 'Enter Phone Number',
validation:"required"
}
];
It will bind the validation to the elements and if you want to easily check for the form validity directly from the Controller, simply use this
var myValidation = new validationService({ isolatedScope: $scope });
function saveData() {
if(myValidation.checkFormValidity($scope.Form1)) {
alert('all good');
}
}
You can also use interpolation like so
<input class="form-control" type="text" name="if1"
ng-model="vm.model.if1"
validation="{{vm.isRequired}}" />
Or using a radio/checkbox to enable/disable a field that you still want to validate when it becomes enable:
ON <input type="radio" ng-model="vm.disableInput4" value="on" ng-init="vm.disableInput4 = 'on'">
OFF <input type="radio" ng-model="vm.disableInput4" value="off">
<input type="text" name="input4"
ng-model="vm.input4"
validation="alpha_dash|min_len:2|required"
ng-disabled="vm.disableInput4 == 'on'" />
It really as a lot of features, and is available on both Bower and NuGet (under the tag name of angular-validation-ghiscoding). So please take a look at my library Angular-Validation and a live demo on PLUNKER.
It's loaded with features (custom Regex validators, AJAX remote validation, validation summary, alternate text errors, validation on the fly with the Service, etc...). So make sure to check the Wiki Documentation as well... and finally, it's fully tested with Protractor (over 1500 assertions), so don't be afraid of using in production.
Please note that I am the author of this library
I ran into this issue as well with Angular v1.5.9. The main issue here is that you are compiling the HTML template before it is in the DOM, so Angular doesn't know it's part of the form. If you add the HTML first, then compile, Angular will see your input as a form child, and it will be used in the form validation.
See similar answer to Form Validation and fields added with $compile
Don't do:
var myCompiledString = $compile(myHtmlString)(scope);
// ...
element.replaceWith(myCompiledString);
Do this instead:
var myElement = angular.element(myHtmlString)
element.replaceWith(myElement) // put in DOM
$compile(myElement)(scope) // $compile after myElement in DOM
Note: I swapped the more conventional element for OP's iElement, which is a jQLite reference of the directive's HTML element
you need to use ng-form directive as a wrapper for your input.
you can see an example of this here
but it works for me. You can pass the form reference to the directive and use it directly.
in the code below, the scope.form is used to know the general state of the form, and the scope.name to access the input field state.
< ng-form name="{{name}}" ng-class="{error: this[name][name].$invalid && form.$submitted}" >
i hope it helps
you need to set name of control dynamic and use this dynamic name for form validation. in the following e.g. you see the dynamic name and id of control and used for the validation of angular (using ng-massages)
more details see http://www.advancesharp.com/blog/1208/dynamic-angular-forms-validation-in-ngrepeat-with-ngmessage-and-live-demo
Field Num Is Required.
For a project I'm working on, I created a simplified version of the UI Bootstrap Calendar widget.
Plunker of my Simplified Calendar, and how I'm using it, is here.
One interesting aspect of the UI Bootstrap calendar, is that even though it goes onto a input[text], it still produces a date validation in the $error dictionary for a form controller, just as if I had specified an input[date] element in my DOM.
However, there's a catch with numerous sub-catches. One thing you'll notice right away in my plunker's DOM is that I've specified error spans for times when the given date fields are not actually dates (try entering something ridiculous like 'cat' for a value!) If you enter something that isn't a date, those should appear, but they don't.
I've tried a few things to expose the markup being created to the name field of the parent:
$transclude set to false, such that the <calendar></calendar> tags get replaced with the contents of the calendar directive's template, with a name attribute specified. This "works", except that said input is wrapped in a span that has a class necessary to look correct using the Bootstrap styling framework.
Directly creating a name attribute in the calendar directive's input field with a binding, like so*:
app.directive('mustPrecedeDate', [
function () {
return {
restrict: 'E',
template: '<input type="text" name="{{ someName }}" />',
scope: {},
controller: 'calendarCtrl',
link: function () {}
};
}
};
Writing link code to explicitly find the input that is a child of the calendar generated markup, and assign it a name attribute. Both 2 and 3 failed, because apparently that's not really something that can be done (I can't find the SO question that was the source of that discovery.)
This leads to my Question: in what way can I get a name down to the input element, such that validation results can be reported to the $error dictionary, so that I can give my users helpful validation messages?
*: Apparently, code blocks with the 'four spaces from the left' formatting don't behave well with numbered lists, so I had to use back-quote code notation to get the text to format halfway correctly. Please feel free to correct my formatting, if I haven't found a bug in the markdown setup SO uses.
The #3 thing needed to be tried a bit harder!
I was able to get a name on the input by adding the following code into my link function:
var inputElement = elem.find('input');
inputElement.attr('name', inputName);
...Where inputName is scraped from the attributes list. I was able to get the inputName down to the generated input[text] field by using a compile function as below.
app.directive('calendar', [
function() {
return {
restrict: 'E',
transclude: false,
scope: {},
template:
'<span class="input-group">'
+ '<input class="form-control" required '
+ 'type="text" placeholder="MM/dd/yyyy" '
+ 'data-ng-model="dt" data-ng-click="toggle($event)" '
+ 'data-ng-change="updateParentProperty()" '
+ 'datepicker-popup="MM/dd/yyyy" is-open="isOpen" />'
+ '<span class="input-group-btn">'
+ '<button type="button" class="btn btn-default" data-ng-click="toggle($event)">'
+ '<i class="fa fa-calendar"></i>'
+ '</button>'
+ '</span>'
+ '</span>',
controller: 'calendarCtrl',
compile: function(elem, attrs) {
var inputName = attrs.inputName;
var inputElement = elem.find('input');
inputElement.attr('name', inputName);
// Compile returns a Link function!
return function(scope, elem, attrs, ctrl) {
var modelName = attrs.ngModel;
scope.parentProperty = modelName;
scope.dt = scope.$parent[modelName];
};
}
};
}
]);
I transform FCKTAG to INPUT and attach dhtmlxcalendar. It works.
Directive works fine too.
But I need attach calendar to an input with an icon Initialization of dhtmlxCalendar Doc
Initialization of dhtmlxCalendar Doc: I must put in
<span><img id="calendar_icon" src="path.gif" border="0"></span>
In Angular Directive I put
template: '<input type="text" ng-model="g" ></input>' +
'<span><img src="http://clans.worldoftanks.ru/media/' +
'clans/emblems/cl_582/2582/emblem_24x24.png" border="0"></span>',
It's error. I need one root tag. I choose DIV:
template: '<div>' +
'<input type="text" ng-model="g" ></input>' +
<span><img src="http://clans.worldoftanks.ru/media/' +
'clans/emblems/cl_582/2582/emblem_24x24.png" border="0"></span>' +
'</div>',
Annnd... Calendar doesn't load.
I haven't any idea why it.
plunker without an icon
plunker with an icon DOESN'T WORK
In your link function you are get the input element using element[0]. Once you wrap all of it in a div tag it is no longer element[0]. Trying substituting the following code.
var input = element.find('input')[0];
if (myCalendar == null) {
myCalendar = new dhtmlXCalendarObject(input);
} else {
myCalendar.attachObj({input:input, button: input});
}
updated plunker
This is my scenario. I am adding a block of code dynamically using javascript. Since its dynamic I have to bind it to my angularjs scope, which is done. But I have one problem here. One of the text box has a directive for it which works. But on change of any text box other than the directive text box for first time the scope.$watch triggers, later on it does not. Here is my code
$('.addNew').click(function(){
var uniqid = Date.now();
var html= '';
html += '<section class="newItem" id="'+uniqid+'">';
html += '<h4 style="margin: 10px 22px 8px 22px;color:#FF9900;border-bottom:1px dotted black;padding:1%;" > Grocery: <em>{{gName}}</em></h4>';
html += '<div class="grosinput" style="width:0%;">-</div>';
html += '<div class="grosinput" style="width:50%;">';
html += '<lable style="color:#6699CC;font-size: 15px;">Name:</lable><input type="text" placeholder="Enter grocery item" name="name" ng-model="gName"/>';
html += '</div>';
html += '<div class="grosinput">';
html += '<lable style="color:#6699CC;font-size: 15px;">Cost:</lable><input type="text" placeholder="Enter Cost" value="30" name="cost" cost-check ng-model="cost"/></div></section>';
var $injector = angular.injector(['ng', 'grocery']);
$injector.invoke(function($rootScope, $compile) {alert('t');
$('.grocadd').hide().after($compile(html)($rootScope)).fadeIn(1500);
});
});
Here is the directive
app.directive('costCheck',function($compile,$rootScope){
$rootScope.gName= "What did i buy?";
return{
restrict: 'A',
link: function(scope,element,attrs){
scope.$watch('cost',function(oldval,newval){alert(attrs.name);
if(attrs.name === 'cost'){
alert(oldval+'--'+newval);
}
});
}
}
});
why is it triggering for other text box also
Please note Angular relies on dirty checking which means it triggers all the watches to check if anything is dirty in order to update the View.
Either you should put a if condition to check newVal and oldVal are different to go ahead as:
scope.$watch('cost',function(newVal, oldVal){
if (newVal !== oldVal){
alert(oldval+'--'+newval);
}
});
Or you can use attrs.$observe to observe the attributes as:
I changed the directive in the DOM from cost-check to cost-check="{{cost}}" and replace $watch with $observe as:
attrs.$observe('costCheck', function(val) {
console.log(scope.$eval(val));
});
Demo: http://jsfiddle.net/HB7LU/3018/