Data Fetched Not Getting to the Client Side - c#

I have an angular application I am creating where I click a page that fetches data from the database but for some weird reason, does not display on the browser. The operation sort of crashes and I click on this particular page and there are no errors logged anywhere, not even on the console or in a text file designed to log all errors/exceptions.
I debugged the C# code on server side and realized the rows are loaded successfully but it doesn't display on the browser and the operation crashes without an error. Please help me. I don't know what I'm doing wrong. Similar approach of fetching data was used for other pages and they work very well.
Server Side
public PagedResultDto<GoodsRequestDto> GetGoodsRequestPaged(GoodsRequestListInput input)
{
var goodsRequests = _goodsRequestRepo.GetAllIncluding(g => g.GoodsQuotes)
.WhereIf(input.UserId.HasValue, g => g.CreatedBy == input.UserId)
.OrderBy(d => d.Id)
.PageBy(input)
.ToList();
return new PagedResultDto<GoodsRequestDto>
{
TotalCount = goodsRequests.Count,
Items = goodsRequests.MapTo<List<GoodsRequestDto>>()
};
}
Angular Controller
vm.loadGoodsRequests = function () {
var skipCount = 0;
abp.ui.setBusy(null,
projectService.getGoodsRequestPaged({
skipCount: skipCount,
userId: appSession.user.id
}).success(function (data) {
vm.goodsRequests = data.items;
})
);
}
Html
<div ng-if="vm.goodsRequests.length" ng-repeat="gr in vm.goodsRequests" class="classInfo-list-item col-md-6">
<div class="classInfo-body">
<h3 class="classInfo-title">
{{gr.categoryItem.name + "_" + gr.brand.name + "_" + gr.product.name | cut:true:50:' ...'}}
</h3>
<p class="classInfo-description">Quantity: {{gr.quantity}} {{gr.unit}}</p>
<p class="classInfo-description">Payment Term: {{gr.paymentTerm}}</p>
<div class="classInfo-registration-info">
{{gr.goodsQuotes.length}} Quote(s).
</div>
<div class="classInfo-actions">
<a class="btn btn-sm btn-info" ng-href="#/my-goods-requests/{{gr.id}}">#L("Details") <i class="fa fa-arrow-circle-right"></i></a>
</div>
<span ng-class="vm.statusClass(gr.statusString)" class="classInfo-date"> {{gr.statusString }}</span>
</div>
</div>
This is the third day on this issue. I am desperate for some help.

The api is returning Items not items
So try using data.Items
These errors can be easily avoided using browser development tools and the debugger; at the right place ..
hovering on the data variable will show you the structure which is returning at run time

Related

How To Save Database Changes to Drag and Drop Lists with Ajax in Asp.Net MVC5

I am attempting to implement a Dragula Drag and Drop feature to the part of my application that allows an Admin to assign users to certain roles. The .cshtml is implemented and displaying correctly but am having trouble figuring out how to write the Ajax call so that I can pass parameters to my method in the controller that utilizes a helper class to add and remove. Can parameters be passed into the ajax $.post(url) that are derived from the location where it was dropped? Included below should be all relevant code. I also understand Viewbags aren't the best way to do this, but if i can get it functioning i will write a viewmodel and pass the data through that way instead.
.cshtml Code
there are 4 of these, one for each of the 3 assignable roles, and one for those not assigned to any role. They're populating correctly currently.
<div class="col-3 h-100">
<div class="bg-secondary p-4">
<h6 class="card-title">Developers</h6>
<div id="dragula-developers" class="py-2">
#foreach (var user in ViewBag.Developers)
{
<div class="card rounded mb-2">
<div class="card-body p-3">
<div class="media">
<div class="media-body">
<h6 class="mb-1">#user.FirstName #user.LastName</h6>
<p class="mb-0 text-muted"> #user.Email </p>
</div>
</div>
</div>
</div>
}
Controller Method
public JsonResult ManageRoles(string id, string role)
{
var message = "User Not Assigned To Role";
if (id != null)
{
userRolesHelper.RemoveUserFromRole(id, role);
if (!string.IsNullOrEmpty(role))
{
userRolesHelper.AddUserToRole(id, role);
message = "User Assigned To Role Successfully!";
}
}
return Json(message);
}
and finally the bare bones of my dragula script where my ajax should go i believe, after the .on('drop').
(function ($) {
'use strict';
dragula([document.getElementById("dragula-noRole"), document.getElementById("dragula-submitters"),
document.getElementById("dragula-developers"), document.getElementById("dragula-managers")])
.on('drop', function (el) {
console.log($(el));
})
})(jQuery);
There are 4 boxes, and ideally when a user's card is dropped into a dragula container, their id and the role associated with the dragula would be shot to the post and their role would be changed, returning a message. This isn't a feature that would be used heavily, so i shouldnt need websockets, but i can't figure out how to implement it. Any help would be greatly appreciated!
After much longer than I care to admit, I've finally figured it out. Just in case it helps anyone else in the future, I had to change my controller to accept an Array. It ended up looking like this before Abstraction.
[HttpPost]
public JsonResult ReassignToDev(Array userIds)
{
var message = "";
if (userIds == null)
{
message = "No Users to Change Roles Of";
}
if (userIds != null)
{
foreach (var user in userIds)
{
userRolesHelper.RemoveAllRoles(user.ToString());
userRolesHelper.AddUserToRole(user.ToString(), "Developer");
message = "Role Change Successful";
}
}
return Json(message);
}
and my ajax call finally wound up looking like this:
var developerIds = new Array();
$('.developer .hidden').each(function (i) {
developerIds.push($(this).text());
})
console.log(developerIds);
$.ajax({
type: "POST",
url: '#Url.Action("ReassignToDev", "Admin")',
dataType: 'JSON',
data: { userIds: developerIds },
success: function (response) {
alert(message);
},
error: function (response) {
alert(message);
}
});
4 different methods and 4 different calls, submitted through a button click that fires an ajax request. not the most elegant solution i'm sure, but it is functional and fast.

