Render MVC View as a string for Email (With Angular) - angularjs

Technologies: MVC5, ASP.NET 4.5, Angular 1.2.16
I am dynamically generating many parts of my cshtml view using Angular's ng-repeat statement.
I also want the ability to be able to email the page to a user.
I am trying to avoid recreating the entire Html on my controller.
My View:
<div class="col-md-12 form-horizontal" ng-controller="StatusListController">
<div class="adminButtons" ng-if="currentUser.netId == adminUser">
#*#Html.ActionLink("Send Email","Email","Home")*#
<button type="button" class="btn btn-primary" ng-click="statusEmail()">
Send Email
</button>
<button type="button" class="btn btn-danger" ng-click="statusReset()">
Reset
</button>
</div>
<div class="table-responsive">
<table class="table table-hover ">
<thead>
<tr class="tableHeaderRow">
<th class="col-md-3">
Name
</th>
<th class="col-md-7">
Workload Status
</th>
<th class="col-md-2">
Office
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="associate in associateList">
<td>
{{associate.AssociateName}}
</td>
<td>
<span class="col-md-3 tableLabel">
Short-Term:
</span>
<div class="col-md-9 tableValue"
ng-class="{
'statusBusy': associate.Status == 'busy',
'statusFlex': associate.Status == 'Flexible',
'statusFree': associate.Status == 'Available'
}">
{{associate.Status}}
</div>
<br />
<span class="col-md-3 tableLabel">
Mid-Term:
</span>
<div class="col-md-9 tableValue" ng-class="{
'statusBusy': associate.Status2 == 'busy',
'statusFlex': associate.Status2 == 'Flexible',
'statusFree': associate.Status2 == 'Available'
}">
{{associate.Status2}}
</div>
</td>
<td>
{{associate.Office}}
</td>
</tr>
</tbody>
</table>
</div>
I am calling my mvc controller method on an $html call from my angular controller.
Here's the mvc controller:
public virtual string RenderViewToString(ControllerContext controllerContext, string viewPath, string layoutPath, bool partial)
{
try
{
// first find the ViewEngine for this view
var viewEngineResult = partial
? ViewEngines.Engines.FindPartialView(controllerContext, viewPath)
: ViewEngines.Engines.FindView(controllerContext, viewPath, null);
if (viewEngineResult == null)
{
throw new FileNotFoundException("View cannot be found.");
}
// get the view and attach the model to view data
var view = viewEngineResult.View;
using (var sw = new StringWriter())
{
var viewPage = new ViewPage
{
ViewContext =
new ViewContext(controllerContext, view, controllerContext.Controller.ViewData, controllerContext.Controller.TempData, sw)
};
view.Render(viewPage.ViewContext, sw);
return sw.ToString();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
throw;
}
}
The html that it generates has the angular statements in it and not the final render of the view.
Any ideas on how I can get the angular rendered html?

My suggestion would be to just POST your angular-rendered HTML content from the client back to your server, where you server can forward it via email to the user. That way you don't have to do any sort of server-side rendering. You've already got the content rendered by the client, so you might as well use it.
Apart from that, the only other thing I can think of is using a headless browser to render the content. I'm not aware of any headless .NET browsers, but for Node.js there is Phantom.js, which is a headless browser that would render your angular content on the server.
Hope that helps.

Related

Hierarchy implementation in angularjs

I'm new to angularjs and want to implement some hierarchy screen in which user can add a new group, child to that group and so on. (parent/child hierarchy)
Something like this
1 Admin
1.1 -----User Admin
1.1.1 ----------Tech Support Admin
1.1.1.1 --------------------Tech Support Team
1.2 ----------Login Admin
1.2.1 --------------------Profiles
1.3 -----Client Admin
1.3.1 ----------Client 1
1.3.2 ----------Client 2
2 Users
2.1 -----User 1
2.2 -----User 2
2.3 -----User 3
I've searched some tutorials and the only solution I found out relative to my problem is angular ui tree.
But I'm not satisfied with that because I have to add images/avatar with all nodes (parents/children), then have to assign some roles to each of them on the basis of their parent node. but ui-tree does not have any procedure to add customized nodes or child of any node.
Is there any better approach to do this? Any kind of help will be appreciated.
To implement following structure there is property name $parentNodesScope added to get parent index so i added
{{this.$parentNodesScope.$parentNodesScope.$index+1}}.
{{this.$parentNodesScope.$index+1}}.
{{$index+1}} {{node.title}}
1 Admin
1.1 -----User Admin
1.1.1 ----------Tech Support Admin
1.1.1.1 --------------------Tech Support Team
1.2 ----------Login Admin
1.2.1 --------------------Profiles
1.3 -----Client Admin
1.3.1 ----------Client 1
1.3.2 ----------Client 2
2 Users
2.1 -----User 1
2.2 -----User 2
2.3 -----User 3
Here is my jsfiddle link
jsfiddle
I rolled my own solution on something similar. I can't remember why I didn't go with ui-tree. What I needed was a way to recursively create views so that I could mimic a filesystem. Here's the plunk from when I was proofing it out. This should be simpler for you since you're not trying to split files and folders like I did.
With the recursion helper I was able to declare my data structure like this:
$scope.items = [
new File('item1', '/item1', 11, false),
new File('item2', '/item2', 22, true),
new File('item3', '/item3', 33, false),
new File('A Really Long File Name Should Go Here', '/item4', 44, false),
new Folder('Folder 1', '/folder1', [new File('item5', '/item5', 55, false)], false)
];
And I could render it using this:
<table class="table table-condensed table-responsive">
<tbody>
<tr>
<th></th>
<th>Name</th>
<th>Size</th>
<th></th>
</tr>
<tr ng-repeat="item in getFiles()">
<td class="minWidth4px">
<input type="checkbox" ng-model="item.isSelected" />
</td>
<td class="truncateName">
{{item.name}}
</td>
<td class="minWidth4px">{{item.size}}mb</td>
<td ng-show="item.canPreview()" class="minWidth4px">
<button class="btn" ng-click="openPreview(item)">Preview</button>
</td>
</tr>
<tr ng-repeat="item in getFolders()" ng-click="openFolder(item)">
<td class="minWidth4px">
<i ng-show="item.isOpen" class="fa fa-folder-open-o"></i>
<i ng-hide="item.isOpen" class="fa fa-folder-o"></i>
</td>
<td colspan="3">
<label>{{item.name}}</label>
<attachments ng-show="item.isOpen" items="item.items"></attachments>
</td>
</tr>
</tbody>
</table>
And here's the directive for the attachements:
var attachmentsLink = function($scope) {
$scope.openFolder = function(folder) {
folder.isOpen = !folder.isOpen;
console.log(folder);
};
$scope.getFiles = function() {
return $scope.items.filter(function(x) {
return x instanceof File;
});
};
$scope.getFolders = function() {
return $scope.items.filter(function(x) {
return x instanceof Folder;
});
};
};
var attachmentsController = function($scope, previewService){
$scope.openPreview = function(file) {
previewService.preview = file;
previewService.showPreview = true;
};
};
var attachments = function(RecursionHelper) {
return {
compile: function(element) {
return RecursionHelper.compile(element, attachmentsLink);
},
controller: attachmentsController,
restrict: 'E',
scope: {
items:'=',
},
templateUrl: 'attachments.html'
};
};
angular.module("app").directive("attachments", attachments);
I can't take credit for the recursion helper that is the meat of it. The recursion helper is here Hope this helps.
Long time.. let me post my solution here..may be it helps someone else..
So basically I did this with the help of angular-ui-tree with some custom changes to add avatar and links etc. here is html code;
<div class="panel-body">
<div class="col-sm-12">
<div ui-tree id="tree-root" ng-if="data.length > 0" data-drop-enabled="false" data-drag-enabled="false" data-nodrop-enabled="true">
<ol ui-tree-nodes ng-model="data">
<li ng-repeat="node in data" ui-tree-node ng-include="'nodes_renderer.html'" ></li>
</ol>
</div>
</div>
</div>
and this is my script;
<script type="text/ng-template" id="nodes_renderer.html">
<div ui-tree-handle class="tree-node tree-node-content">
<a class="btn btn-success btn-xs" ng-if="node.nodes && node.nodes.length > 0" data-nodrag ng-click="toggle(this)"><span
class="glyphicon"
ng-class="{
'glyphicon-chevron-right': collapsed,
'glyphicon-chevron-down': !collapsed
}"></span></a>
<a class="btn btn-xs" data-nodrag>
<img ng-src="{{node.image}}" class="img-avatar" alt="Avatar" height="35" width="35"></a>
{{node.title}}
<div class="dropdown pull-right ">
<a class="btn btn-primary btn-xs dropdown-toggle" data-toggle="dropdown" data-nodrag><span class="glyphicon glyphicon-th-list"></span></a>
<div class="dropdown-content dropdown-menu" data-nodrag>
<a ng-show = "flagCreateUserHierarchy" ng-click="btnAddUserClick(this)"><i class="fa fa-user"></i> Add User</a>
<a ng-show = "flagUpdateUserHierarchy" ng-click="btnEditClick(this)"><i class="fa fa-folder-open"></i> Edit</a>
<a ng-show="{{node.type}} <2 && flagUpdateUserHierarchy" ng-click="btnEditGroupClick(this)"><i class="fa fa-folder-open"></i> Edit Group Name</a>
<a ng-show = "flagUpdateUserHierarchy" ng-click="btnDeleteHierarchy(this)"><i class="fa fa-ban"></i> Delete</a>
</div>
</div>
and this is how it looks like...
I can further explain this whole procedure if anyone needs to understand.

