Represent html from JSON array using ng-bind-html - html

I want to render HTML tags from a property of a JSON object and represent them in a modal.
I have this JSON object:
{
"fileUploadCategories": [
{
"id": "Bread",
"title": "GrandmaBread",
"info": "Some info text.",
"restService": "empty",
"detailedInfo": [ "<h1>Stedsnavn</h1>",
"",//line break
"<i>Some text on a new line.</i>",
"",//line break
"Some text on the next line again <b>Should be bold.</b>"]
},
{
"id": "Cake",
"title": "My Cake",
"info": "Info text blah.",
"restService": "empty",
"detailedInfo": "<b>Test</b> <br> This is a text on a new line <br> New line"
}
]
}
In my html i use angularjs with ng-repeat and ng-bind-html to bind the data to a bootstrap modal. Here is my code:
<div class="panel-group" id="accordion" ng-repeat="fileUpload in fileUploadConfig" >
<div class="panel panel-default" >
<div class="panel-heading">
<h3 class="panel-title">{{fileUpload.title}}</h3>
</div>
<div class="panel-body">
<p class="bg-info">{{fileUpload.info}}</p>
<div class="form-horizontal">
<button class="btn btn-primary" id="{{fileUpload.id}}HelpBox" style="float:right" data-toggle="modal" data-target="#{{fileUpload.id}}HelpModal">
Hjelp
</button>
</div>
</div>
</div>
<!-- Modal -->
<div id="{{fileUpload.id}}HelpModal"class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">{{fileUpload.id}}Help</h4>
</div>
<div class="modal-body" ng-bind-html="fileUpload.detailedInfo">
{{fileUpload.detailedInfo.join('\n')}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Lukk</button>
</div>
</div>
</div>
</div>
</div>
When I have my html text in an array it does not render as it is suppose to. (The first JSON object). It works fine in the second JSON object(just have to remove the .join('\n') ). The reason I have represented the HTML in an array is so that it will be easier to change the text later and see how it will render, instead out putting everything on a single line with html tags inside, which looks really messy.
Is there a better way than using an array to represent the data in the JSON object and get it to render the html tags?

I have created a sample for you in order to visualize what's going on. As you can see, applying angularjs markup to the array data type results in rendering "stringified" array:
<div>{{fields.test}}</div> => ["a","b","c"]
Now, I have big doubts that using an array is good idea at all. Semantically this is incorrect and is quite confusing. Just encode your html within the JSON and later decode it (known as sanitize in angular, see more here):
function MyControllerHtml($scope, $sce) {
$scope.renderHtml = function(value) {
return $sce.trustAsHtml(value);
};
$scope.html = '<h1>Stedsnavn</h1><br/><i>Some text on a new line.</i><br/>Some text on the next line again <b>Should be bold.</b>';
};
<div ng-controller="MyControllerHtml">
<div ng-bind-html="renderHtml(html)"></div>
</div>
Finally, if you still want to keep array, then use angular directive to bind your array into hml

Related

Vue's v-for does not show correct text for modal window (only displays the first item's data)