My Angular Application Script Doesn't Execute

The following page is based on the sample Angular page which is generated by Visual Studio for a new Angular web application:
<h1>Member Module</h1>
<p>This is a simple example of an Angular component.</p>
<p aria-live="polite">Current count: <strong>{{ currentCount }}</strong></p>
<button class="btn btn-primary" (click)="incrementCounter()">Increment</button>
<div ng-app="myApp" ng-controller="myController">
<table>
<tr ng-repeat="x in portfolios">
<td>{{ x }}</td>
</tr>
</table>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myController', function ($scope, $http) {
$http.get("https://localhost:44319/api/Portfolio")
.then(function (response) { $scope.portfolios = response.text(); });
});
</script>
The button counter works so that pretty much confirms that Angular support is present.
I've added some additional angular code into that page. Beginning with the div tag is some sample code which I'm basing on the tutorial which I find here: https://www.w3schools.com/angular/angular_sql.asp. This will be my first foray into fetching data from the backend SQL Server and displaying it on a web page using Angular.
I've set a breakpoint inside of my controller: https://localhost:44319/api/Portfolio
If I hit that URL manually in a browser window, the breakpoint is hit as expected. But when I load the page, I get no breakpoint. So the script is not being run, but I cannot understand why not.
Can you help me with this? Thank you!
move your api call to some function and call that function like
var init=function(){
$http.get("https://localhost:44319/api/Portfolio")
.then(function (response) { $scope.portfolios = response.text(); });
}
init();
Also there is nothing like (click) in angularjs it should be ng-click
Related SO
Thank you all for clarifying the point. My example fails because I have been conflating Angular with AngularJS. My project, produced by Visual Studio 2019 is Angular NOT AngularJS. But the sample I pulled off the web is AngularJS - of course it's not going to work.
For anyone who might be in the same predicament, here is some code which you can use to augment the auto-magically generated sample pages with some of your own code to get a feel for how Angular is constructed.
member.component.html:
<h1>Member Module</h1>
<p>This is a simple example of an Angular component.</p>
<p aria-live="polite">Current count: <strong>{{ currentCount }}</strong></p>
<button class="btn btn-primary" (click)="incrementCounter()">Increment</button>
<hr />
<h1>{{title}}</h1>
<h2>My favorite hero is: {{heroes[currentCount]}}</h2>
<p>Heroes:</p>
<ul>
<li *ngFor="let hero of heroes">
{{ hero }}
</li>
</ul>
member.component.ts:
import { Component } from '#angular/core';
#Component({
selector: 'app-member-component',
templateUrl: './member.component.html'
})
export class MemberComponent {
public currentCount = 0;
public title = 'Tour of Heroes';
public heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
public myHero = this.heroes[0];
public incrementCounter() {
if (this.currentCount < this.heroes.length -1) {
this.currentCount++;
}
else {
this.currentCount = 0;
}
}
}
It works! Every journey begins with its first step. Thanks for all your help!

