I could use some help, figure out how to pass model data from mvc application to a angular2 component running inside mvc.
Lets say I have a cs.html file that has an component
<my-app></my-app>
This will load the angular2 component. I need to generate some binding to keep mvc models intact with my angular2 models.
First of all, I'm trying to pass a model to the component via the Input property.
CSHTML file:
In the top of my cshtml file, I have:
#model MainModel
<script>
var model = #Html.Json(Model.Form.PassengerModel);
</script>
I want to pass this model to my angular2 component.
What I have tried are:
<my-app passengerModel="model"></my-app>
Angular2 component:
import { Component, Input } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './Content/Scripts/angular2components/app.component.html',
})
export class AppComponent {
#Input() passengerModel: PassengerModel;
constructor() {
console.log("Model loaded?: " + this.passengerModel);
}
}
export class PassengerModel {
constructor(
public Adults: number,
public Children: number
) { }
}
The problem is that the model is undefined always. Is there any way to pass a model in to the component?
The problem you have outlined above is that your binding is not correct for the context you are attempting to use it in.
<my-app passengerModel="model"></my-app>
The above line is telling Angular to bind passengerModel inside your my-app component to a property on the host component named model. This means the page (component) which hosts your my-app component should be a component with a property named model. You have created a global variable which is not in the scope of your host component. Angular2 specifically isolates the scope of each component so that you do not accidentally introduce unwanted side effects.
Save yourself some pain an anguish and embrace Angular fully. Ditching your MVC Page Controllers and moving to WebApi service calls will yield better results and save you the need to translate the model manually among other issues you will run into going down the mixed route.
Considerations:
#Html.Json will ultimately cause your data to be exposed directly in your script tag. This could be a security risk if the data is sensitive and if you start using MVC Model bindings in the page alongside Angular bindings they will fight each other.
Basically these approaches are diametrically opposed as ASP.NET MVC is a server side approach and Angular is a client side approach. Mixing them in the same application will always be awkward at best.
WebApi gives you the JSON serialization more or less for free. Your MVC model is automatically serialized to JSON by the framework when returning an HttpAction. If you are trying to avoid converting your ASP.NET MVC views to Angular Components then I understand. You may not have a choice but if you do I would steer clear of mixing these two.
[HttpGet]
[AcceptVerbs["GET"]
[Route("passengers/{id:int}")]
public IHttpActionResult GetPassenger(int id)
{
// For illistration...
try
{
var passengerModel = PassengerService.LoadPassenger(id);
return Ok(passenger);
}
catch(Exception e)
{
return InternalServerError(e);
}
}
https://angular.io/docs/ts/latest/tutorial/toh-pt6.html
// I would normally put this in the environemnt class..
private passengerUrl = 'api/passengers'; // URL to web api
constructor(private http: Http) { }
getPassenger(id: number): Promise<Passenger> {
return this.http.get(`this.passengerUrl/${id}`)
.toPromise()
.then(response => response.json() as Passenger)
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for demo purposes only
return Promise.reject(error.message || error);
}
Related
I'm rather new to Blazor, but I am currently trying to get access to some classes from within a class library that I've created and deployed as a Nuget package. As background, the Nuget package is an Api library, which allows me to talk to a webservice (I don't know if this is relevant or not). However, every time I go to the page where I'm testing, the page never loads and instead I left looking at the browser loading circle until I navigate away or close the application. During my testing here, it seems like it's the #inject call of my interface into the Blazor component which is causing the issue as when I remove it and try to load the page normally, the page does so.
So to demonstrate what I have setup, here is where I've added the Singletons to the DI:
builder.Services.AddSingleton<IApiConfigHelper, ApiConfigHelper>();
builder.Services.AddSingleton<IApiHelper, ApiHelper>();
builder.Services.AddSingleton<ISystemEndpoint, SystemEndpoint>();
Then on the blazor page, I have the following declarations at the top of my page:
#using Library.Endpoints
#using Library.Models
#page "/"
#inject ISystemEndpoint _systemEndpoint
Now I am leaning towards is this something to do with the Nuget package and using it with DI. I have tested the library away from this project (In a console application) and can confirm it's working as it should.
I have also created a local class library as a test to, to see if I could inject a data access class into the page and I can confirm that this works without an issue, which suggests to me that DI is working, just not with my Nuget package.
I did have a look into CORS, given that the Nuget package is accessing an external domain, and setup the following simple CORS policy in the app:
builder.Services.AddCors(policy =>
{
policy.AddPolicy("OpenCorsPolicy", opt =>
opt.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
Which is added to the app after the AddRouting call like so:
app.UseCors("OpenCorsPolicy");
However again, this wasn't the solution so if anyone is able to point me in the right direction with where I may be going wrong with this or offer any advice, I would be most grateful.
EDIT 1 - Provides details #mason queried
Regarding SystemEndpoint, the constructor is being injected with 2 things, as below:
public SystemEndpoint(IApiHelper apiHelper, IOptions<UriConfigModel> uriOptions)
{
_apiHelper = apiHelper;
_uriOptions = uriOptions.Value;
}
My Nuget Library is dependant on the following:
Azure.Identity
Azure.Security.KeyVault.Secrets
Microsoft.AspNet.WebApi.Client
Microsoft.Extensisons.Options.ConfigurationExtensions
EDIT 2 - Doing some further testing with this I have added a simple Endpoint class to my Nuget library, which returns a string with a basic message, as well as returning the values of the 2 UriConfig properties as below. I added this test to 1) sanity check that my DI was working correctly, and 2) check the values that are being assigned from appsettings to my UriConfig Object.
public class TestEndpoint : ITestEndpoint
{
private readonly IOptions<UriConfigModel> _uriConfig;
public TestEndpoint(IOptions<UriConfigModel> uriConfig)
{
_uriConfig = uriConfig;
}
public string TestMethod()
{
return $"You have successfully called the test method\n\n{_uriConfig.Value.Release} / {_uriConfig.Value.Version}";
}
}
However when adding in the dependency of IApiHelper into the Ctor, the method then breaks and fails to load the page. Looking into ApiHeloer, the Ctor has a dependency being injected into it of IApiConfigHelper. Looking at the implementation, the Ctor of ApiConfigHelper is setting up the values and parameters of the HttpClient that should make the REST calls to the external Api.
Now I believe what is breaking the code at this point is a call I'm making to Azure Key Vault, via REST, to pull out the secret values to connect to the Api. The call to KeyVault is being orchestrated via the following method, making use of the Azure.Security.KeyVault.Secrets Nuget Package, however I assume that at the heart of it, it's making a REST call to Azure on my behalf:
private async Task<KeyVaultSecret> GetKeyVaultValue(string secretName = "")
{
try
{
if (_secretClient is not null)
{
var result = await _secretClient.GetSecretAsync(secretName);
return result.Value;
}
}
catch (ArgumentException ae)
{
Console.WriteLine(ae.Message);
}
catch (Azure.RequestFailedException rfe)
{
Console.WriteLine(rfe.Message);
}
return new(secretName, "");
}
So that's where I stand with this at the moment. I still believe it could be down to CORS, as it seems to be falling over when making a call to an external service / domain, but I still can say 100%. As a closing thought, could it be something as simple as when I call call the above method, it's not being awaited????
So after persisting with this it seems like the reason it was failing was down to "awaiting" the call to Azure KeyVault, which was happening indirectly via the constructor of ApiConfigHelper. The resulting method for getting KeyVault value is now:
private KeyVaultSecret GetKeyVaultValue(string secretName = "")
{
try
{
if (_secretClient is not null)
{
var result = _secretClient.GetSecret(secretName);
if (result is not null)
{
return result.Value;
}
}
}
catch (ArgumentException ae)
{
Console.WriteLine(ae.Message);
}
catch (Azure.RequestFailedException rfe)
{
Console.WriteLine(rfe.Message);
}
return new(secretName, "");
}
I am now able to successfully make calls to my library and return values from the Api it interacts with.
I can also confirm that this IS NOT a CORS issue. Once I saw that removing the await was working, I then removed the CORS policy declarations from the service and the app in my Blazor's start-up code and everything continued to work without an issue.
As a final note, I must stress that this is only seems an issue when using the library with Blazor (possibly webApi projects) as I am able to use the library, awaiting the Azure call just fine in a console application.
I am shifting from Razor views to Angular 4, and trying to figure out how to pass global constants from the server to Angular without relying on Ajax calls.
So the server constants will be transaction status for example:
Id: 1->Active
Id: 2-> Inactive
Id: 3->Cancelled etc
So these statuses are saved in the db and are used to query various transactions, Thus will be required in lots of components
In Razor views, I used to pass these values together with the viewmodel. But in Angular currently I can see two options:
Make Ajax calls in ngOnInit of each component that requires these constants
Make a static model to hold these values
Option 1 increases the number of server calls by quite a bit -> so I am trying to avoid this.
Option 2 will require me to change status in multiple places in my application if a new status is added for example, which i am also not fond of.
I am looking for a way to send all my constants to Angular as the application loads or page is reloaded for example.
You need to use ReplaySubject
as per rxjs documentation
ReplaySubject:Represents an object that is both an observable sequence as well as an observer. Each notification is broadcasted to all subscribed
Look at this code snippet
export class GlobalConstants{
Status:number[];
}
import { Observable, ReplaySubject } from 'rxjs';
import { GlobalConstants } from '../models/GlobalConstants';
#Injectable()
export class YourService {
//This line will cache the latest list you will get from the server
private dataSubject = new ReplaySubject<GlobalConstants>();
//you will use this observer in your components to subscribe to the getStatus result
yourStatusList$: Observable<GlobalConstants> = this.dataSubject.asObservable();
constructor(private http: Http) {
this.getStatus()
}
getStatus() {
return this.http.get('url').subscribe(res => {
this.dataSubject.next(res);
})
}
export class ExampleComponent {
public statusList;
public constructor(private _yourService: YourService) {
this.getStatus();
}
getStatus(): void {
this._yourService.yourStatusList$.subscribe(
result => {
this.statusList = result;
}
)
}
}
what will happen is when angular create the service it will call getStatus method one time per the app life cycle and then fetch your status list from the server then u will need to subscribe in your components to yourStatusList$ , for each subscrbition you will get latest cached list and if the list changed in your server u just need to call YourService.getStatus then u will fetch the status list again and all component subscribed to this observer will get notified by the new list
let's take your two challenges
1-Make Ajax calls in ngOnInit of each component that requires these constants
-by using this code your app will make one call to the server to fetch status list so u don't need to make Ajax call in ngOnInit of each component
2-Make a static model to hold these values will require me to change status in multiple places in my application if a new status is added
-if new status is added you just need to call YourService.getStatus one time in any place in your code and all components subscribed to your yourStatusList will get notified by the new status list
NOTE: you must n't use providers: [yourService] in your component cause if u used it it will create a new object and will not use the global object , just add your service in #NgModule providers and use component constructor to inject the service object
It may be best to have a service cache the information in a local variable. Then, when you inject the service into your components, and one calls a service function, the service checks the local variable. If something is in the variable, use it, if not, load the data and cache it for later use.
Since the service is a singleton, the data should only load once unless you create some mechanism to timeout the value. So, the first time the service is called, the data will be fetched. After that, the local variable (below called globals) should be used.
Service:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class GlobalsService {
private globals: any;
constructor(private httpClient: HttpClient) { }
getGlobals(): any {
if (this.globals) {
return this.globals;
} else {
// call your API to get global data from DB
this.httpClient.get<any>('...').subscribe((data: any) => {
this.globals = data;
return this.globals;
});
}
}
}
Component using the service:
import { GlobalsService } from './../globals.service';
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-tester',
templateUrl: './tester.component.html',
styleUrls: ['./tester.component.css']
})
export class TesterComponent implements OnInit {
constructor(private globalsService: GlobalsService) { }
ngOnInit() {
// Do something here with the globals from the service
const gbls = this.globalsService.getGlobals();
if (gbls) {
// ... maybe put these in a variable for later use, what ever you need
}
}
}
Doing this will keep you from having to do the Ajax call you mention, and avoid you have to keep code in more than one place. The service pattern offers a nice central place to keep this data for the lifetime of the application. All you need to do is inject the service into the component, or other services, where it is needed.
You can add you constants as attributes on your app element inside you razor view
<app someatt="{ your json data here }">Loading...</app>
then on you app's root component access them like this:
export class AppComponent implements OnInit {
constructor(
private el: ElementRef
) {
}
ngOnInit() {
console.log(this.el.nativeElement.attributes["someatt"].value);
}
}
then you can have a global service with its statuses data set here on ngOnInit and consumed in all your components
I did it in my rc1 project like:
User.Claims.ElementAt(#).Value
But after I switched to rtm it wouldn’t work anymore. When I debug the Razor view the object looks the same but User.Claims is just empty. Any idea what the reason could be.
Assuming you have claims attached to the current principal. In your Razor view:
#((ClaimsIdentity) User.Identity)
This will give you access to the ClaimsIdentity of the current user. In an effort to keep your claims fetching clean you may want to create an extension method for searching claims.
public static string GetSpecificClaim(this ClaimsIdentity claimsIdentity, string claimType)
{
var claim = claimsIdentity.Claims.FirstOrDefault(x => x.Type == claimType);
return (claim != null) ? claim.Value : string.Empty;
}
Then you can just access whatever claim you want with:
#((ClaimsIdentity) User.Identity).GetSpecificClaim("someclaimtype")
Hope this helps.
Quick search for claims identity in razor view came up with a similar question and answer:
MVC 5 Access Claims Identity User Data
Tested in .net core 2.2
in the razor page :
#User.FindFirst("nameOfClaim").Value
In Core 3.0, use view authorization.
In Startup.cs:
services.AddAuthorization(options =>
{
options.AddPolicy("Policy_Name", x => x.RequireClaim("Policy_Name"));
});
At the top of your UI file where you are inserting the conditional element, insert:
#using Microsoft.AspNetCore.Authorization
#inject IAuthorizationService AuthorizationService
Then inside the body use:
#if ((await AuthorizationService.AuthorizeAsync(User, "Policy_Name")).Succeeded){
//show ui element
}
View-based authorization in ASP.NET Core MVC
You can achieve this with the following code in your view :
if(User.FindFirst("MyClaim")?.Value == "some_value")
{
... Show concerned UI block
}
Altough, if you use policies (as it's the recommended way), I suggest to define policies in your Startup.cs/Program.cs and use injected IAuthorizationService to call AuthorizeAsync :
if((await AuthorizationService.AuthorizeAsync(User, "MyClaim")).Succeeded)
{
... Show concerned UI block
}
This way is better as it use defined policies, which can validates many different values.
I'm trying to get the URL parameters into my component to search using an externsre service. I read I can use angular2 routes and get params using RouteParams but I don't think that's what I need because the first page of my angular application is a Razor view.
I'll try to explain better.
My URL looks like:
http://dev.local/listing/?city=melbourne
I'm loading my root component in a razor view with something like:
<house-list city = "#Request.Params["city"]"></house-list>
then on my root component I have:
export class AppComponent implements OnInit {
constructor(private _cityService: CityService) { }
response: any;
#Input() city: any;
ngOnInit() {
this.getHouses();
}
getHouses() {
this.__cityService.getHousesByCity(this.city)
.subscribe(data => this.response = data);
}
}
So I was expecting that the 'city' in my component gets the string passed from the razor view but it's undefined.
What am I doing wrong? Is there a way of getting the params without using the routing? If not, what is the right way of using razor?
Angular doesn't provide special support for this. You can use How can I get query string values in JavaScript?
From what I have seen Location is planned to be reworked to provide support for get query params even without using the router.
I'm currently working on an ASP.NET MVC project to which some AngularJS was added - including some AngularJS directives.
I need to add to an AngularJS directive a MVC partial view. Obviously,
#Html.Partial("_PartialView", {{name}})
doesn't work.
So far all my searches online provided no help.
Any idea how I could render a partial view inside an Angular directive?
Thanks!
Angular exists strictly on the client side whereas MVC views exist on the server side. These two cannot interact directly. However, you could create an endpoint in which your partial view is returned as HTML. Angular could call this endpoint, retrieve the HTML, and then include it inside a directive.
Something like this:
app.directive("specialView", function($http) {
return {
link: function(scope, element) {
$http.get("/views/partials/special-view") // immediately call to retrieve partial
.success(function(data) {
element.html(data); // replace insides of this element with response
});
}
};
});
app.directive("myDirective", ['', function () {
return {
restrict: 'A',
scope: {
foo: '='
},
templateUrl: '/home/_myDirectivePartialView',
}]
} }]);
Just need to use templareURL and specify the route to get the partial view.