On html.actionlink click, load a partial view below the link - html

Currently in my view this is what I have
<dt>Credit Saldo</dt>
<dd>#Model.CreditSaldo #Html.ActionLink("Add Credit","AddCredit",routeValues:new{Model.LicenseId})</dd>
<br/>
<dd>
<div id="partialDiv"></div>
</dd>
Whenever User clicks Add Credit, currently it forwards me to new partial view (no surprise).
What I want to do is load that partial view in same view, in the div that has Id as partialdiv.
Here is the method in controller, thats being called when the link is called
[HttpGet]
public ActionResult AddCredit(Guid licenseid)
{
var newCredit = new AddCredits();
return PartialView(newCredit);
}
and this is what I've done in partial view
#model AdminPortal.Areas.Customer.Models.ViewModels.AddCredits
<div class="input-small">#Html.EditorFor(m=>m.CreditToAdd) <button class="btn-small" type="submit">Add</button></div>
How can I load the partial view in same view when the link "Add Credit" is clicked?
Edit 1 : Tried Ajax
<dt>Credit Saldo</dt>
<dd>#Model.CreditSaldo #Ajax.ActionLink("Add Credit","AddCredit",new{Model.LicenseId}, new AjaxOptions {UpdateTargetId = "partialDiv"})
<br/>
<dd>
<div id="partialDiv"></div>
</dd>
Result: Partial View still loads as new view (not in same view)

You can use the ajax helpers for that
#Ajax.ActionLink("Add Credit","AddCredit",routeValues:new{Model.LicenseId}, new AjaxOptions {UpdateTagetId = "partialDiv"}, new {})
In order for these to work, you will need to make sure that you load jquery and jquery.unobtrusive-ajax in your layout.
When including jquery, use a pre 1.9 version or the scripts won't work. If you need a version of jquery 1.9+ add the jquery.migrate package from nuget to your project and load that in your layout too.

Related

Razor component tag helper not actually loading the razor component

I've been following the steps in this guide to set up Blazor components in my Razor app. I completed all the steps from the "Prepare the app" section of that guide, modifying the _Layout.cshtml & Startup.cs files and adding the _Imports.razor file. To test this, I'm just trying to implement a basic counter component.
I added the below code to MyApp/Components/Counter.razor:
<p>Current count: #currentCount</p>
<button class="btn btn-primary" #onclick="IncrementCount">Click me</button>
#code {
private int currentCount = 0;
[Parameter]
public int InitialValue { get; set; }
private void IncrementCount() => currentCount++;
protected override void OnParametersSet()
{
currentCount = InitialValue;
}
}
Then in MyApp/Pages/Counter.cshtml i have this:
#page
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#using Microsoft.AspNetCore.Components
#using Microsoft.AspNetCore.Components.Web
#using System.Net.Http
#using Microsoft.AspNetCore.Authorization
#using Microsoft.AspNetCore.Components.Authorization
#using Microsoft.AspNetCore.Components.Forms
#using Microsoft.AspNetCore.Components.Routing
#using Microsoft.JSInterop
#using MyApp
#using MyApp.Components
//This does not work--it appears exactly like this in the HTML when the page loads
<component type="typeof(Counter)" render-mode="ServerPrerendered" />
//this works as expected and loads the razor component
#(await Html.RenderComponentAsync<Counter>(RenderMode.ServerPrerendered))
Note that I copied all the using directives from the _Imports.razor file to see if that fixed things, but it didn't make a difference. My understanding is that the RenderComponentAsync function is outdated and the the "component" tag helper is the current way to use razor components. I'd also prefer to use that syntax since it's easier to pass parameters. Does anyone know what I'm missing to get it to work?
Welp, after messing around with this for hours I realized that my app was on Net Core 3.0 and the tag helper is only available in 3.1+. Updating MyApp.csproj to have 3.1 instead fixed it:
<TargetFramework>netcoreapp3.1</TargetFramework>

.net mvc html help ActionLink and parameters to controller class