Some of my search results don't have datums in local storage

I have been trying to research this for a while and I have found one person with a similar problem but there was no solution to his question:
https://github.com/twitter/typeahead.js/issues/1340
Some of my bloodhound objects are producing expected results into local storage however two of them are missing the data (datums). Even though they are missing the datum part everything else works. I can search with type-ahead as expected. I would like to know why these two objects don't have the data stored.
I now try to use cacheKey attribute instead of the default so this image does not show me using the cacheKey attribute.
Sample Image : Local Storage
Here is a part of my code that uses typeahead. The typeahead works but it's not being stored in local storage:
JavaScript:
var sections = new Bloodhound({
datumTokenizer: function (d) {
$(".loadingSections").hide();
var namepartTokens = Bloodhound.tokenizers.nonword(d);
var nameTokens = Bloodhound.tokenizers.whitespace(d);
var allTokens = namepartTokens.concat(nameTokens);
return allTokens;
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: classLookUp,
cacheKey: 'sectionLookUp'
}
});
sections.initialize();
$(".section-lookup .typeahead").typeahead({ highlight: true }, {
source: sections.ttAdapter()
});
HTML
...
<script>
var classLookUp = '#Url.Action("GetClassNames", "Home")';
...
</script>
...
<div class="form-group section-lookup">
<div class="loadingSections"></div>
<label class="control-label col-sm-2">Section:</label>
<div class="col-sm-10">
#Html.TextBoxFor(x => x.DisplayName, new { #class = "form-control typeahead", #id = "editLookup", #placeholder = "ex. 15/FA CIS-131-102" })
</div>
</div>
<button type="button" id="editSearch" class="btn btn-primary col-sm-offset-4 col-sm-4">Search for Course</button>
C#
[HttpGet]
public ActionResult GetClassNames()
{
try
{
var newSections = this.repo.Courses;
var names = newSections.Select(course => course.DisplayName).ToList();
return this.Json(names, JsonRequestBehavior.AllowGet);
}
catch (DatabaseConnectionException)
{
return this.Json(new { failure = true }, JsonRequestBehavior.AllowGet);
}
}
Note: I seem to be far away from exceeding localstorage max storage
I finally found out why it's not being stored in local storage. The reason is exceeding local storage max storage. I found out at the end of the datum section being stored there is a trie attribute that it stores along with it. Even if your list has only 3,000 elements the trie attached with it is massive enough to take a lot of storage space. So there was unknown overhead.
trie: It seems to be the tree for searching. I would like a way to store only the datum section and have the tree always generated client side. This way we can have more things stored on local storage. Not having this as a default defeats the purpose of prefech which the idea was to not having calls to the server. I know the tree is being build client side but we do not need to have it saved in local storage.

Is there a way to keep a jQuery selected tab, after the page is refreshed?

I have a screen that uses jQuery tabs and was wondering if somehow I can keep the selected tab after a refresh of the page (JS .reload();)?
Any guidance appreciated.
https://github.com/carhartl/jquery-cookie
or
http://code.google.com/p/cookies/downloads/detail?name=jquery.cookies.2.2.0.js.zip&can=2&q=
Example for jquery.cookies.2.2.0.js
$(document).ready(function () {
var initialTabIndex = 0;
var jCookies = jQuery.cookies;
//alert('getting ' + jCookies.get("currentTab"));
if(jCookies.get("currentTab") != null){
initialTabIndex = jCookies.get("currentTab");
}
$('#tabs').tabs({
activate : function(e, ui) {
//alert('setting ' + ui.newTab.index());
jCookies.set("currentTab", ui.newTab.index().toString());
},
active : initialTabIndex
});
});
While the previous answer using cookies will certainly work, it is probably not the ideal solution given peoples aversions to accepting cookies. (it also means you would need to show a disclaimer on your site stating you use cookies, at least to EU visitors). I'd recommend avoiding using cookies where possible so your site remains functional if cookies are disabled/rejected.
A better way is to use the "hash" on the end of a URL.
Modify your tab links as follows:
<div id="UITabs">
<ul>
<li>Tab 1</li>
<li>Tab 2</li>
<li>Tab 3</li>
</ul>
<div id="Tab1"></div>
<div id="Tab2"></div>
<div id="Tab3"></div>
</div>
Then in your head, add the following javascript to ensure the hash is set when changing tabs, and get the hash on pageload and display the required tab:
$(document).ready(function () {
$(function () {
$("#UITabs").tabs();
$("#UITabs").bind("tabsshow", function (event, ui) {
location.hash = ui.newTab.find('a.ui-tabs-anchor').attr('href');
});
$(window).bind('hashchange', function (e) {
$("#UITabs").tabs("select", location.hash);
});
});
});

