Dont understand how views work in ASP.NET - asp.net-mvc-4

I'm trying to understand ASP.NET MVC4.
Within HomeController I have code that seems to call a view ...
public ViewResult RsvpForm()
{
return View();
}
And here is the view....
#model PartyInvites.Models.GuestResponse
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>RsvpForm</title>
</head>
<body>
<div>
#using (Html.BeginForm()) {
#Html.ValidationSummary()
<p>Your name: #Html.TextBoxFor(x => x.Name) </p>
<p>Your email: #Html.TextBoxFor(x => x.Email)</p>
<input type="submit" value="Submit RSVP" />
}
</div>
</body>
</html>
What I dont understand is...
How come the view has access to data when I the call view with empty parameters?
And what this is for: #model PartyInvites.Models.GuestResponse
What is going on with x => x.Name. I believe this is an anonymous function, but I dont understand the need for it. And I do not see where x comes from (it doesnt appear in the code above. Is it a global or something?).
To help with answers, I have done lots of PHP and web stuff and I have grasped most of C# and Razor, so I am assuming I am just being thick or this is something special for ASP
Thanks.

x => x.Name is an anonymous function - it's never executed.
In fact, the Html helper casts it as an Expression<Func<TModel, TProperty>>. This is similar to anonymous functions in PHP or passing functions around in JavaScript (Well, Func is, Expression wraps that and allows access to the execution tree of the function - basically tons of metadata about the function itself).
Once in the html helper, TextBoxFor, there's a ton of extra processing and comparison of the expression tree against the model metadata. Attributes get factored in and all that is used to build up the HTML (like [Required] or [Scaffold])
Your controller doesn't pass in data, but the view declares a strong model.
In the view, #model PartyInvites.Models.GuestResponse does not mean any data is actually passed in. It merely declares the view as strongly typed and makes the model metadata available to Intellisense and the view compiler. The Controller/View relationship is a whole lot more complicated than just a simple function call, unlike most PHP MVC frameworks (or basic PHP includes). The data the controller passes in gets wrapped up into the Page and ViewData with all the metadata and controller context and more.

Related

Microsoft WebHelpers with NETCore.App (2.1)

I'm trying to get the below code to work, but I keep getting compatibility problems with Microsoft.Web.Helpers v 3.2.6 and my current SDK package of NETCore 2.1. Also, for the life of me, I can't get the simplest calls of IsPost and Request to be recognized. I'm sure it's an obvious fix, but I can't find it!
Thanks in Advance for any direction...
#using Microsoft.Web.Helpers;
#{
var fileName = "";
if (IsPost) {
var fileSavePath = "";
var uploadedFile = Request.Files[0];
fileName = Path.GetFileName(uploadedFile.FileName);
fileSavePath = Server.MapPath("~/App_Data/UploadedFiles/" +
fileName);
uploadedFile.SaveAs(fileSavePath);
}
}
<!DOCTYPE html>
<html>
<head>
<title>FileUpload - Single-File Example</title>
</head>
<body>
<h1>FileUpload - Single-File Example</h1>
#FileUpload.GetHtml(
initialNumberOfFiles:1,
allowMoreFilesToBeAdded:false,
includeFormTag:true,
uploadText:"Upload")
#if (IsPost) {
<span>File uploaded!</span><br/>
}
</body>
</html>
The WebHelpers library is not compatible with ASP.NET Core. It relies on System.Web, which .NET Core has been designed to move away from.
The replacement for the IsPost block is a handler method. By convention, a handler method named OnPost will be executed if the method used to request the page is POST (which is what the IsPost property used to check).
Personally, I never understood the point of the FileUpload helper unless you wanted to allow the user to add additional file uploads to the page (which you clearly don't in this case). An input type="file" is easier to add to a page.
File uploading in ASP.NET Core is completely different to Web Pages. Here's some guidance on it: https://www.learnrazorpages.com/razor-pages/forms/file-upload

Refreshing Html.Action

I have a partial view rendered with an Html.Action() that I want to refresh on a button click. I've tried AJAX requests, but the data I'm passing back and forth exceeds the maximum length for JSON.
The basic structure of the page looks like:
<html>
<head>
...
</head>
<body>
<div>
#Html.Action("DisplayBox")
</div>
<div>
<input type="button" id="RefreshButton" value="Refresh Box" />
</div>
</body>
</html>
The reason why I'm asking for a method other than an AJAX request is that the partial I'm rendering is a PDF object:
#model byte[]
#{
String base64EncodedPDF = System.Convert.ToBase64String(Model);
Layout = null;
}
<object data="data:application/pdf;base64,#base64EncodedPDF"
width="900" height="900" type="application/pdf"></object>
Thus, the data passed to the partial view for rendering is too big to put in an AJAX request. On button click, I want to be able to execute the controller action and have the results update the partial with new data. Is there any way of doing this?
You have to load the HTML with the link to the controller that generate the PDF or generate the file on the server side, host it and return the URL of this PDF, then, javascript can redirect user to that file.
I don't think that returning file trough AJAX is really not a good practice!

