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!
Related
I have a Cefsharp chromium browser and a simple web application hosted with Nancy on a local port built into a wpf application. I would like to use angular with my web application, but I am struggling to change variables inside the angular scope.
Directly on the angular page, everything works perfectly. However, when I try to cross the gap between C# and JS, it partially fails. When I fire the call off from C#, the alert windows still appear and the value of report_type does appear to change in the alert box. However, in the ng-switch, nothing is updated. It's almost as if I am not accessing the correct scope when firing the call from C#... yet if that were the case, the methods in the angular scope shouldn't be callable from C#.
in C#, I call this:
private void GIS_Button_Click(object sender, RoutedEventArgs e)
{
this.report_browser.GetBrowser().MainFrame.ExecuteJavaScriptAsync("ext_switch_gis();");
}
On the served web page:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.switch_gis = function switch_gis() {
alert("begin switch gis");
$scope.report_type = "gis";
alert("end switch gis");
alert("report value is: " + $scope.report_type);
}
$scope.switch_bar = function switch_bar() {
alert("begin switch bar");
$scope.report_type = "bar";
alert("end switch bar");
alert("report value is: " + $scope.report_type);
}
$scope.mytest = function mytest(words) {
alert(words);
}
$scope.switch_bar();
});
function ext_switch_gis() {
var outside_scope = angular.element(document.getElementById('myAppDiv')).scope();
outside_scope.mytest("Beginning of external js call!");
outside_scope.switch_gis();
outside_scope.mytest("End of external js call!");
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
<div id="myAppDiv" ng-app="myApp" ng-controller="myCtrl">
<div id="report_switch_div" ng-switch="report_type">
<div ng-switch-when="bar">
<h1>bar</h1>
</div>
<div ng-switch-when="gis">
<h1>gis</h1>
</div>
<div ng-switch-default>
<h1>Select a report type</h1>
</div>
</div>
<button ng-click="switch_gis()">gis test</button>
<button ng-click="switch_bar()">bar test</button>
</div>
Found a solution. It seems that when invoking a JS function externally, it may often be necessary to use $apply to make sure the "magic" behind angular that allows for 2 way bindings continues to take effect.
This article was very helpful to me: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
The actual code change I made was this:
$scope.switch_gis = function switch_gis() {
$scope.$apply(function () {
$scope.report_type = "gis";
});
}
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
So I'm trying to create a comments section for a blog. I'm having trouble identifying the opened blog posts id in my jquery.
I'm getting these errors from the chrome console
GET http://localhost:46223/api/posts//comments
the postid should be inbetween the double slash but its not. When I manually enter the postID inside the ajax call it works perfectly.
An Api Controller is exposing the comments from the database, relevant code below.
[Route("api/posts/{postId:long}/comments")]
public class CommentsController : Controller
{
readonly BlogDataContext _dbContext;
public CommentsController(BlogDataContext db)
{
_dbContext = db;
}
// GET: api/values
[HttpGet]
public IQueryable<Comment> Get(long postId)
{
return _dbContext.Comments.Where(x => x.PostId == postId);
}
When I press the "Show Comments" link it chrome console gives me the error I was talking about earlier. Relevant code from my partial view below. The most important line from below is only the first one.
Show Comments
<div class="comments-container hide">
<h3>Comments</h3>
<div class="comments">
</div>
<hr />
<div>
Add a comment
<div class="new-comment hide">
<form role="form">
<div class="form-group">
<textarea name="Body" class="new-comment form-control" placeholder="Enter comment here..."></textarea>
<button type="submit" class="btn btn-default">Create Comment</button>
</div>
</form>
</div>
</div>
</div>
Relevant code snippets from my .js
$(document).on('click', '.show-comments', function (evt) {
evt.stopPropagation();
new Post(this).showComments();
return false;
});
function Post(el) {
var $el = $(el),
postEl = $el.hasClass('blog-post') ? $el : $el.parents('.blog-post'),
postId = postEl.data('post-id'),
addCommentEl = postEl.find('.add-comment'),
newCommentEl = postEl.find('.new-comment'),
commentEl = newCommentEl.find('[name=Body]'),
commentsContainer = postEl.find('.comments-container'),
commentsEl = postEl.find('.comments'),
showCommentsButton = postEl.find('.show-comments'),
noCommentsEl = postEl.find('.no-comments');
return {
addComment: addComment,
renderComment: renderComments,
showAddComment: showAddComment,
showComments: showComments,
};
function showComments() {
PostCommentService.getComments(postId).then(renderComments);
}
var PostCommentService = (
function PostCommentService() {
function call(postId, method, data) {
return $.ajax({
// RESTful Web API URL: /api/posts/{postId}/comments
url: ['/api/posts', postId, 'comments'].join('/'), // If I Change the 'postId' here to an integer of an existing postId, it works perfectly.
type: method,
data: JSON.stringify(data),
contentType: 'application/json'
});
}
return {
// Add comment by calling URL with POST method and passing data
addComment: function (comment) {
return call(comment.PostId, 'POST', comment);
},
// Get comments by calling URL with GET method
getComments: function (postId) {
return call(postId, 'GET');
}
};
})();
Full .js file
I'm sorry if I missed to include something, but I have a lot of code. If you need to know anything else let me know.
I'd also be grateful just for some suggestions where my error might be.
Your code is getting the post id from the data attribute post-id of the postEl. postEl could be the same anchor tag which was clicked or it's parent with blog-post css class.
var $el = $(el),
postEl = $el.hasClass('blog-post') ? $el : $el.parents('.blog-post'),
postId = postEl.data('post-id'),
But in your HTML markup, there is no such data attribute for the anchor tag. So if you add that, your code will be able to get the post id and use that to build the url
Show Comments
I hard coded 250 as the value for the data-post-id attribute. You may replace it with a value coming from your model.
Show Comments
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);
});
});
});
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/