how can I keep some values in a ngshow and ng hide variable so that $digest does not initialize them again and again in angularjs?

I am displaying some data in a web app that I am getting from a SQL database using a http service. I want to be able to modify the information of that data on the same table where the data is shown. I am using angularjs and I am using the directives ng-show and ng-hide on the table. Here I show the part of the code I am talking about.
<table>
<tr>
<td><font size = 4>Servicio: </font> </td>
<td> </td>
<td>
<select class="col-sm-12" name="repeatSelect" id="repeatSelect" ng-model="currentItem.ServicioId">
<option ng-repeat="option in items.Servicio" value="{{option.Id}}"> {{option.Nombre}}</option>
</select>
</td>
</tr>
</table>
<h4>Cliente: {{getNameC(currentItem.ServicioId)}}</h4>
<br />
<button class="btn btn-primary" ng-click="editOrCreate()">Nuevo</button>
<button class="btn btn-primary" ng-click="list()">Actualizar</button>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Cantidad</th>
<th>Tipo Concepto</th>
<th>Descripción</th>
<th>Precio Unitario</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in itemsSC">
<td>
<div ng-hide="editingData[item.Id]">{{item.Cantidad}}</div>
<div ng-show="editingData[item.Id]"><input ng-model="currentItem.Cantidad" /></div>
</td>
<td>
<div ng-hide="editingData[item.Id]">{{getName(item.ServicioConceptoTipoId)}}</div>
<div ng-show="editingData[item.Id]">
<select name="repeatSelect" id="repeatSelect" ng-model="currentItem.ServicioConceptoTipoId">
<option ng-repeat="option in items.ServicioConceptoTipo" value="{{option.Id}}"> {{option.Nombre}}</option>
</select>
</div>
</td>
<td>
<div ng-hide="editingData[item.Id]">{{item.Descripcion}}</div>
<div ng-show="editingData[item.Id]"><input type="text" ng-model="currentItem.Descripcion" /></div>
</td>
<td>
<div ng-hide="editingData[item.Id]">{{item.PrecioUnitario}}</div>
<div ng-show="editingData[item.Id]"><input ng-model="currentItem.PrecioUnitario" /></div>
</td>
<td>
<button class="btn btn-xs btn-primary" ng-hide="editingData[item.Id]" ng-click="editOrCreate(item)">Modificar</button>
<button class="btn btn-xs btn-primary" ng-show="editingData[item.Id]" ng-click="saveEdit(currentItem,0)">Actualizar</button>
<button class="btn btn-xs btn-primary" ng-hide="viewField" ng-click="delete(item)">Eliminar</button>
</td>
</tr>
</tbody>
<tfoot id="total">
<tr>
<td colspan="3" class="text-right">Total:</td>
<td class="text-right">
{{total(currentItem.ServicioId) | currency}}
</td>
</tr>
</tfoot>
</table>
On my controller I am using object arrays to get the information from the databases and I am using a mirror array to keep the boolean values of the data shown on the table, so depending on the boolean value the tables shows the data or get an input for the user to modify the data.
I get the mirror array once I know the service and client related to the data I want to show and which is possible to be modified.
However, when I click on the button to modify the status of the ng-hide or ng-show object named editingData, the controllers modifies it, but the $digest run all the other functions related to the bindings of the view and returns the bool object array to its inital values.
I haven´t been able to find away to go around this normal working way of the $digest and $watches so my $editingData object is not modified to its initial values. Here I show the code of the controller related to this.
angular.module("App")
.controller("ConceptoCtrl", function ($scope, $http, $resource, serviciosConceptoFactory, serviciosConceptoTipoFactory, serviciosFactory, clientesFactory) {
$scope.list = function () {
$scope.items = serviciosConceptoFactory.query();
$scope.itemsT = serviciosConceptoTipoFactory.query();
$scope.items.ServicioConceptoTipo = $scope.itemsT;
$scope.itemsS = serviciosFactory.query();
$scope.items.Servicio = $scope.itemsS;
$scope.itemsC = clientesFactory.query();
$scope.itemsS.Cliente = $scope.itemsC;
}
//some other functions
$scope.editingData = {};
$scope.getitemsSC = function (item) {
$scope.itemsSC = [];
for (var j = 0; j < $scope.items.length; j++) {
if ($scope.items[j].ServicioId == item) {
newitem = $scope.items[j];
$scope.itemsSC.push(newitem);
}
}
for (var i = 0; i < $scope.itemsSC.length; i++) {
$scope.editingData[$scope.itemsSC[i].Id] = false;
}
}
$scope.editOrCreate = function (item) {
$scope.currentItem = item;
$scope.editingData[item.Id] = true;
}
$scope.list();
});
The {{getNameC(currentItem.ServicioId}} function shown on the html file is the one that calls the $scope.getItemsSC(item) function once it knows the services and client related to the data that will be shown and it is this function the one that initializes the boolean values for the $scope.editingData mirror array depending on the Id of the item.
The $scope.editOrCreate(item) function is the one that changes the boolean object of the specified item so that the view shows the input element for the user instead of the data. However, $digest re-runs the $scope.getItemsSC(item) function because the $watches were modified, and that´s what I want to avoid, because in this case the input elements are never shown.
I thank in advance any help provided
Instead of using the HTML to call functions when data is a available, use the $promise property attached to the resources.
$scope.list = function () {
$scope.items = serviciosConceptoFactory.query();
$scope.itemsT = serviciosConceptoTipoFactory.query();
$scope.items.ServicioConceptoTipo = $scope.itemsT;
$scope.itemsS = serviciosFactory.query();
$scope.items.Servicio = $scope.itemsS;
$scope.itemsC = clientesFactory.query();
$scope.itemsS.Cliente = $scope.itemsC;
var promiseArray = [$scope.items.$promise,
$scope.itemsT.$promise,
$scope.itemsS.$promise,
$scope.itemsC.$promise
];
$q.all(promiseArray).then(function(resultsArray) {
var items = resultsArray[0];
var itemsT = resultsArray[1];
var itemsS = resultsArray[2];
var itemsC = resultsAttay[3];
//create mirror array here
//create editingData array here
});
};
By using the .then method of the promises, the processing of the data can be delayed until it arrives from the server. This way there is no need for the HTML to call functions.

ng-model="query" not working after connecting to Firebase

Part of a project I am working on is listing members, members companies, etc... I used $http within my controller to connect to a JSON file to get everything wired up and test functionality. I am using ng-repeat to populate the page and ng-scope to allow visitors to search the listings.
Everything worked as expected until I wired up Firebase to hold the data. Again, the data populates the page, but now my ng-model is broke and there's no way to search the listings. My Angular skills are still being polished, to say the least, so I'm sure I am having a noob moment. Any chance someone could take a look, I 'de be very grateful. Here is my code.
My Controller
.controller( 'AlliedCtrl', function AlliedCtrl( $scope, $firebase) {
var ref = new Firebase("https://mybase.firebaseio.com/members");
var sync = $firebase(ref);
var syncObject = sync.$asObject();
syncObject.$bindTo($scope, "members");
});
My HTML
<div class="search-sort">
<div class="row">
<div class="col-sm-12 col-xs-12">
<input ng-model="query" placeholder="Search and Sort by Keyword e.g. Accounting, Law, Mobile etc..." class="small">
</div>
</div>
</div>
<table class="table table-condensed table-responsive">
<thead>
<tr>
<th></th>
<th>Member Company</th>
<th>Company Description</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="m in members | filter:query">
<td style="width:250px"></td>
<td style="width:250px"><b>{{ m.company }}</b><br>{{ m.address }}<br>{{ m.address2 }}<br>{{ m.url }}<br><br>{{ m.name }}<br><em>{{ m.title }}</em><br>{{ m.phone }}<br><a mailto="{{ m.email }}">{{ m.email }}</a><br><br></td>
<td>{{ m.description }}</td>
</tr>
</tbody>
</table>
If you want to use ng-repeat on a collection from Firebase, you have to call $asArray and not $asObject + $bindTo.
So in your controller:
var ref = new Firebase("https://mybase.firebaseio.com/members");
var sync = $firebase(ref);
var syncObject = sync.$asArray();
$scope.members = syncObject;
If I understand your problem correctly, you need to wait until the syncObject has loaded so...
//Controller
var members = syncObject.$loaded().then(function() {
members.$bindTo($scope, "members");
}

Angular display message if list is empty

I'm displaying subwebs that the current user has access to, in a sharepoint app part.
I've managed to hide or show a message saying "There is no sites avaiable for you" if my app.js returns 0 in the scope.array.
BUT, the message is displaying before the list has finished loading and when the list has loaded the message dissapears. How can I fix that?
I have a ng-repeat in a table with textbox for filtering:
<input type="text" ng-model="text" placeholder="Filter..." />
<div ng-controller="MainCtrl">
<table>
<thead>
<tr>
<th>
Site
</th>
<th>
Created
</th>
</tr>
</thead>
<tbody ng-repeat="site in sites | filter:text">
<tr>
<td>
{{site.title}}
</td>
<td>
{{site.created | date:dateFormat}}
</td>
</tr>
</tbody>
</table>
<div ng-show="!(sites| filter:text).length">There is no sites avaiable for you.</div>
</div>
I understand that the ng-show is outside the table where ng-repeat is executed, but I've tried inserting the ng-show div inside the tbody but then if no sites exist the table is never executed.
Also, how can I disable the filter textbox if the array is empty? Tried the same attributes as ng-show but in ng-disabled, but that doesnt work.
Wait for your success callback to determine there are no results before showing the message.
//view
<div ng-controller="MainCtrl">
<div ng-show="loading">Loading..</div>
<input ng-hide="noSites" ng-model="text" placeholder="Filter...">
//table ..
<div ng-show="noSites">
There are no sites available for you.
</div>
</div>
//controller
app.controller('MainCtrl', function($scope) {
//initialize
$scope.loading = true
$scope.noSites = false
//sharepoint code to get sites
.success(function(data) {
if (data.length != 0) {
$scope.sites = data
} else {
$scope.noSites = true
}
$scope.loading = false
})
})
<input type="text" ng-model="text" placeholder="Filter..." />
put this inside the <div ng-controller="MainCtrl">.. div
and modify this
<div ng-show="!sites.length" style="display:none">There is no sites avaiable for you.</div> // added `display:none

How do I check my database every once in a while in a jsp page?

Ok, so I have a jsp web page that shows me what are the courses that I am registered to.
the jsp code that is performed in the begining of the page:
<div>
<%
String username = request.getUserPrincipal().getName();
Iterable<Course> avCourses = ORM.getAvailableCourses(username);
%>
</div>
<form id="avCoursesForm">
<fieldset>
<legend>Available courses: </legend>
<table class="coursesTable">
<tr id="firstTr" style="display: none;">
<td class="blueBorder" style="width:5%;"></td>
<td class="blueBorder" style="width:20%;"><b>Course name</b></td>
<td class="blueBorder" style="width:15%;"><b>Course id</b></td>
<td class="blueBorder" style="width:5%;"><b>Credit points</b></td>
<td class="blueBorder" style="width:10%;"><b>Group number</b></td>
</tr>
<%
Iterator<Course> iter = avCourses.iterator();
int size = 0;
while(iter.hasNext())
{
Course course = iter.next();
%>
<tr id="trOfTable<%=size%>" style="display: none;">
<td class="blueBorder" style="width:5px;">
<input type="checkbox" name="option" value="<%=course.courseName%>" />
</td>
<td class="blueBorder">
<a href=# onclick="getPage('<%=request.getContextPath() %>', '<%=course.courseName %>');">
<%=course.courseName%> </a>
</td>
<td class="blueBorder">
<%=course.courseId%>
</td>
<td class="blueBorder">
<%=course.creditPoints%>
</td>
<td class="blueBorder">
<%=course.groupNum%>
</td>
</tr>
<%
size++;
}
%>
</table>
</fieldset>
</form>
the function getAvailableCourses() is at ORM.java file (this file queries the DB).
My goal is to check the database every X seconds to see if a certain course is still available for registration. I don't know how to do that. Can you help me?
In other words, I want to asynchronously check the database and accordingly refresh parts of the page (unavailable course will disappear and new available courses will appear).
tnx, Itamar.
You can use javascript setTimeout. This method call a target javascript function after every x times. Here is the pseudo code
<script type="text/javascript">
function checkCourse(){
var courseId = 5; //for example
// call servlet using ajax to check course is available or not.
callServlet(); // you have to implement the callServlet
}
var x = 2000;
setTimeout("checkCourse()",x);
</script>

Resources