How can I use cshtml files with Durandal?

I got the DurandalJS StarterKit template on VS2012... All works great...
But in some views I need to do something like that:
#if (Roles.IsUserInRole("Administrators"))
{
<p>Test</p>
}
However with durandal all my views are '.html' files... Is that possible to use '.cshtml' files to access some information like that?
Or is there any other way to do that with durandal?
Junior
I am doing it like this:
Create a generic controller for Durandal views:
public class DurandalViewController : Controller
{
//
// GET: /App/views/{viewName}.html
[HttpGet]
public ActionResult Get(string viewName)
{
return View("~/App/views/" + viewName + ".cshtml");
}
}
Register a route:
routes.MapRoute(
name: "Durandal App Views",
url: "App/views/{viewName}.html",
defaults: new { controller = "DurandalView", action = "Get" }
);
Copy Views/web.config to /App/views/web.config (so Razor views work in this location).
This lets me use the normal Durandal conventions (even the html extension for views), and put durandal views as cshtml files in their normal location without adding any more server code.
If you also have static html views, you can also place the cshtml views in a subfolder or use the normal MVC /Views folder.
I wouldn't recommend using ASP.NET MVC with Durandal.
What you are probably looking to do is use the Razor view engine (to get the benefits of a compiler, strong typing etc.) which exists independently from ASP.NET MVC. Just WebAPI for data I/O is more than enough to very efficiently create a Durandal.js application.
If you are interested in using Razor/CSHTML with Durandal and Knockout there is an open source option out there called FluentKnockoutHelpers that may be exactly what you are looking for. It offers much of the 'nice' parts of ASP.NET MVC allowing you to use the awesome abilities of Durandal and Knockout with almost no downfalls.
Source
Live demo using Durandal.js
In a nutshell it provides a bunch of features which makes doing Durandal/Knockout development just as easy as ASP.NET MVC. (You simply provide a C# type that your JavaScript model is based off of for most of the features.) You only have to write JavaScript and un-compiled markup for complicated cases which is unavoidable and no different than MVC! (Except in MVC your code would also likely end up would also be a big jQuery mess which is why you are using Durandal/Knockout in the first place!)
Features:
Painlessly generate Knockout syntax with strongly typed, fluent, lambda expression helpers similar to ASP.NET MVC
Rich intellisense and compiler support for syntax generation
Fluent syntax makes it a breeze to create custom helpers or extend whats built in
OSS alternative to ASP.NET MVC helpers: feel free to add optional features that everyone in the community can use
Painlessly provides validation based on .NET types and DataAnnotations in a few lines of code for all current/future application types and changes
Client side JavaScript object factory (based on C# types) to create new items in for example, a list, with zero headaches or server traffic
Example without FluentKnockoutHelpers
<div class="control-group">
<label for="FirstName" class="control-label">
First Name
</label>
<div class="controls">
<input type="text" data-bind="value: person.FirstName" id="FirstName" />
</div>
</div>
<div class="control-group">
<label for="LastName" class="control-label">
Last Name
</label>
<div class="controls">
<input type="text" data-bind="value: person.LastName" id="LastName" />
</div>
</div>
<h2>
Hello,
<!-- ko text: person.FirstName --><!-- /ko -->
<!-- ko text: person.LastName --><!-- /ko -->
</h2>
Provide FluentKnockoutHelpers with a .NET type and you can do this in style with Intellisense and a compiler in Razor / CSHTML
#{
var person = this.KnockoutHelperForType<Person>("person", true);
}
<div class="control-group">
#person.LabelFor(x => x.FirstName).Class("control-label")
<div class="controls">
#person.BoundTextBoxFor(x => x.FirstName)
</div>
</div>
<div class="control-group">
#person.LabelFor(x => x.LastName).Class("control-label")
<div class="controls">
#person.BoundTextBoxFor(x => x.LastName)
</div>
</div>
<h2>
Hello,
#person.BoundTextFor(x => x.FirstName)
#person.BoundTextFor(x => x.LastName)
</h2>
Take a look at the Source or Live Demo for an exhaustive overview of FluentKnockoutHelper's features in a non-trivial Durandal.js application.
Yes, you can absolutely use cshtml files with Durandal and take advantage of Razor on the server. I assume that also means you want MVC, so you can do that too and use its routing.
If you don;t want the routing then you can set the webpages.Enabled in the web.config, as the other comments suggest.
<add key="webpages:Enabled" value="true" />
I don't recommend that you use .cshtml files as views directly. You're better off placing the .cshtml files behind a controller.
For example, take the HotTowel sample, edit /App/main.js, and replace the function definition with the following:
define(['durandal/app',
'durandal/viewLocator',
'durandal/system',
'durandal/plugins/router',
'durandal/viewEngine',
'services/logger'],
function (app, viewLocator, system, router, viewEngine, logger) {
Note that we added a reference to the Durandal viewEngine. Then we need to replace
viewLocator.useConvention();
with
viewLocator.useConvention('viewmodels', '../../dynamic');
viewEngine.viewExtension = '/';
The first argument to viewLocation.useConvention sets the /Apps/viewmodels/ directory as the location for the view models js files, but for the view location, uses the URL http://example.com/dynamic/, with an extension of '/'. So that if Durandal is looking for the view named 'shell', it will reference http://example.com/dynamic/shell/ (this is because the view directory is mapped relative to the viewmodels directory, hence /App/viewmodels/../../dynamic will give you simply /dynamic).
By convention, this previous URL (http://example.com/dynamic/shell/) will be mapped to the controller DynamicController, and the action "Shell".
After this, you simply add a controller - DynamicController.cs, like this:
// will render dynamic views for Durandal
public class DynamicController : Controller
{
public ActionResult Shell()
{
return View();
}
public ActionResult Home()
{
return View();
}
public ActionResult Nav()
{
return View();
}
public ActionResult Details()
{
return View();
}
public ActionResult Sessions()
{
return View();
}
public ActionResult Footer()
{
return View();
}
}
Create .cshtml files for each of the above actions. This way you get to use controllers, server side IoC et al to generate dynamic views for your SPA.
DurandaljS is a client framework which forms mainly a solid base for single-page apps (SPA).
I assume you are using asp.net web API as your server technology. In that case, you can determine the user's role inside your API controller and based on that return data to the client. On the client you can use Knockout "if" binding in order to show / hide certain areas of your page.
What you perhaps can do is placing this code in the Index.cshtml.
Following link shows how to customize moduleid to viewid mapping
http://durandaljs.com/documentation/View-Location/
by convention durandal tries to find view url in following steps
1) Checke whether object has getView() function which returns either dom or a string ( url for the view)
2) If object does not have getView function then checks whether object has viewUrl property
3) If above two steps fails to produce url or a DOM view drundal falls to default convention
which maps moduleid xyz.js to view xyz.html using view url ( path of Views folder ) defined in main.js
so for moduleid xyz.js path of the view will be views/xyz.html
you can overwrite this default mapping behavior by overwriting convertModuleIdToViewId function.
So there are many ways you can customize your view url for specific model (.js object)
I made an extension to Durandal which gives you the ability to place an applicationContent div in your cshtml file together with the applicationHost div. In applicationContent you can now use both ASP .Net MVC syntax together with knockout bindings.
Only thing I did was put some extra code in the viewLocator.js file which looks for an applicationContent div:
locateViewForObject: function(obj, area, elementsToSearch) {
var view;
if (obj.getView) {
view = obj.getView();
if (view) {
return this.locateView(view, area, elementsToSearch);
}
}
if (obj.viewUrl) {
return this.locateView(obj.viewUrl, area, elementsToSearch);
}
view = document.getElementById('applicationContent');
if (view) {
return this.locateView(view, area, elementsToSearch);
}
var id = system.getModuleId(obj);
if (id) {
return this.locateView(this.convertModuleIdToViewId(id), area, elementsToSearch);
}
return this.locateView(this.determineFallbackViewId(obj), area, elementsToSearch);
},
Your original cshtml file can now do something like this:
<div class="row underheader" id="applicationContent">
<div class="small-5 columns">
<div class="contentbox">
#using (Html.BeginForm("Generate", "Barcode", FormMethod.Post, Attributes.Create()
.With("data-bind", "submit: generateBarcodes")))
{
<div class="row formrow">
<label for="aantalBijlagen">#Translations.Label_AantalBijlagen</label>
</div>
<div class="row">
<select name="aantalBijlagen" class="small-6 columns">
<option>0</option>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
</div>
<div class="row">
<button class="button right" type="submit" id="loginbutton"><span class="glyphicon glyphicon-cog"></span> #Translations.Action_Generate</button>
</div>
}
</div>
</div>
<div class="small-7 columns" data-bind="if: hasPdfUrl">
<div class="contentbox lastcontent">
<iframe data-bind="attr: {src: pdf_url}"></iframe>
</div>
</div>
You can find my fork of the durandal project here and a small blogpost of what and how I did this here.
I'm not very familiar with DurandalJS but because it's a client-side system, it should make no difference what technology is used on the server to generate the HTML markup. So if you use Razor CSHTML files to generate the HTML on the server, DurandalJS should work just fine with it.
If you're getting a particular error then please share that error, but I can't think of any reason why it wouldn't work.