Reloading Partial View with JQuery

I have a page with a video at the top and a list of videos you can choose from. Currently, clicking a link in the video list will reload the entire page. I need it to only refresh the partial view I have containing the video at the top of the page.
I saw several posts here on SO showing how to reload partial views with JQuery, but couldn't get it to work correctly in my situation. I'm unsure how to pass the correct id of the video along.
Controller:
public ActionResult Videos(int topVideo = 0)
{
VideosModel model = new VideosModel();
model.Videos = StatsVideoService.GetEntityList(new Lookup(TableStatsVideo.IsDeleted, false)).OrderByDescending(x => x.DateCreated).ToList();
if (topVideo == 0)
model.TopVideo = model.Videos.First();
else
{
model.TopVideo = model.Videos.Where(x => x.StatsVideoId == topVideo).FirstOrDefault();
if (model.TopVideo == null)
model.TopVideo = model.Videos.First();
}
return View(model);
}
View:
#model Project.Models.VideosModel
<section class="videos">
<div id="top_video">
#{Html.RenderPartial("StatsVideo", Model.TopVideo);}
</div>
<ul>
#foreach (var item in Model.Videos)
{
<li>
<div class="videoList">
<a href ="#Url.Action("Videos", "Home", new { topVideo = item.StatsVideoId })">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
</li>
}
</ul>
</section>
If there's any more information needed, please let me know.
After several hours of bashing my head against the wall, I got it to work! Just as a reference to anyone else in the future who's viewing this article, here's how I got it to work:
I set the onclick of the link to point to a javascript method, passing in the id of the video as a parameter:
#foreach (var item in Model.Videos)
{
<li>
<div class="videoList">
<a href ="#" onclick="updateTopVideo(#item.StatsVideoId)">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
</li>
}
And then I included this script in the view at the bottom:
<script>
var updateTopVideo = function (itemId) {
var url = '#Url.Content("~/Home/StatsVideo/")';
url = url + itemId;
$.get(url, "", callBack, "html");
};
var callBack = function (response) {
$('#top_video').html(response);
};
</script>
Finally, I added a method to my controller that would return the partial view needed for the video at the top of the screen:
public ActionResult StatsVideo(int Id)
{
IStatsVideo vid = StatsVideoService.GetEntity(new Lookup(TableStatsVideo.StatsVideoId, Id));
if (vid == null)
vid = StatsVideoService.GetEntityList(new Lookup(TableStatsVideo.IsDeleted, false)).OrderByDescending(x => x.DateCreated).FirstOrDefault();
return PartialView(vid);
}
This code should be fairly easy to understand. Basically, the onclick calls the first javascript method, which then calls the controller. The controller builds the partial view and returns it. The first javascript method passes it to the second javascript method which sets the html of the div "top_video" to be the returned partial view.
If anything doesn't make sense, or anyone's having trouble with this in the future, let me know and I'll do my best to offer some help.
I think there may be several confusing and inconsistent elements here.
First, you are returning a full view instead of a partial view. This reloads all containing elements, not just the part that is relevant to your partial view.
Second, you are using Url.Action, which only generates the url. I would recommend using Ajax.ActionLink, which allows you to do fully ajax calls, refreshing the content of your partial div and updating a target div element.
instead of:
<div class="videoList">
<a href ="#Url.Action("Videos", "Home", new { topVideo = item.StatsVideoId })">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
try the more modern solution
<div class="videoList">
#Ajax.ActionLink(
"Videos",
"Home",
"new { topVideo = item.StatsVideoId },
new AjaxOptions {
HttpMethod = "GET",
OnSuccess = "handleSuccess"
}
)
</div>
This way you can be very specific on what you want each link to do, and you can pass along multiple parameters as well as define a callback function. You can also use "UpdateTargetId" in your ajax options to load your newly refreshed partial view into a DOM element.
You can remove the around the image and just store the url generated by the Url.Action in a data-href attribute.
Then you can use the jquery load method to load the data:
$(".videolist>img").click(function () {
$("#content").load($(this).data("href"));
});
I created a fiddle that loads content dynamically here, so you can play with it if you want: http://jsfiddle.net/bTsLV/1/

Categories

Resources