I'm currently using razor to render some views in DotNetNuke and everything is working fine except for one thing I'm missing. I'm trying to get access to some module level methods such as EditUrl etc. but can't seem to figure out how to go about it.
This is what I have for my view, although it errors on EditUrl. I'm using RazorEngine.Render to render the view. There are some helpers that are including for basic DNN info but I can't seem to find anything like NavigateUrl or EditUrl.
Any ideas?
#inherits DotNetNuke.Web.Razor.DotNetNukeWebPage<dynamic>
<div id="items-panel">
<table>
<thead>
<tr>
<th>Title</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Items)
{
<tr>
<td>
#item.Title
</td>
</tr>
}
</tbody>
</table>
</div>
I found a way that seems to work for EditUrl by creating a helper method in the razor view shown below. It's a shame I have to do this because the ModuleInstanceContext is actually passed into the constructor of the RazorEngine but not exposed to the view. If anyone else finds a way around this I'd appreciate a comment.
#helper EditUrl(string keyName, string keyValue, string controlKey)
{
#DotNetNuke.Common.Globals.NavigateURL(Dnn.Tab.TabID, controlKey, "mid="+Dnn.Module.ModuleID, keyName + "=" + keyValue)
}
Edit: The following method also worked well for me. I added a property to the model named ItemViewUrl with a placeholder token for the ItemId and then just did a replace in the view.
In the calling page:
dynamic model = new ExpandoObject();
model.Items = ItemRepository.List();
model.ViewItemUrl = EditUrl("ItemId", "[ITEMID]", "ViewItem");
and then in the Razor view:
<td>#item.Title</td>
Related
I have the following view that displays gaming related data from a controller.
When the page initially loads, it hits an Index Controller that just lists all the gaming sessions ever created (100 total).
However, there is an input field, where the user can input a date, and then click a button.
When clicked, this button sends the date & time to another method called GamingSessionsByDate.
The GamingSessionsByDate method then returns new data which only contains Gaming Sessions with a start date of whatever the user entered.
Here is the view:
#model IEnumerable<GamingSessions.Session>
#{
ViewData["Title"] = "GamingSessionsByDate";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Gaming Sessions By Date</h2>
<input type="date" name="gameSession" id="gameSession">
<input type="Submit" id="postToController" name="postToController" Value="Find" />
#section Scripts
{
<script type="text/javascript">
$("#postToController").click(function () {
var url = '#Url.Action("GamingSessionsByDate", "GameSession")';
var inputDate = new Date('2019-01-23T15:30').toISOString();
$.ajax({
type: "POST",
url: url,
data: "startdate=" + inputDate,
success: function (data) {
console.log("data: ", data);
}
});
});
</script>
}
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.GameName)
</th>
<th>
#Html.DisplayNameFor(model => model.PlayDuration)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.GameName)
</td>
<td>
#Html.DisplayFor(modelItem => item.PlayDuration)
</td>
</tr>
}
</tbody>
</table>
Here is the controller that returns the gaming sessions by date:
public IActionResult GamingSessionsByDate(DateTime startdate)
{
var response = GetGameSessionsList(startdate);
var r = response.Results;
return View(r);
}
By the way, I have hard-coded a date time value into the AJAX call above that I know contains 5 gaming sessions.
Please note that I am writing out the data returned from the controller in the AJAX success method.
So when I click the button, nothing happens on the screen, I just see the initially-loaded 100 gaming sessions from the call to the Index controller.
However, behind the scenes, I can see the 5 gaming sessions I need being written to the console via the console.log command in the Ajax call.
I also see the correct data when I step-through the project in Visual Studio.
So it looks like everything is working, but it appears as if the view/page is not getting refreshed.
So, how do I get that data to display on the page?
Thanks!
The XMLHttpRequest object in JavaScript (what actually makes "AJAX" requests) is what's known as a "thin client". Your web browser is a "thick client", it does more than just make requests and receives responses: it actually does stuff automatically such as take HTML, CSS, and JavaScript that's returned and "runs" them, building a DOM and rendering pretty pictures and text to your screen. A thin client, conversely, literally just makes requests and receives responses. That's it. It doesn't do anything on its own. You are responsible, as the developer, for using the responses to actually do something.
In the case here, that means taking the response you receive and manipulating the DOM to replace the list of game sessions with the different game sessions retrieved. How you do that depends on what exactly you're returning as a response from your AJAX call. It could be HTML ready to be inserted or some sort of object like JSON. In the former case, you'd literally just select some parent element in the DOM and then replace its innerHTML with the response you received. In latter case, you'd need to use the JSON data to actually build and insert elements into the DOM.
Returning straight HTML is easier, but it's also less flexible. Returning JSON gives you ultimate freedom, but it's more difficult out of the box to manipulate the DOM to display that data. That's generally the point where you want to employ a client-side framework like Vue, Angular, React, etc. All of these can create templated components. With that, you need only change the underlying data source (i.e. set the data to the JSON that was returned), and the component will react accordingly, manipulating the DOM as necessary to create the view.
I personally like to use Vue, since it has the least friction to get started with an it's almost stupidly simple to use. For example:
<div id="App">
<input type="date" v-model="startDate" />
<button type="button" v-on:click="filterGameSessionsByDate">Find</button>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.GameName)
</th>
<th>
#Html.DisplayNameFor(model => model.PlayDuration)
</th>
</tr>
</thead>
<tbody>
<tr v-for="item in items">
<td>{{ item.GameName }}</td>
<td>{{ item.PlayDuration }}</td>
</tr>
</tbody>
</table>
</div>
Then a bit of JS to wire it up:
(function (options) {
let vm = new Vue({
el: '#App",
data: {
items: options.items,
startDate: null
},
methods: {
filterGameSessionsByDate: function () {
let self = this;
$.ajax({
type: "POST",
url: options.filterByDateUrl,
data: "startdate=" + self.startDate,
success: function (data) {
self.items = data;
}
});
}
}
});
})(
#Html.Raw(Json.Encode(new {
items = Model,
filterByDateUrl = Url.Action("GamingSessionsByDate", "GameSession")
}))
)
That may look a little funky if you're not that used to JS. I'm just using what's called a closure here: defining and calling a function in place. It takes an options param, which is being filled by the parenthesis at the bottom. Inside those, I'm creating an anonymous object that holds info I need, such as the initial items to display and the URL to get filtered results from. Then, that object is encoded into JSON and dumped to the page.
There is no better way to frame the question but:
I have a button in my Application:
<div #displayDiv></div>
<button (click)="genTable()"></button>
The function call in the component is as follows:
#ViewChild('displayDiv') div: ElementRef;
.
.
genTable(): void {
this.div.nativeElement.innerHTML = ''; // clear the contents of the div
// Make some API call and get data in JSON format and store it
// in a private variable of the class called tableResult
// change content of div using innerHTML
this.div.nativeElement.innerHTML = `
<table>
<tr *ngFor="let eachProp of tableResult">
<th>{{eachProp}}</th>
</tr>
</table>
`;
}
The above code just shows {{eachProp}} as a string and not its content plus it is shown only once.
How do I make the *ngFor and the Angular templates viz. {{eachProp}} dynamically available when writing code within the innerHTML of the div?
Further Info
I already am rendering some Diagram in the displayDiv and once when the user clicks the button, the diagram is cleared off and a table is displayed based on the JSON response from a server.
JSON Response:
{"input":{"concept":"HighChair",
"parameters":["hasHeight","hasWidth"],
"filters":[{"min":3.0,"max":5.2}]},
"columns":["hasHeight","hasWidth"],
"rows":[["106.0","47.0"],["85.0","50.0"]]}
<div>
<table *ngIf="tableResult">
<tr>
<th *ngFor="let eachProp of tableResult?.columns">{{eachProp}}</th>
</tr>
<tr *ngFor="let row of tableResult?.rows">
<td *ngFor="let field of row">
{{field}}
</td>
</tr>
</table>
<div *ngIf="!tableResult">
<!-- put your initial diagram here -->
</div>
</div>
<button (click)="genTable()"></button>
And in the component, something like this:
/* typescript */
tableResult: any;
genTable(): void {
this.tableResult = this.someService.fetchResults();
}
There's no way to know from your code what's in tableResult but I suspect that's what's causing your error. A suggestion would be try to do {{ tableResult }} right below the table tag. That way you can see if its content is correct and if it is an array or collection that can be iterated by *ngFor.
** EDIT: As other users have pointed out, you are trying to insert an uncompiled element to the DOM. That's not going to fly. You need to have a template and do things in your template. see here: https://angular.io/guide/architecture#templates
I am showing an array which contains JSON and am directly showing this with HTML, here you can find elem.fc + elem.cpc. It's giving NaN as an output.
Here is my HTML:
<tr ng-show="table.table1loaded" ng-repeat="elem in filteredcampaignslist" ng-init="initializepopover()">
<td ng-show="app.ostype=='iOS'"><div class="text-center">{{elem.fc+elem.cpc}}</div></td>
</tr>
Please check this: How to parseInt in Angular.js
According to the Angular docs within that post, you cannot, as of the moment, perform such operations within expressions.
The best way to go around this is to create a function that does so within a controller and then bind it to your HTML.
HTML:
<tr ng-show="table.table1loaded" ng-repeat="elem in filteredcampaignslist" ng-init="initializepopover()">
<td ng-show="app.ostype=='iOS'"><div class="text-center">{{ Total() }}</div></td>
</tr>
Inside AngularJS Controller:
$scope.Total = function() { return parseInt(elem.fc) + parseInt(elem.cpc); };
Change {{elem.fc+elem.cpc}} to {{+elem.fc + +elem.cpc}}
That should do the trick.
This is the answer for versions 2 and above of angular:
Number('1234') // 1234
Number('9BX9') // NaN
hello i am trying to pass inline edited table value as a parameter to play routes can someone help me with the same
here is my html code
<table class="gradienttable">
<thead>
<tr>
<th>Task</th>
<th>TimeSheetdate</th>
<th>Hours</th>
<th>IsBilled</th>
<th>Work Place</th>
</tr>
</thead>
<tbody>
#for(element <- CurrentPage) {
<tr>
<td contenteditable="true" id="task1">#element.getTask()</td>
<td>#element.getTimeSheetDate()</td>
<td contenteditable="true" id="hours">#element.getHours()</td>
<td contenteditable="true" id="isbilled">#element.getIsBilled()</td>
<td contenteditable="true"id="workplace">#element.getWorkPlace()</td>
<td>
</tr>
}
</tbody>
</table>
routes
GET /Application/edit controllers.Application.edit(task1:String)
application.java
public static Result edit(String task1)
{
return ok(DisplayTimeSheet.render(task1));
}
It looks like you're confusing the server-side rendered Scala template with the DOM actions of the client. For #{routes.Application.edit(task1.innerHTML)}, task1 doesn't exist as far as the non-DOM template is concerned.
Your use of <a href="..."> is kind of weird, because that would make a synchronous call and if you're into inline editing then maybe that's not what you want. This answer covers an asynchronous approach using Play's JavaScript router support (which, if you haven't seen it before, is very cool).
You'll need to expose edit in the JavaScript router:
// You can also return an F.Promise<Result>
public static Result jsRoutes() {
response().setContentType("text/javascript");
return ok(Routes.javascriptRouter("appRoutes", //appRoutes will be the JS object available in our view
routes.javascript.Application.edit()));
}
And then expose that method in the routes:
GET /assets/js/routes controllers.Application.jsRoutes()
That will generate a chunk of JavaScript that you can import
<script src="#controllers.routes.Application.jsRoutes()" type="text/javascript"></script>
And finally, write some JavaScript to handle the inline editing completion.
function doInlineEdit(taskName) {
appRoutes.controllers.Application.edit(taskName).ajax( {
success : function ( data ) {
target.closest('li').remove();
}
});
}
Then you just need to wire that method to be called when your inline-editable element changes content.
You can find additional info here.
We use elastic search highlighter and get the below code from the highlighter.
<span style='font-weight:bold;color:red;'>Baskar</span> Test
We display the result in html as follows.
<tr
ng-repeat="result in searchModelResult">
<td><p ng-bind-html="result.name"></p></td>
</tr>
I have included sanitize.js and have ngSanitize in the angular module. Still i dont see the html effect like red color font and bold font.
Am i missing anything here?
Thanks,
Baskar.S
You need to do 2 things, first remove
{{result.name}}
as Daniel said... then you need to say to angular to trust in that html in your controller:
myApp.controller('MyCtrl', function ($scope, $sce) {
$scope.result.name = $sce.trustAsHtml('<span style="font-weight:bold; color:red;">Baskar</span> Test');
});
You can see the full documentation of $sce here
If you need to iterate inside a ng-repeat you can create a filter:
myApp.filter('unsafe', function($sce) { return $sce.trustAsHtml; });
And then use it in the view:
<tr ng-repeat="result in searchModelResult">
<td><p ng-bind-html="result.name | unsafe"></p></td>
</tr>
You need to remove the second binding expression:
{{result.name}}
It looks like that is overriding the binding from ng-bind-html, and the default binding will HTML escape the string.