Using Yii's Ajax Validation without autoload Jquery

I want to use CActiveForm's AjaxValidation.
My layout view file was like this before enabling AjaxValidation:
<html lang="tr-TR" dir="ltr">
<head>
<script src="<?php echo Yii::app()->request->baseUrl; ?>/js/jquery.js"></script>
</head>
As you see i'm calling jquery framework on my layout page (because i'm using on every page).
And i decided to use CActiveForm's ajax validation. Firstly enable enableAjaxValidation while calling it:
<?php $form=$this->beginWidget('CActiveForm', array(
'id'=>'otel-form',
'enableAjaxValidation'=>true,
)); ?>
And then uncomment this on my controller
$this->performAjaxValidation($model);
But i got $(...).yiiactiveform is not a function error. When i check source code of page :
As you see, one more jquery library included, too. So there are 2 jquery files on page. Because of this i'm getting error. Next i put something like this for disabling jquery.
Yii::app()->clientscript->scriptMap['jquery.js'] = false;
Now jquery is loading only once. But this result is :
<html lang="tr-TR" dir="ltr">
<head>
<script type="text/javascript" src="/istanbulcityhotels/assets/cb2686c8/jquery.yiiactiveform.js"></script>
<script src="/istanbulcityhotels/js/jquery.js"></script>
</head>
jquery.yiiactiveform.js calling BEFORE jquery.js . It should called AFTER jquery.js.
It confused a bit. What should i do?
ADDITIONAL
Yes, i read this question because titles' are really similar, but question isnot same.
Thank you.
You should not be including jQuery manually from your layout. Instead of doing this, include it from within your Controller base class:
public function init() {
Yii::app()->clientScript->registerCoreScript('jquery');
}
Don't forget to call parent::init() from within your concrete controllers.
It seems CActiveForm inserts the scripts before the title tag using CClientScript::POS_HEAD constant. So a workaround is to add this code
<?php
$cs=Yii::app()->clientScript;
$cs->scriptMap=array(
'jquery.js'=>false
);?>
to the top of the main layout file in order stop it from loading jquery, then put the title tag after you load your jquery file
<script src="<?php echo Yii::app()->request->baseUrl; ?>/js/jquery.js"></script>
This way jquery.yiiactiveform.js will be loaded right after jquery.
just put your own jquery on tag title,
just like this:
<script src="/istanbulcityhotels/js/jquery.js"></script>
<title>your title</title>

WinJS: How can I access page functions and variables from HTML?

I'm defining page with some vars and methods. Then I wanna use it in html markup (for example data-win-bind="textContent: myPage.variable). How can I access page variables in html markup?
In the JavaScript code behind your page, say default.js, you'd include your ViewModel for the data binding, something like:
(function(){
WinJS.Namespace.define("MyModel.myPage", {
variable : null
};
MyModel.myPage.variable = 'foo';
})();
Then in when the page is activated (in default.js), you'll need to initialize the bindings with a call like
WinJS.Binding.processAll(document.body, MyModel);
There's quite a bit more functionality available though, so this is just a simplistic one-way binding case that should get you started. For more info, check out the Quickstart: binding data and styles.
You have to use javascript here's the skeleton:
<html>
<head>
<script type="text/javascript">
//Code goes here
</script>
</head>
<body>
</body>
<html>