Dynamically Added ng-model in custom directive does not work - javascript

I have searched for this question on Stackoverflow, but all are different from this one. Although the question might be the same the implementation is different.
This directive accepts an array of JSON called config. Which contains properties of an input or a dropdown. I also receive the variable that has to be binded within ngModel. Everything works fine like ng-maxlength ng-minlength but ng-model does not. I cannot fetch the values or in simple words there is no two way binding. Also to get all the values within a single object I append a string called pgCard within the name attribute in the custom directive. To make it look like pgCard.* to use it directly as a json object. But all in vain. I even tried removing the pgCard but of no use.
Below is my custom directive Element markup
<program-gateway config="configArrayCard" name="pgCard"></program-gateway>
This is my custom directive.
app.directive('programGateway',['$compile',
function($compile){
return {
restrict : 'E',
replace :true,
template:"<div class='row'></div>",
scope:{
config:"=",
name:"#"
},
link: function(scope,e,attr){
var modelPrefix=scope.name+"." || "";
var field="",model="";
var max="",min="",maxlen="",req="",options="";
var leftHTML="",rightHTML="",innerHTML="";
scope.config=sortByKey(scope.config,"propertyKey");
angular.forEach(scope.config,function(value,key){
model=modelPrefix+value.propertyKey;
var midLen=(Math.floor(scope.config.length/2))+(Math.floor(scope.config.length%2));
if(!value.propertyInfo.selection){
if(value.propertyInfo.hasOwnProperty("max")){
max="data-ng-maxlength='"+value.propertyInfo.max+"'";
maxlen="maxlength='"+value.propertyInfo.max+"'"
} if(value.propertyInfo.hasOwnProperty("min")){
min="data-ng-minlength='"+value.propertyInfo.min+"'";
}
req=(value.propertyInfo.optional)?"":"required";
field="<input "+req+" class='form-control' type='text' id='"+model+"' data-ng-model='"+model+"' "+max+" "+min+" "+maxlen+">";
}else{
var optArr="[\""+(value.propertyInfo.selectionOptions).replace(/,/g, '","')+"\"]";
field="<select data-ng-options='opt for opt in "+optArr+"' "+req+" data-ng-model='"+model+"' class='form-control' id='"+model+"'>"+
"<option value=''>Select </option>"+
"</select>";
}
if(key<midLen){
leftHTML+="<div class='form-group'>"+
"<label for='"+value.propertyInfo.propertyLabel+"' class='col-sm-5 control-label'><span data-ng-hide='"+value.propertyInfo.optional+"' class='red'>*</span>"+value.propertyInfo.propertyLabel+":</label>"+
"<div class='col-sm-7'>"
+field+
"</div>"+
"</div>";
}else{
rightHTML+="<div class='form-group'>"+
"<label for='"+value.propertyInfo.propertyLabel+"' class='col-sm-5 control-label'><span data-ng-hide='"+value.propertyInfo.optional+"' class='red'>*</span>"+value.propertyInfo.propertyLabel+":</label>"+
"<div class='col-sm-7'>"
+field+
"</div>"+
"</div>";
}//if closed
});// for closed
innerHTML="<div class='col-sm-6'>"+leftHTML+"</div>"+
"<div class='col-sm-6'>"+rightHTML+"</div>";
e.html(innerHTML);
scope.$watch(function() {
return scope.pgCard = scope.pgCard;
});
$compile(e.contents())(scope);
}
}
}]);
This is the snap of the json that I receive
"config":[{"propertyKey":"reportGroup","editable":true,"propertyInfo"
:{"max":3,"optional":true,"propertyLabel":"Report Group","selection":false}},{"propertyKey":"sendStatementToCompany"
,"propertyValue":"Yes","editable":true,"propertyInfo":{"optional":false,"propertyLabel":"Send Statement
to Company","selection":true,"selectionOptions":"Yes,No","defaultValue":"Yes"}}]
Please help am I doing something wrong.

Related

Nested ng-repeat with ng-bind-html

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

How to display javascript variables in html

I have the the following javascript variables.
goalDiv ="<div class=goal-parent id=goal-parent"+Id+">"+
"<div class=goal>"+
"<div class=top-layer>"+
"<div class=component>"+
"<select class=component-select>"+
"<option id=one><span id='source_name'></span></option>"+
"</select>"+
"</div>"+
"</div>"+
"</div>"+
"</div>";
Id = "some div value";
I am trying to show values inside these divs. I first show this goalDiv into a div and then try to update the values inside it, like following.
document.getElementById(Id).innerHTML = goalDiv;
document.getElementById('source_name').innerHTML = "some value";
The first innerHTML works but the second doesn't. What am i doing wrong?
You can't have span inside option.
If you want to change the dropdown options simply use:
document.getElementById('one').innerHTML = "some value";

Jquery for loop not working

I am currently working on a site that does not allow me to edit some of the html content on each of the pages because it is part of a template. I am using Jquery to insert the divs on each page so that i can style them for later. I am currently stuck on this piece of code and cannot get it to work for me. Any suggestions would be greatly appreciated.
jQuery(document).ready(function() {
var URLarray = {
"/100.htm", "<div class='Plugins-div'>Volusion Plugins</div>";
"/101.htm", "<div class='Pricing-div'>Pricing</div>";
"/102.htm", "<div class='Services-div'>Services</div>";
"/103.htm", "<div class='Contact-div'>Contact Us</div>";
}
jQuery.each(URLarray, function(key, value) {
if (location.pathname.indexOf(key) > -1) {
pageHTML = URLarray
jQuery('div#navheader').after('<div id="top-banner" class="col-xs-12"><div class="text-center container">' + pageHTML + '</div></div>');
}
}
});
});
Your code doesn't work (and causes an exception, check your console), because it is full of syntax errors. Object literal that looks like and array (and is named as an array), but has semicolons inside, misplaced closing curly braces, assignment of object where a string was probably meant to be used...
It's not entirely clear what you want to do, but this will probably work (and is at least valid code):
var URLarray = {
"/100.htm": "<div class='Plugins-div'>Volusion Plugins</div>",
"/101.htm": "<div class='Pricing-div'>Pricing</div>",
"/102.htm": "<div class='Services-div'>Services</div>",
"/103.htm": "<div class='Contact-div'>Contact Us</div>"
}
jQuery.each(URLarray, function(key, value) {
if (location.pathname.indexOf(key) > -1) {
jQuery('div#navheader').after('<div id="top-banner" class="col-xs-12"><div class="text-center container">' + value + '</div></div>');
}
});

Value not passing properly in javascript function while iterator using $.each() function

I am returning array of object called "categories" from the Ajax call and then i am replacing its object(category) value in the string(categoriesList). Then i am replacing this string in the html div Area as $("#divAreaName").html("StringName");
For proper understanding the code is shown below:
$.each(categories,function(index,category){
categoriesList = categoriesList +
"<div class='media'>"+
"<img onclick=dispProductCategoryWise('"+category.name+"','"+category.id+"','"+id+"','"+retailerId+"'); src='${pageContext.request.contextPath}"+category.image+"'>"+
"</div>"+
"<div class='item-info'>"+
"<div class='item-name item-row'>"+
"<span class='full-item-name'>"+category.name+"</span>"+
"</div>"+
"</div>";
});
Then i am replacing the string in the div area using the following line of code
$("#divName").html(categoriesList);
However the problem is when category.name is "Fruits and Vegetable". The HTML is formed as shown below:
<img onclick="dispProductCategoryWise('Fruits" &="" Vegetable','60','1','2');="" src="/closerby/images/customer/category/FandV.jpeg">
Can someone let me know why the values are not getting passed properly and the possible solution?
Interesting problem, it seems the spaces are strangely converted when using .html(). As if the '/" itself are rendered. console.log gives correct string as well.
As an alternative, create an event on that specific image using a dynamically generated id.
Like so:
categories = {
"category":{
"name": "Fruits and Vegetables",
"id": 1,
}
}
$.each(categories,function(index,category){
categoriesList =
"<div class='media'>"+
"<img id='specific_"+index+"' src='{somethingwashere}'>"+
"</div>"+
"<div class='item-info'>"+
"<div class='item-name item-row'>"+
"<span class='full-item-name'>"+category.name+"</span>"+
"</div>"+
"</div>";
$("#myDiv").html(categoriesList);
//Added event.
$('#specific_'+index).click(function() {
return displayProductCategoryWise(category.name, category.id);
});
});
function displayProductCategoryWise(name, id) {
alert(name);
alert(id);
}
Ive only used $('selector').each(fn) to do something for each matched jquery element
Perhaps try using categories.forEach(function(category, index){ ...
Are you turning the quote makes into special characters..... add a blackslash ()
Something like :
<img onclick=dispProductCategoryWise('\"+category.name+\"','\"+category.id+\"','\"+id+\"','\"+retailerId+\"'); src='${pageContext.request.contextPath}\"+category.image+\"'>\"

AngularJS generate HTML template with ng-click="function(arg)"

I am trying to generate an HTML templaye from AngularJS directive but I'm not able to pass any object into a function within one of the generated elements. This is my directive:
app.directive('listObject', function($compile){
return {
scope : {
obj : "="
},
controller: "httpPostController",
link : function(scope, element) {
scope.elementTree = null;
//recursivly generate the object output
scope.printObject = function (field, content) {
if (field){
content = "<div>"
if (field.isRequired){
content += "<p>" + field.name + "*:</p>";
} else {
content += "<p>" + field.name + ":</p>";
}
if (field.isEnum){
content += '<select ng-model="createEntityResource[field.name]" ng-change="getCreateEntityAsText()" class="form-control">' +
'<option></option>' +
'<option ng-repeat="enumValue in field.enumValues" label={{enumValue.name}}>{{enumValue.ordinal}}</option>' +
'</select>';
} else if (field.restResourceName) {
content += '<button type="button" ng-click="loadResourceFieldsForField(field)">Create new</button>';
// content += "<p>"+i+""+scope.printObject(field)+"</p>";
} else {
content += '<input type="text" ng-model="createEntityResource[' + field.name + ']" ng-change="getCreateEntityAsText()"' +
'class="form-control" placeholder="{{parseClassName(field.type)}}">';
}
content+="</div>";
return content;
}
};
scope.refresh = function (){
if(scope.elementTree){
scope.elementTree.remove();
}
//generate the html into a string
var content = scope.printObject(scope.obj, content);
//make elements out of a string
scope.elementTree = angular.element(content);
compiled = $compile(scope.elementTree);
element.append(scope.elementTree);
compiled(scope);
};
scope.refresh();
}
};
});
When I create a <button> element - I give it a ng-click function. The function is called and works fine, except that the param it is passed (field) is always undefined.
Does anyone know how to pass object into function within an AngularJS directive?
Every useful answer is highly appreciated and evaluated.
Thank you.
P.S. I tried to split the definition of the button to :
'<button type="button" ng-click="loadResourceFieldsForField(' + field + ')">Create new</button>';
but this does not seem work, AngularJS argues about [Object object] being passed to a function.
P.P.S I also tried to make it according to documentation which says:
Often it's desirable to pass data from the isolated scope via an
expression and to the parent scope, this can be done by passing a map
of local variable names and values into the expression wrapper fn. For
example, if the expression is increment(amount) then we can specify
Blockquote
the amount value by calling the localFn as localFn({amount: 22}).
So it looks like this, but field is still undefined:
'<button type="button" ng-click="loadResourceFieldsForField({field: field})">Create new</button>';
So my printObject algorithm worked great :)
`<button type="button" ng-click="loadResourceFieldsForField(' + field + ')">Create new</button>`
The problem here is: You are generating a string.
And you print your object into the string. This will call toString within the object and won't do what you want to do. It will just print "loadResourceFieldsForField([Object])"
`<button type="button" ng-click="loadResourceFieldsForField({field: field})">Create new</button>`
The field property in the string doesn't have any reference to your param field in the method.
Within $compile: angularjs will search for a variable field within the scope and won't find any.
You have to place this field in the scope, to make this work as aspected. But it seems not like a easy job there.
I would try this:
$compile in each printObject with an own $scope.$new containing the field variable.

Categories

Resources