I'm using Vue (just started learning it) and Bootstrap to make a blog and I want a modal window (that shows the post's name/etc) to pop up when the user clicks on the comment button. However, right now the modal window only gets the post information from the latest blog post. This means that even if you click the 1st post, you would get the id information and post title of the latest one... Any suggestions or solutions? Thanks in advance!
It seems that the main problem with my code is that it makes n numbers of modals in vue if you have n posts. I have tried slots but the modals started displaying the first post instead of the last one. I wonder if all my modals are being displayed at once but I only the last one... Should I use an index instead?
Is there any way to make only one modal that gets and display the information of the post clicked?
Here's my code of the post component
Vue.component('posts', {
props: ['post', 'loggedUser'],
template: `
<div class='blog-post py-2'>
<h3> {{post.title}} </h3>
<h6> Posted by {{post.postedBy}} on {{formatCompat(post.createdAt)}} </h6>
<span class='comments'>
<i class='far fa-comment-dots ml-3' data-toggle="modal" data-target="#commentModal"></i>
{{post.comments.length}}
</span>
</div>
`
My modal component: (mostly from the bootstrap docs)
Vue.component('modal', {
props: ['post'],
template: `
<div>
<div class="modal fade" id="commentModal" tabindex="-1" role="dialog" aria-labelledby="commentModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="commentModalLabel">Comment on {{post.postedBy}}'s post! ({{post.title}})</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
</div>
`
})
Here, I'd like the {{posted.title}} and other information to reflect the post where the comment button is clicked, but it's showing the latest post.
Parent element:
const postComponent = new Vue({
el: '#posts',
data: {
posts: null
},
methods: { // mostly code unrelated to modal stuff
}
My pug file:
#posts
if user
posts(name=username, v-for='post in posts' v-bind:post='post' v-bind:key="post.id" v-on:like-post='like' logged-user=loggedUser v-on:comment-post='comment')
modal(v-for='post in posts' v-bind:post='post' v-bind:key="post.id")
You need to add a custom data property that reflects the selected post for the template to read.
data: {
...
selected: '',
...
}
Then, you need a method that will assign the selected property to whichever post is clicked.
methods: {
changeTitle(postId) {
const currentPost = posts.filter((post) => {
return post.id == postId;
});
this.selected = currentPost.id;
}
}
So now that you have a data property to represent the current selected title, and a method to change that property with a click, you need to assign the directive for the method to the post. So, add this to the html tag you want to trigger the method:
#click = "changeTitle(postId);"
This will trigger the method along with the post id from the post that called the method. The current title can now be rendered anywhere in the template that has access to the data, like this:
{{selected}}

passing html values to javascript functions

I have a ng-repeat in my view and I want to pass the object from my ng-repeat to a javascript function but when I try to display it on the console it gives me undefined.
Here is my html:
<!-- Panel -->
<div class="row">
<div class="col-lg-12">
<div class="panel panel-primary">
<div class="panel-heading">
{{card}}
</div>
<!-- /.panel-heading -->
<div class="panel-body">
<div id="nvd3-container" class="md-card" style="overflow-x: auto" ng-if="dataloaded0">
<md-card-content id="nvd3-scrollable-content" style="width: {{width}}px; height: 350px;">
<md-tabs md-dynamic-height="" md-border-bottom="">
<md-tab ng-repeat="pu in selectedKpi" label="{{pu.dprdProuNr}}">
<md-content class="md-padding">
<div class="row">
<div class="col-md-6">
{{pu.item1}}
{{pu.item2}}
</div>
</div>
</md-content>
</md-tab>
</md-tabs>
</md-card-content>
</div>
</div>
<!-- /.panel-body -->
<a href="" ng-click="footerLinkClicked(pu)">
<div class="panel-footer">
<span class="pull-left">Trend</span>
<span
class="pull-right">
<i class="fa fa-arrow-circle-right"></i>
</span>
<div class="clearfix"></div>
</div>
</a>
</div>
</div>
</div>
<!-- /.panel -->
Here is my js file that returns undefined:
angular.module('App')
.directive('KpiParameter', function() {
return {
restrict: 'E',
templateUrl: 'app/kpi/kpi-parameter/kpi-parameter.html',
scope: {
card: '=',
kpiParamCallback: '&',
selectedProductionUnit: '<'
},
controller: function($scope, $rootScope, KpiChartFactory, $filter) {
console.log("!???????");
console.log($scope.selectedProductionUnit);
$scope.$watch('selectedProductionUnit', function() {
console.log($scope.selectedProductionUnit);
console.log("Changed");
KpiParamUpdated();
$scope.kpiParamCallback({
selectedProductionUnit: $scope.productionUnitDefault
});
}, true);
function KpiParamUpdated() {
console.log("KPiParamUpdated");
console.log($scope.selectedProductionUnit);
$scope.dataloaded0 = true;
KpiChartFactory.get({ pu: $scope.selectedProductionUnit }, function(data) {
$scope.selectedKpi = data;
console.log($scope.selectedKpi);
$rootScope.$broadcast('kpiParams', $scope.selectedKpi);
});
}
$scope.footerLinkClicked = function(pu) {
console.log("parameters received :");
console.log(pu);
}
},
controllerAs: "KpiPCtrl"
};
});
Do you have any idea why? I need to define it also in my js file?
As found in the docs of AngularMaterial, you can only achieve what you want to do by using md-on-select
Attributes
Parameter Type Description
label string
Optional attribute to specify a simple string as the tab label
ng-disabled boolean If present and expression evaluates to truthy, disabled tab selection.
md-on-deselect expression Expression to be evaluated after the tab has been de-selected.
md-on-select expression Expression to be evaluated after the tab has been selected.
md-active boolean When true, sets the active tab. Note: There can only be one active tab at a time.
Note: This event differs slightly from ng-click, in that if a tab is already selected and then clicked, the event will not fire.
Your call to footerLinkClicked() has no way of knowing which pu to use unless you tell it which one to use. And since it's outside of your ng-repeat, there's no overly easy way to do that.
md-tabs has an attribute called md-selected that allows you to store the currently selected index in a variable. So assuming that selectedKpi is an array (or is array-like), you can do this:
<md-tabs md-dynamic-height="" md-border-bottom="" md-selected="selectedTab">
and this:
<a href="" ng-click="footerLinkClicked(selectedKpi[selectedTab])">
and you should be all set.

How to call HTML popup page from AngularJS side?

The question is: How to call popup page from AngularJS side?
Currently I have popup page shown on anchor HREF click in HTML like this:
1) the HTML part is following:
<modal lolo="modal1" modal-body="body" modal-footer="footer" modal-header="header" template-url="contactus"></modal>
CONTACT
2) the directives created in AngularJS side:
(function(angular) {
angular.module("app")
.directive('modal', function() {
return {
restrict : 'EA',
scope : {
title : '=modalTitle',
header : '=modalHeader',
body : '=modalBody',
footer : '=modalFooter',
callbackbuttonleft : '&ngClickLeftButton',
callbackbuttonright : '&ngClickRightButton',
handler : '=lolo'
},
templateUrl : function(elem, attrs) {
if (attrs) {
if (attrs.templateUrl === 'sendverify') {
return "./pages/register/sendverificationcode.html";
} else if (attrs.templateUrl === 'contactus') {
return "./pages/contact/contact_us.html";
}
}
},
transclude : true,
controller : function($scope, $attrs) {
$scope.handler = $attrs.templateUrl;
},
};
})
}(angular));
3) And contact_us.html :
<div id="{{handler}}" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" aria-hidden="true">×</button>
<h4 class="modal-title">
<h2>{{header}}</h2>
</h4>
</div>
<div class="modal-body">
<div>
<p>
<b>HAVE A QUESTION OR COMMENT? NEED HELP?</b>
</p>
</div>
<div>
<p>How can we be of service? For answers to all your questions, call us. We 're happy to help. Or, send us an email below and we 'll get back to you as soon as we can.</p>
</div>
</div>
<div class="modal-footer">
<button>1-844-WHATEVER(123-4567)</button>
<button>hello#whatever.com</button>
</div>
</div>
</div>
</div>
Everything works fine, you click CONTACT Anchor element and it shows the popup, but How to call popup page from AngularJS side?. For example you have some logic in AngularJS and based on the result you call the pop up and pass some other data to it to display?

Pass variable from link to div

I have a pure css3 modal call by a link, and I wish to pass some variable to the modal.
Modal
<div id="reasonmodal">
<div class="modal-content">
<div class="header">
Reason
</div>
<div class="copy">
//content
</div>
<div class="footer">Close</div>
<br />
</div>
HTML
//some other code with foreach appid
Click
I want pass foreach appid to the modal, any suggestion to do that ? Thanks
i have an idea (js only):
<a href="#reasonmodal" class="modal-link" rel="<?=$app_id?>" >Click</a>
Close
and modal action :
$(".modal-link").click(function(){
$("#submit-link").attr('href','?r=Register/UpdateReason&appid='+$(this).attr('rel'));
$($(this).attr('href')).modal('show');
});
Just print the var
<div id="reasonmodal">
<div class="modal-content">
<div class="header">
Reason
</div>
<div class="copy">
//content
</div>
<div class="footer">Close</div>
<br />
</div>
I see that you want to dynamically pass variables to the model each time click the html div.
Just use ajax post to get these values and update the modal content before it is triggered
Modal
<div id="reasonmodal">
<div class="modal-content">
<div class="header">
Reason
</div>
<div class="copy" id="modal_content">
//content
</div>
<div class="footer">Close</div>
<br />
</div>
Add a onclick event to html div
<a href="#reasonmodal" onlick='updateModal()'>Click</a>
Implement the updateModal() function which will POST an ajax request to get data. Something like this:
function viewDetail()
{
$.ajax({
type: "POST",
url: <?php echo "\"" . Yii::app()->createUrl('controller/action') . "\""; ?>,
data: { param : "value"},
}).done(function(msg){
//update the model content with msg responded from server
document.getElementById("modal_content").innerHTML = msg;
});
}
The modal will be triggered after the html content is updated.
(Give me a vote-up if it works for you)

MVC 4 Bootstrap Modal Edit \ Detail

Hoping someone might be able to help me with something I am experimenting with in MVC 4 using bootstrap.
I have a strongly-typed index view which displays items in a table along with edit and delete action icons in each line.
#model IEnumerable<Models.EquipmentClass>
....
#foreach (var item in Model)
{
<tbody>
<tr>
<td>
#item.ClassId
</td>
<td>
#item.ClassName
</td>
<td>
<a href=#Url.Action("Edit", "EquipmentClass", new { id = item.ClassId })>
<i class="icon-edit"></i>
</a>
<a href=#Url.Action("Delete", "EquipmentClass", new { id = item.ClassId })>
<i class="icon-trash"></i>
</a>
</td>
</tr>
</tbody>
} <!-- foreach -->
The EquipmentClass controller returns the Edit view for the selected item based on the id. All great and as expected at this point
public ViewResult Edit(int id)
{
return View(equipmentclassRepository.Find(id));
}
What I would like to know is how to open the edit form in a bootstrap modal dialog.
I could try and substitute the edit action in the table with the following and then have a modal div at the bottom of the view but how do I pass in the ID of the selected item and which html helper should I use in the modal section?
<!-- replaced table action -->
<a class="btn pull-right" data-toggle="modal" href="#myModal" >Details</a>
....
<!-- modal div -->
<div class="modal hide fade in" id="myModal" )>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Modal header</h3>
</div>
<div class="modal-body">
#Html.Partial("Edit")
</div>
<div class="modal-footer">
Close
Save changes
</div>
</div>
I'd greatly appreciate any advice, many thanks
I think that I have a solution to your problem. To make your MVC application work as you want it to you should make some changes to the code you provided:
1. Add a div representing a modal for editing an item at the bottom of your layout page:
<div id="editModalContainerID" class="modal hide fade" data-url='#Url.Action("Edit", "EquipmentClass")'>
<div id="editModalBodyID"></div>
</div>
Notice that this modal is strictly connected to the controller action responsible for editing an EquipmentClass item.
2. Add a jQuery function to your custom javaScript:
function showModal(modalContainerId, modalBodyId, id) {
var url = $(modalContainerId).data('url');
$.get(url, { id: id }, function (data) {
$(modalBodyId).html(data);
$(modalContainerId).modal('show');
});
}
As you can see this function takes id as one of its parameters. In general its purpose is to replace the empty modal body with what we will put in a separate partial view and than display whole container as a modal page.
3. Put your modal div in a separate partial view, and call it Edit (has to be the same as your controller action name). You will have to change your Edit partial name to sth else, for example EditBody.
The Edit partial view should now look sth like this:
#model EquipmentClass
<!-- modal div -->
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Modal header</h3>
</div>
<div class="modal-body">
#Html.Partial("EditBody", Model)
</div>
<div class="modal-footer">
Close
Save changes
</div>
4. Change the controller action so that it returns the partial view created in the previous step:
public PartialViewResult Edit(int id)
{
return PartialView(equipmentclassRepository.Find(id));
}
5. Change the edit 'a' anchor so that it calls created jQuery function:
#model IEnumerable<Models.EquipmentClass>
....
#foreach (var item in Model)
{
<tbody>
<tr>
<td>
#item.ClassId
</td>
<td>
#item.ClassName
</td>
<td>
<a data-toggle="modal" onclick="showModal('#editModalContainerID', '#editModalBodyID', #item.ClassId)">
<i class="icon-edit"></i>
</a>
<a href=#Url.Action("Delete", "EquipmentClass", new { id = item.ClassId })>
<i class="icon-trash"></i>
</a>
</td>
</tr>
</tbody>
} <!-- foreach -->
This way each time an 'a' anchor with edit icon is clicked the showModal function (with related id being passed) is fired and a bootstrap modal with related data is displayed.
I'm sure there is a better way to do it, but this way worked for me just fine :)
Hope this helps you a bit :)
Przemo answer worked for me, however do note that I only managed to get it to work when I changed the first block of code from
<div id="editModalContainerID" class="modal hide fade" data-url='#Url.Action("Edit", "EquipmentClass")'>
<div id="editModalBodyID"></div>
</div>
to
<div id="editModalContainerID" class="modal fade" data-url='#Url.Action("Edit", "EquipmentClass")'>
<div id="editModalBodyID"></div>
</div>
Notice that I removed the "fade" class from editModalContainerID. Otherwise the partial view does not load. Hope this helps anyone else with the same issue.