so simply want to make a button that will call the controller action passing a parameter...
have everything I believe in place but unable to configure/reference the parameter in the actionlink helper...
Yes I will be refactoring my button onclick once I get through this html helper setup...
<h1 style="font-size:30px">Enter The Core-i Product Key (format RST 102A08R EPCA 00007)</h1>
<form action="/action_page.php">
<label for="productKey">Product Key:</label>
<input type="text" id="productKey" name="productKey"><br><br>
</form>
<p>Click the "Get Key" button and a trial key will be generated custom to your IBMi hardware".</p>
<p>
#Html.ActionLink(
"Get Key",
"GetTrialKey", // controller action
"HomeController", // controller
new { productKey }, // IT DOES NOT LIKE PRODUCTKEY (REFERENCED ABOVE)
new { #class = "btn btn-info" }) // html attributes
</p>
<div class="display-2">
<a button class="text-center, btn btn-info form-control text-white" typeof="button" onclick="location.href='#Url.Action("GetTrialKey(productKey)")'">Get Key</button></a>
<p>
<br />
</p>
</div>
refactored to...
view...
<form action="HomeController/getTrialKey" method="POST">
<label for="productKey">Product Key:</label>
<input type="text" name="productKey" maxlength="22" value="xxx xxxxxxx xxxx xxxxx"><br><br>
<input type="submit" value="Get Trial Key" class="btn btn-primary" />
</form>
controller...
[HttpPost]
public async Task<IActionResult> getTrialKey(string productKey)
{
when I run it i get...
This localhost page can’t be foundNo webpage was found for the web address: https://localhost:44346/HomeController/getTrialKey
Referring back to one of the comments:
I didn't discourage you from using HTML Helpers. I just meant the way you constructed the form and you used ActionLink was wrong. And it would be easier to just have an input for the product key inside the form, if that's the only thing you want to post back to the server.
And I would highly recommend you to read through documentations from Microsoft, at least this one: https://dotnet.microsoft.com/apps/aspnet/mvc to understand what a MVC is. From your code sample, I didn't see you used M - Model at all.
Anyway, if you just only want to get the product key the user types in, I would do it like this:
Define a Controller
I dislike the idea of putting everything under /home (i.e., HomeController). Just think about the URL to the page that would make sense to the user.
Now I am guessing what you are trying to do. I saw terms like product keys and trial keys. What about a controller called ProductKeyController:
public class ProductKeyController : Controller
{
// This corresponds to /productkeys, and you can list all the product keys
// on the view it returns.
public ActionResult Index()
{
return View();
}
// This corresponds to /productkeys/create, and you can create a specific product
// key by asking the user to provide a trial key?
// The view this returns might be the page where you build the form
public ActionResult Create()
{
...
return View();
}
// This corresponds the form post.
[HttpPost]
public ActionResult Create(CreateProductKeyViewModel model)
{
...
return View(model);
}
}
The view model
Your MVC controller is responsible to fetch data, if needed, build a view model, and pass it to the view. When you create a product key, if you need to ask the user to enter anything, you can declare a model and properties within it:
public class CreateProductKeyViewModel
{
[Required]
[Display(Name = "Trial Key")]
[MaxLength(22)]
public string TrialKey { get; set; }
}
The View Create.cshtml
Since you know the controller will be passing the view model to the view, you can declare it on top of the view so that everything you do with the view model inside the view is strongly-typed.
#model CreateProductViewModel
#{
Layout = "xxx";
}
<h1>Enter The Core-i Product Key (format RST 102A08R EPCA 00007)</h1>
#using(Html.BeginForm("create", "productKey", new { area = "" }, FormMethod.Post))
{
#Html.LabelFor(x => x.TrialKey)
#Html.TextBoxFor(x => x.TrialKey)
<button type="submit">Create</button>
}
See how everything within the view is strongly-typed? You don't have to manually create the form and the input for asking user for the trial key.
And when the user enters the trial key and presses submit, it will post back to the Post method of Create. Since the view is declared with the view model, and the view model is the parameter of the create method, MVC has already done the model binding for you hence you will get what user entered on the post back.
This is at least something to get you started.
Note: I wrote everything by hand. Not tested.

AngularJS dynamic additions to page

We have this AngularJS SP application (smart-mirror) in electron browser, which has user createable extensions.
the extensions are small snippets of html that use angular directives
and use controllers and services.
to install an extension, one has to edit the main page and insert the script tags for the controller and service functions and a <div ng-include= ...> for the snippet of HTML
hardcoded this single page app works great.
but I want to add the capability to this app (opensource) to dynamically load those elements somehow...
adding the tags to the dom works, BUT are not processed correctly.
the HTML is processed before the scripts (from the inserted tags) are run, and when the ng-include inserts the HTML snippet, then controllers are not defined yet...
the body (with the extensions in hard-coded positions commented out)
<body ng-controller="MirrorCtrl" ng-cloak>
<div class="top">
<div class="top-left">
<!-- <div ng-include="'plugins/datetime/index.html'"></div>
<div ng-include="'plugins/calendar/index.html'"></div> -->
</div>
<div class="top-right">
<!-- <div ng-include="'plugins/weather/index.html'"></div>
<div ng-include="'plugins/traffic/index.html'"></div>
<div ng-include="'plugins/stock/index.html'"></div>
<div ng-include="'plugins/tvshows/index.html'"></div>
<div ng-include="'plugins/ha-display/index.html'"></div> -->
</div>
</div>
...
...
<script src="filename.service"/>
<script src= filename.controller"/>
</body>
the calendar extension html (inserted into specific div area of the page)
<ul ng-controller="Calendar" class="calendar fade" ng-show="focus == 'default'" ng-class="config.calendar.showCalendarNames ? 'show-calendar-names' : ''">
<li class="event" ng-repeat="event in calendar" ng-class="(calendar[$index - 1].label != event.label) ? 'day-marker' : ''">
<div class="event-details">
<span class="day">
<span ng-bind="event.startName"></span>
<span ng-if="event.startName != event.endName"> - <span ng-bind="event.endName"></span></span>
</span>
<div class="details calendar-name" ng-bind="event.calendarName"></div>
<span class="summary" ng-bind="event.SUMMARY"></span>
<div class="details" ng-if="event.start.format('LT') != event.end.format('LT')">
<span ng-if="event.startName != event.endName"><span ng-bind="event.start.format('M/D')"></span> <span ng-bind="event.start.format('LT')"></span> - <span ng-bind="event.end.format('M/D')"></span> <span ng-bind="event.end.format('LT')"></span></span>
<span ng-if="event.startName == event.endName"><span ng-bind="event.start.format('LT')"></span> - <span ng-bind="event.end.format('LT')"></span></span>
</div>
<div class="details" ng-if="event.start.format('LT') == event.end.format('LT')">All day</div>
</div>
</li>
</ul>
the calendar extension controller (used by the html)
function Calendar($scope, $http, $interval, CalendarService) {
var getCalendar = function(){
CalendarService.getCalendarEvents().then(function () {
$scope.calendar = CalendarService.getFutureEvents();
}, function (error) {
console.log(error);
});
}
getCalendar();
$interval(getCalendar, config.calendar.refreshInterval * 60000 || 1800000)
}
console.log("registering calendar controller")
angular.module('SmartMirror')
.controller('Calendar', Calendar);
the calendar extension service (used by the controller, shortened for this discussion)
(function () {
'use strict';
function CalendarService($window, $http, $q) {
...
...
return service;
}
console.log("registering calendar service")
angular.module('SmartMirror')
.factory('CalendarService', CalendarService);
} ());
so a user wanting to add an extension would have to create these files,
and edit the main page HTML and insert them
<div ng-include src="filename.html"></div>
in the right place and then add the
<script src="filename.service" >
and
<script src="filename.controller">
in the right place and order, service needs to be done before the controller,
as controller uses service.
anyhow, it's easy to add code to locate all the extensions and dynamically insert elements into the dom in their respective places... but...
in the hard coded, the scripts are added after the html in the body
so, I added a new script (processed when the page is loaded), which locates and inserts all the elements to support the extensions in the right places..
and then the script ends.... (last one in the hard-coded HTML) and the HTML directives are processed and boom, the dynamically added scripts have not been loaded or processed, so the controllers are not found...
I CAN create a temp HTML file with all this info in it and load THAT instead of dealing with the dynamic loading, but I think its better to resolve this
I have tried creating my own angular directive and compiling that in, but get stuck in a loop
<divinc src="filename.service"></divinc>
the inserted div is correct, as a child of the divinc directive
angular.module('SmartMirror')
.directive("divincl", ["$compile" ,function($compile){
return {
priority: 100,
terminal: true,
compile: function(scope, element, attrs) {
var html = "<div ng-include=\"" + element['incl']+ "\" onload='function(){console.log(\'html loaded\')}'></div>"
var templateGoesHere = angular.element(document.getElementById(element['id']));
templateGoesHere.html(html);
//document.body.innerHTML='';
var v= $compile(templateGoesHere);
//scope.$apply();
return function linkFn(scope) {
v(scope) // Link compiled element to scope
}
}
}
}]);
advice on how to solve this problem.. Thanks
In order to make an angularjs 1.7 application load dynamically extensions, there are 2 ways:
either use "nested angularjs applications", which is clearly an advanced use of angularjs and will require you to communicate between 2 angularjs applications, to use $scope.$apply to tell the other app to update etc..
either don't load them dynamically in the frontend, but in your backend when generating the html page which contains the application. Try to list all the extensions from the start.
I recommend you to forget the use of ng-include too, and the fact of trying to add <script></script> inside a directive of your application.
First, you need to re-understand how an angularjs application is started.
When you load your main application, you have a script in which angular.module, angular.directive, angular.value, angular.config, angular.run ... calls are made. This is the declaration step
If you declare a module MyApp and that in your html you have a DOM element with ng-app="MyApp", angularjs will automatically run angular.bootstrap() on this DOM element in order to start MyApp. The execution of the application starts here. You cannot declare anything anymore in the module MyApp.
Secondly, I think that <script></script> code inside templates is sanitized and removed by angular. Plus, even if you execute the code, since the declaration step has finished, you are not supposed to create new directives or register new services, it won't work.
A good way is that when you load your plugin, you:
Load the script of the plugin from the start, it must declare a new module like MyPlugin1.
In the directive which will contain the plugin, put the code of the link I sent you, which makes possible to insert a sub-application. In the end you will have a <div ng-app="MyPlugin1"></div> inside your directive's template
Then call angular.bootstrap on that node, which will make possible to start the sub application.
If you do this, you can run the sub application, but you didn't pass it parameters. In order to pass it parameters, you can put the code of the module MyPlugin1 inside a function, in order to have an application factory. Then use app.value('param1', parameter1) to initialize the app.
For example:
function declarePlugin1(myParam1, myParam2) {
var app = angular.module('MyPlugin1', []);
// app.directive();
app.value('myParam1', myParam1);
app.value('myParam2', myParam2);
}
And inside the directive call declarePlugin1("test", 42);, which will declare the application MyPlugin1 with the initialized values, and then angular.bootstrap to tell angularjs to start this application.
You can pass callbacks too, in order to communicate between the 2 applications.

Reusing pages in WinJS's Pivot

I'm developing app for Windows Phone 8.1 using WinJS and I used Visual Studio's template for pivot application. My Applications queries external API and displays results in PivotItem. Since there are three very similar queries that reurn same type of data, I'd like to reuse one code for all the sections in Pivot. The PivotItem page consist basically only of ListView with items received from API. My section page javascript looks like this:
var ControlConstructor = WinJS.UI.Pages.define("/pages/bookmarks/sectionPage.html", {
ready: function(element, options) {
//Here I call API based on received option and render the page
}
}
WinJS.Namespace.define("bookmarksApps_SectionControls", {
SectionControl: ControlConstructor
});
My page declaring the Pivot looks like this:
<div class="bookmarks" data-win-control="WinJS.UI.Pivot" data-win-res="{ winControl: {'title': 'BookmarksTitle'} }">
<div class="section1 section" data-win-control="WinJS.UI.PivotItem" data-win-options="{ isHeaderStatic: true }" data-win-res="{ winControl: {'header': 'BookmarksNew'} }">
<div class="sectioncontrol" id="section1contenthost" data-win-control="bookmarksApps_SectionControls.SectionControl" data-win-options="{'section': 'new'}"></div>
</div>
<div class="section2 section" data-win-control="WinJS.UI.PivotItem" data-win-options="{ isHeaderStatic: true }" data-win-res="{ winControl: {'header': 'BookmarksAll'} }">
<div class="sectioncontrol" id="section2contenthost" data-win-control="bookmarksApps_SectionControls.SectionControl" data-win-options="{'section': 'all'}"></div>
</div>
<div class="section3 section" data-win-control="WinJS.UI.PivotItem" data-win-options="{ isHeaderStatic: true }" data-win-res="{ winControl: {'header': 'BookmarksHistory'} }">
<div class="sectioncontrol" id="section3contenthost" data-win-control="bookmarksApps_SectionControls.SectionControl" data-win-options="{'section': 'history'}"></div>
</div>
</div>
Now, when I open the app,pivot page correctly loads and displays first section with data. But when I swipe the different section, new data is loaded (so the ready function is called, but nothing is displayed (page is blank, only PivotItems' headers are visible). But if I swipe back to section1, it contains data, that I want to display in section2.
Is it possible to reuse my SectionPage.html and SectionPage.js in different PivotItems, preferably without too much of boilerplate code?
You need to create custom HTML control which will host these pages, custom control can accept uri as data-win-options, then inside your control you can have updateLayout() which will render the page and append to parentElement.
Sample code in update layout method:
var options = {} //Page options
if (!this._isLoaded) {
this._isLoaded = true;
WinJS.UI.Pages.render(this.uri, this._pageElement, options);
}
I found source of my problem. In page /pages/bookmarks/sectionPage.html I had <div> with an id meant for holding my ListVIew. And I was getting win control for listview using document.getElementById("listViewId").winControl. This is wrong, because then I had three divs with same id (each for every section), so getElementById was always returning same list (the one on the first section).
So I changed getting of the wincontrol to
var discussionList = document.querySelector("#" + contentHost + " .disucssionsListView").winControl;
where contentHost depends on data-win-options received from main page and everything works as expected.

Forwarding the partial view's details to HttpPost method of a controller when button in partial view is clicked

Whenever following HttpGet method in controller is called it generates a partial view.
Controller HttpGet Method
[HttpGet]
public ActionResult AddCredit(Guid creditBalanceId)
{
var newCredit = new AddCredits()
{
CreditBalanceId = creditBalanceId
};
return PartialView(newCredit);
}
View
#model AdminPortal.Areas.Customer.Models.ViewModels.AddCredits
#Html.HiddenFor(m=>m.CreditBalanceId)
<div class="input-small" id="credit">#Html.EditorFor(m=>m.CreditToAdd) </div>
#Html.ActionLink("Add","AddCredit", new {#class="btn"})
Whenever Add button is clicked in the partial view, I want it to be forwarded to HttpPost method of my controller with HiddenFor(CreditBalanceId) and CreditToAdd value
[HttpPost]
public ActionResult AddCredit(AddCredits credits)
{
_businessUnitRepository.AddCredits(credits);
Information("Credits Successfully added!");
return RedirectToAction("LicenseDetails");
}
Question
What changes do I need to make to my view so that when the button is clicked, i get forwarded to httppost method in controller with all the values?
Have you tried this?
#using(Html.BeginForm("Add", "AddCredit"))
{
#Html.HiddenFor(m=>m.CreditBalanceId)
<div class="input-small" id="credit">#Html.EditorFor(m=>m.CreditToAdd) </div>
<button type="submit" class="btn">Add Credit</button>
}
You could do it quite easily using something like Html.BeginForm and an type of submit. This link is a pretty good example MVC4 forms that save to entity framework models