I'm new in ASP.NET MVC world and I've been trying to get more familiar with the .NET plataform and its particularities. Currently, I am trying to make a C# MVC app that uses the Github API to retrieve some data like list all my repositories, show some of their info, search for repositories by name (not necessarily mine) etc.
But I'm struggling on simple things. First I don't know how to make a correct connection to get these information. I've tried following the octokit tutorial on their page and tried to retrieve data using JSON and convert to a C# object but all my attempts failed on both.
I saw this example in octokit, it works for console apps:
var github = new GitHubClient(new ProductHeaderValue("MyAmazingApp"));
var user = await github.User.Get("half-ogre");
Console.WriteLine(user.Followers + " folks love the half ogre!");
But I am confused where I should put these, considering the MVC structure on ASP.NET, or how it should be different. I didn't find octokit documentation very helpful on those aspects.
And some of my things I've done so far trying to deserialize JSON to C#:
Models
namespace GithubAPI.Models {
public class Repositories {
public string name { get; set; }
public string language { get; set; }
public string owner { get; set; } //or login ?
public string updated_at { get; set; }
}
public class RepCollections {
private List<Repositories> repositories;
public List<Repositories> Repositories { get => this.repositories; set => this.repositories = value; }
}
}
I did this when I tried to save JSON data to a Class
Controllers
public ActionResult Index() {
var url = "https://api.github.com/users/{myuser}/repos";
using (var webClient = new WebClient()) {
var rawJSON = string.Empty;
try {
rawJSON = webClient.DownloadString(url);
}
catch (Exception) { }
RepCollections rep = JsonConvert.DeserializeObject<RepCollections>(rawJSON);
return View();
}
}
It didn't work, probably because it's in the wrong place, idk.
View
#using GithubAPI.Models
#model RepCollections
#{
ViewBag.Title = "Repositories";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="row">
<div class="col-md-4">
<h2>Repositories</h2>
<br/>
<p>User: #Html.DisplayNameFor(model => model.owner)</p>
<div>
<table class="table">
<tbody>
<tr><th></th></tr>
#foreach (Repositories r in Model) {
<tr>
<td>
#r.name
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
Needless to say the foreach didn't work.
Can someone give me some light, please? Thanks.
P.S.: I not using OAuth. Just want a basic authentication.
Your foreach loop is not working because you are not passing the model from Controller to the View. Try this:
Controller
public ActionResult Index()
{
var url = "https://api.github.com/users/{myuser}/repos";
using (var webClient = new WebClient())
{
var rawJSON = webClient.DownloadString(url);
RepCollections rep = JsonConvert.DeserializeObject<RepCollections>(rawJSON);
return View(rep);
}
}
View
#using GithubAPI.Models
#model RepCollections
#{
ViewBag.Title = "Repositories";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="row">
<div class="col-md-4">
<h2>Repositories</h2>
<br />
<p>User: #Html.DisplayNameFor(model => model.owner)</p>
<div>
<table class="table">
<tbody>
<tr><th></th></tr>
#foreach (Repositories r in Model)
{
<tr>
<td>
#r.name
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
Related
I am new to MVC and I am trying populate a list of item in the MVC view class, but the model object is null in the .cshtml file during the startup.
#foreach (var element in Model)
Thanks for your help.
My Code:
public class HomeController : Controller
{
List<ModelMath> mathList = new List<ModelMath>();
[HttpPost]
public ActionResult Submit(FormCollection fc)
{
mathList = new List<ModelMath>();
int num = Convert.ToInt32(fc["Num"]);
while(num > 1)
{
ModelMath modelMath = new ModelMath();
modelMath.Number = num;
mathList.Add(modelMath);
num--;
}
return View(mathList);
}
}
Model class:
public class ModelMath
{
public int Number { get; set; }
}
Index.cshtml
#{
ViewBag.Title = "Home Page";
}
<h3><b>HTTPPost Method</b></h3>
#using (Html.BeginForm("Submit", "Index", FormMethod.Post))
{
<table>
<tr>
<td>Enter a Number: </td>
<td>#Html.TextBox("Num")</td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Submit"></td>
</tr>
</table>
}
<h4 style="color:purple">
<div class="panel-body">
<div class="col-md-12" style="margin-top: 15px;">
<table class="table table-bordered table-responsive table-hover">
<tr>
<th>Input Numbers </th>
</tr>
#foreach (var element in Model)
{
<td>#d.Number</td>
}
</table>
</div>
</div>
</h4>
Could you please let me know what's wrong with my code? Thanks again for your help.
In your Index function, you need to populate the model and pass to the view. Something like
Public ActionResult Index()
{
var myList = new List<example>();
return view(myList)
}
and in your view:
#model List<example>
That is what populates your index view model. It would help if you show us the controller function returning your index view.
you should write the type of Model at first of your View
#model List<ModelMath>
and for showing a view you need [HttpGet] attribute action
[HttpGet]
Public ActionResult Index()
{
//var mathList= new list<ModelMath>();
return view(mathList)
}
I want to use 3 partial views to display my search result(Client name) in 3 different tables. I do not want to show any info from the tables before the search has been completed. As i have understood you can only use one model per view, unless you make some workarounds.
I have tried to make a view model so i can reference the multiple tables from both the index and the partial views. But can't figure it out. Since the methods for the 3 different tables will be the pretty much same i am only going to post the code for the client table. Any help or pointers would be much appreciated
ViewModel:
public class ViewModel
{
public List<Clients> allClients { get; set; }
public List<OrderLines> allOrders { get; set; }
public List<ViewNewOrderSum> allViewNewOrderSum { get; set; }
}
HomeController:
using testForAutofill.Models;
public class HomeController : Controller
{
test_Db_Context db = new test_Db_Context();
// GET: Home
public ActionResult Index()
{
ViewModel vm = new ViewModel();
vm.allClients = GetClients();
return View();
}
private List<Clients> GetClients()
{
List<Clients> clientList = new List<Clients>();
clientList = db.Clients.ToList();
return clientList;
}
[HttpPost]
public ActionResult Index(string searchTerm)
{
Scaleit_Db_Context db = new Scaleit_Db_Context();
List<Clients> orderSums;
if (string.IsNullOrEmpty(searchTerm))//Fix this!
{
orderSums = db.Clients.ToList();
}
else
{
orderSums = db.Clients.Where(x => x.Name.Equals(searchTerm)).ToList();
}
return View(orderSums);
}
IndexView:
#using testForAutofill.Models;
#model testForAutofill.Models.ViewModel
#if (Model.allClients != null && Model.allClients.Count() > 0)
{
#using (Html.BeginForm())
{
<b>Kundenavn:</b>
#Html.TextBox("searchTerm", null, new { id = "txtSearch" })
<input type="submit" value="🔍 Search" class="btn btn-primary" id="btn-search" />
#using (Html.BeginForm())
{
<div class="card-container">
<div class="card border-primary mb-3 card-client" style="max-width: 40rem;">
<div class="card-header">Kunde</div>
<div class="card-body">
<table class="table table-hover">
#foreach (Clients clients in Model.allClients)
{
#Html.Partial("_Client", clients)
}
</table>
</div>
</div>
</div>
}
_Client:
#model testForAutofill.Models.ViewModel
//Tried both DisplayFor and not.
<tr>
<th>Kunde:</th>
<td>#Html.Model.allClients.Name</td>// This is where the error gets
// thrown.
</tr>
<tr>
<th>Org.nr:</th>
<td>#Html.DisplayFor(modelItem => clients.OrgNr)</td>
</tr>
<tr>
<th>Adresse:</th>
<td>#Html.DisplayFor(modelItem => clients.Address1)</td>
#if (clients.Address2 != null)
{
<td>#Html.DisplayFor(modelItem => clients.PostNr)</td>
}
</tr>
The program/webpage doesnt run, and i get the error message:
" CS1061: 'HtmlHelper' does not contain a definition for
'Model' and no extension method 'Model' accepting a first argument of
type 'HtmlHelper' could be found (are you missing a using
directive or an assembly reference?)".
At the commented line in the Partial view.
TRY:
Set object on viewData
inside controller:
ActionResult SomeView(){
ViewData["object"] = theObj;
return View();
}
inside cshtml:
#using objectNamespace
#(((objectType)ViewData["object"]).name)
My problem is probably something obvious or simple but I can't seem to figure it out.
I have a button that when pressed it retrieves JSON data from an API endpoint (from another company). This endpoint returns over 30000 records.
When the data has been retrieved I am showing it to the fron-end with the proper Model. This all works fine.
I added a search box which is handled server side, when a search value is given it returns to the same endpoint which retrieves the data, however if the data has already been loaded, I dont want to retrieve the data again. So I thought I could just use the Data property model to check whether it is empty. This however does not work.
The code looks as follows:
Backend razor page:
public class IndexModel : PageModel
{
private readonly IGetDataService _getDataService;
[BindProperty]
public List<DataModel> DataModel { get; set; }
public string CurrentFilter { get; set; }
public IndexModel(IGetDataService dataService)
{
_getDataService = dataService;
}
public async Task<IActionResult> OnGetLoadData(string searchString)
{
CurrentFilter = searchString;
List<DataModel> data = null;
// Check if data has already been retrieved
if (DataModel == null)
data = await _getDataService.ReadAll(new Uri("https://restapi.xxxxx"));
else
data = DataModel;
if (!String.IsNullOrEmpty(searchString))
{
data.Where(s =>
s.field1.Contains(searchString) ||
s.field2.Contains(searchString) ||
s.field3.Contains(searchString) ||
s.field4.Contains(searchString)
);
}
DataModel = data;
return Page();
}
}
Front-end code:
#page "{handler?}"
#model IndexModel
#{
ViewData["Title"] = "Data page";
}
<form asp-page-handler="LoadData" asp-route-id="" method="get">
<button id="lurvink-button" class="btn btn-primary">
Get Data
</button>
</form>
#if (Model.DataModel != null)
{
<form class="form-group has-search" asp-page-handler="LoadData" method="get">
<span class="fa fa-search form-control-feedback"></span>
<input type="text" class="form-control" placeholder="Search" name="SearchString" value="#Model.CurrentFilter">
</form>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.DataModel[0].field1)
</th>
<th>
#Html.DisplayNameFor(model => model.DataModel[0].field2)
</th>
<th>
#Html.DisplayNameFor(model => model.DataModel[0].field3)
</th>
<th>
#Html.DisplayNameFor(model => model.DataModel[0].field4)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.DataModel)
{
<tr>
<td>#item.field1</td>
<td>#item.field2</td>
<td>#item.field3</td>
<td>#item.field4</td>
</tr>
}
</tbody>
</table>
}
I am not familiar with Razor Pages, but expect (like with normal MVC) the state is not maintained over multiple calls.
You could use MemoryCache to store the DataModel for a certain time.
I am having difficulty passing an IEnumerable as a model. The data is populating a form on one page - and doing so correctly. Upon submission the model returns as null.
I've seen various posts on this and they mostly reference naming-conventions so I have attempted different methods of naming the parameters to try to avoid any confusion in the model binding.
I have also tried various models and helpers to try and pass the data and all have the same result.
Current implementation:
Models:
public class UserProfileListModel
{
public IEnumerable<UserProfileViewModel> UserProfileViewModels { get; set; }
}
public class UserProfileViewModel
{
public UserProfile UserProfile { get; set; }
public Role UserRole { get; set; }
public Team UserTeam { get; set; }
public Scope UserScope { get; set; }
}
View:
#model Project.WebUI.Models.UserPRofileListModel
SNIP
<fieldset>
<legend>Administrate Users:</legend>
<table class="adminTbl">
<thead>
<tr>
<th>UserName:</th>
<th>Role:</th>
<th>Team:</th>
<th>Scope:</th>
<th>Update:</th>
<th>Delete:</th>
</tr>
</thead>
<tbody>
#{foreach (var user in Model.UserProfileViewModels)
{
<tr>
<td>
<p>#user.UserProfile.UserName
#{if (!user.UserProfile.Membership.IsConfirmed)
{
using (Html.BeginForm("Confirm", "Account", FormMethod.Post, null)){
#Html.AntiForgeryToken()
#Html.Hidden("Token", user.UserProfile.Membership.ConfirmationToken)
#Html.Hidden("Name", user.UserProfile.UserName)
}
<input type="submit" value="Confirm" />}
}
</p>
</td>
#{using (Html.BeginForm("SaveUserChanges", "Account", FormMethod.Post, null))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(u => user.UserProfile)
if (user.UserProfile.UserName != User.Identity.Name && user.UserProfile.Membership.IsConfirmed)
{
<td>
#Html.DropDownListFor(u => user.UserRole, Project.WebUI.Controllers.AccountController.RoleList, new { #class = "formdrop" })
</td>
<td>
#Html.DropDownListFor(u => user.UserTeam, Project.WebUI.Controllers.AccountController.TeamList, new { #class = "formdrop" })
</td>
<td>
#Html.DropDownListFor(u => user.UserScope, Project.WebUI.Controllers.AccountController.ScopeList, new { #class = "formdrop" })
</td>
<td>
<input type="submit" value="Save Changes" onclick="return confirm('Are you sure you wish to update this user? ')" />
</td>
}
else
{
/*If user is self or not yet confirmed these are here to buffer the delete button into the last cell*/
<td></td>
<td></td>
<td></td>
<td></td>
}
}
}
<td>
#Html.ActionLink("Delete", "Delete", new { user.UserProfile.UserId }, new
{
onclick = "return confirm('Warning: Action cannot be undone. Are you sure you wish to permanently delete this entry?')"
})
</td>
</tr>
}
}
</tbody>
</table>
</fieldset>
Controller:
Populate View:
public ActionResult AdministrateUsers()
{
populateLists();
var query = repository.UserProfiles.OrderBy(e => e.UserName);
List<UserProfileViewModel> list = new List<UserProfileViewModel>();
foreach(UserProfile up in query)
{
UserProfileViewModel vm = new UserProfileViewModel() { UserProfile = up };
list.Add(vm);
}
UserProfileListModel models = new UserProfileListModel()
{
UserProfileViewModels = list.OrderBy(up => up.UserProfile.UserName)
};
return View(models);
}
Accept Post:
public ActionResult SaveUserChanges(UserProfileListModel model)
{
foreach (UserProfileViewModel upvm in model.UserProfileViewModels)
{
UserProfile up = new UserProfile()
{
UserId = upvm.UserProfile.UserId,
UserEmail = upvm.UserProfile.UserName,
UserName = upvm.UserProfile.UserName
};
if (ModelState.IsValid)
{
repository.SaveUserProfile(up);
}
else
{
return View(model);
}
}
return RedirectToAction("Index", "Admin");
}
The code does still need a lot of work but I can't get past getting the model back to the controller on post. I have also tried returning the UserProfileViewModel instead of the entire list.
Can anyone tell what I am doing wrong?
Thanks!
You have a lot of invalid html including form elements as child elements of tr elements and duplicate id attributes. If you want to post back UserProfileListModel then you need a single form element and use an EditorTemplate or a for loop (not foreach) to render the controls so they are correctly named with indexers.
You are also trying to bind your dropdown lists to complex objects (for example UserProfile, Role etc.). <select> elements (and all form controls) only post back key/value pairs so you need to bind to a value type (for example UserProfile.UserId).
Your SaveUserChanges() post method is also trying access properties of UserProfile but you don't even have controls for properties of UserProfile in the form that post back to this method (for example UserId = upvm.UserProfile.UserId, UserEmail = upvm.UserProfile.UserName, ...) so they will always be null.
You probalby need to bind properties in POST method like here:
public ActionResult Create([Bind(Include = "Id,Subject,Text,IsImportant")] Announcment announcment) {... }
So it will be:
public ActionResult SaveUserChanges([Bind(Include = "UserProfile,Role,UserTeam,UserScope")]UserProfileListModel model)
Have you specified your action method is for HTTP Post? And change your action method to accept UserProfileViewModels instead.
[HttpPost]
public ActionResult SaveUserChanges(UserProfileViewModels model)
{
You are also only posting back one model: UserProfileViewModels.
You have your form in your foreach loop, so each UserProfileViewModels has its own form. If you want to change it to post back your UserProfileListModel, move
#{using (Html.BeginForm("SaveUserChanges", "Account", FormMethod.Post, null))
outside of your foreach.
How to I convert my simple MVC4 todo list application to AJAX?
The ideal answer would give me the steps that would lead to a successful conversion of this example to AJAX.
Note: I don't need an AJAX tutorial so much as an understanding of how the ASP.NET MVC architecture supports it.
Side Question: Why does #Html.EditorFor(model => model.TodoItemToCreate) bring back the value that was typed in even though the view model sets with this.TodoItemToCreate = null?
Model
public class TodosViewModel
{
List<string> todoItems;
public List<string> TodoItems
{
get { return this.todoItems ?? (todoItems = new List<string>()); }
}
[Display(Name="What do you need to do?")]
public string TodoItemToCreate { get; set; }
public bool AcceptTodoItem()
{
bool isThereAnItemToAccept = !string.IsNullOrWhiteSpace(this.TodoItemToCreate);
if (isThereAnItemToAccept)
{
this.TodoItems.Add(this.TodoItemToCreate);
this.TodoItemToCreate = null;
}
return isThereAnItemToAccept;
}
}
Controller
public class TodosController : Controller
{
public ActionResult Index()
{
return View(new TodosViewModel());
}
public ActionResult Create(TodosViewModel todosViewModel)
{
todosViewModel.AcceptTodoItem();
return View("Index", todosViewModel);
}
}
Index View
#model Programming.LearnWeb.Models.TodosViewModel
#{
ViewBag.Title = "Todos";
}
#using (Html.BeginForm("Create", "Todos"))
{
#Html.Partial("List")
#Html.LabelFor(model => model.TodoItemToCreate)
#Html.EditorFor(model => model.TodoItemToCreate)
<input type="submit" value="Create" />
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
List View
#model Programming.LearnWeb.Models.TodosViewModel
#{ int i = 0; }
<table>
#foreach (var todoItem in Model.TodoItems)
{
<tr>
<td>
#Html.Hidden("TodoItems[" + i++ + "]", todoItem)
#todoItem
</td>
</tr>
}
</table>
I got this done on my own - the result is at https://github.com/gabrielgreen/Todos.Mvc if anyone is interested or has any comments.
I put a fair amount of effort in and would appreciate any feedback that indicates if I did it right.