Load generic angular dynamic component - angular-dynamic-components

I am trying to make a dynamic component for the popup to create view and edit page for different content. I have made a popup component in which I want to pass a new component name and title of the page. However, I am not getting new component data in the popup component. Please have a look at code, if you need any more detail, please ask. Thanks in Advance.
I have tried to inject service in another component and it gets data on button click, but in the popup component, I am not getting data. For now, I am doing just console.log data to popup.component.ts file but there is no result in console.log.
popup.service.ts
export class PopupService {
isShowing = false;
private popup = new Subject();
loadingPopup = new Subject();
outputEmitter = new Subject();
popupContent: any = {
isShowing: false,
content: null,
contentParams: PopupModel
}
constructor() { }
public getPopup() {
return this.popup;
}
public showLoading(isLoading: boolean = true) {
this.loadingPopup.next(isLoading);
}
public create(component: any, parameters?: PopupModel): any {
this.showLoading(true);
this.popupContent.isShowing = true;
this.popupContent.content = component;
this.popupContent.contentParams = parameters;
this.popup.next(this.popupContent);
console.log(this.popupContent)
}
}
Popupcomponent.ts
export class PopupComponent implements OnInit, OnDestroy {
public popupObservable: Subscription;
constructor(private popupService: PopupService) { }
ngOnInit() {
this.popupObservable = this.popupService.getPopup().subscribe((popupContent: any) => {
console.log(popupContent)
//code here to use createDynamicComponent method }
}
private createDynamicComponent(component: Type<any>): void {
//code here using ComponentFactoryResolver and ViewContainerRef to create dynamic component
}
ngOnDestroy() {
if (this.popupObservable && !this.popupObservable.closed) {
this.popupObservable.unsubscribe();
}
}
}
This is the code where the dynamic component is being called and the popup should be created.
Component.ts
AddRecord(){
this.popupService.create( NewRecordComponent, {
title: 'Add',
emitterMethod: 'saved'
})
}
component.html
<button (click)="AddRecord()">Add</button>

You are not emitting a value from your subject anywhere.
You would need to call popup.next(popupContent).
However, I don't think you have the right model here.
If you're not doing any async calls (api, filesystem etc..) in your getPopup method, then just return the popupContent directly
public getPopup() : {} {
return this.popupContent;
}
You should also define an interface for your popupContent somewhere so you can import it and use a strong interface to avoid runtime errors.
e.g.
export interface IPopupContent {
isShowing: boolean;
content: string;
contentParams: PopupModel;
}
Also, don't expose subjects directly, rather expose an observable linked to the subject.
See When to use asObservable() in rxjs?

Related

Using Blazor with Electron.NET, how can I execute code inside a component whenever I click on a MenuItem?

I am using Blazor Server.
Inside my startup I am configuring the MenuItems like this:
var menu = new[]
{
new MenuItem
{
Label = "File", Submenu = new[]
{
new MenuItem
{
Label = "Save",
Accelerator = "CmdOrCtrl+S",
Enabled = false,
Click = () =>
{
// How do I execute code inside my component?
},
}
}
}
};
Electron.Menu.SetApplicationMenu(menu);
My component lives on the root and is always rendered. It has a simple method called Save which I want to call.
public async Task SaveProject()
{
await Project.Save();
}
Isn't there some kind of event inside the static Electron class that I could use? Something like OnMenuItemClicked.
Having a static property that I could access inside my component would not only be bad design, it would prevent me from accessing any instance properties.
I came up with a more or less applicable solution myself.
I have created a singleton service IMenuItemService which I am using in both my Startup and my Component. Since MenuItems do not have an ID per se, I created a seperate Enum MenuItemType to seperate them. The service looks something like this:
public class MenuItemService : IMenuItemService
{
public Action<MenuItemType> MenuItemClicked { get; set; }
private Dictionary<MenuItemType, MenuItem> ConfigurableMenuItems { get; }
public MenuItemService()
{
ConfigurableMenuItems = new Dictionary<MenuItemType, MenuItem>();
InitializeMenuItem(MenuItemType.Close, "Close Project", null, false);
}
private void InitializeMenuItem(MenuItemType type, string label, string accelerator, bool enabled)
{
ConfigurableMenuItems.Add(type, new MenuItem
{
Label = label,
Accelerator = accelerator,
Enabled = enabled,
Click = () => { MenuItemClicked?.Invoke(type); },
});
}
public void SetEnabled(MenuItemType menuItemType)
{
ConfigurableMenuItems[menuItemType].Enabled = true;
RenderMenuItems();
}
public void SetDisabled(MenuItemType menuItemType)
{
ConfigurableMenuItems[menuItemType].Enabled = false;
RenderMenuItems();
}
public void RenderMenuItems()
{
Electron.Menu.SetApplicationMenu(new[]
{
new MenuItem
{
Label = "File", Submenu = new []
{
ConfigurableMenuItems[MenuItemType.Close]
}
}
});
}
}
With this approach I can call menuItemService.RenderMenuItems() from anywhere in my application including Startup.cs while in my Components I am setting the MenuItemClicked Action in order to listen to clicks.
[Inject]
public IMenuItemService MenuItemService { get; set; }
private void InitializeMenuItemActions()
{
MenuItemService.SetEnabled(MenuItemType.Close);
MenuItemService.MenuItemClicked = type =>
{
if (type == MenuItemType.Close)
{
ProjectManager.CloseProject();
NavigationManager.NavigateTo("/");
}
};
}
In my case I intentionally used an Action Property instead on an EventHandler since I do not need multiple listeners for my MenuItem.

How to Call Application service from Web Api in ASP.Net Boilerplate MVC project?

ASP.NET Boilerplate (.Net Framework v4.7.2 & MVC)
I am creating a web api controller(AgentInfoController) for an application service class(AgentInfoAppService), which depends on a repository IAgentInfoRepository. From AgentInfoController, I am able to call a method from AgentInfoAppService by passing IAgentInfoRepository in the constructor like this new AgentInfoAppService(_agentInfoRepository).GetAgentCampaignswithCode but I am not sure if this is the correct way?
We have a working code from mvc layer where we call application service class(dynamic web api layer) without adding any references to Repository like below. Is there a way we could add a dependency injection so I don't have to add the repository in each application service calls from web api layer?Please advise.
jQuery.ajax({
url: "/api/services/app/AgentInfoAppService/GetAgentCampaignswithCode?agentCode=100",
type: "GET",
contentType: "application/json; charset=utf-8"
})
public class AgentInfoAppService : ApplicationService, IAgentInfoAppService
{
private readonly IAgentInfoRepository _agentInfoRepository;
public AgentInfoAppService(IAgentInfoRepository agentInfoRepository)
{
_agentInfoRepository = agentInfoRepository;
}
public GetAgentCodeCampaignOutput GetAgentCampaignswithCode(string agentCode, bool? defaultCampaign)
{
var agentCampaigns = _agentInfoRepository.GetAgentCampaignswithCode(agentCode, defaultCampaign);
return new GetAgentCodeCampaignOutput()
{
AgentCodeCampaign = Mapper.Map<List<AgentCodeCampaignDto>>(agentCampaigns)
};
}
}
public class AgentInfoController : AbpApiController
{
private readonly IAgentInfoRepository _agentInfoRepository;
[HttpGet]
public GetAgentCodeCampaignOutput GetAgentCampaignswithCode(string agentCode, bool? defaultCampaign)
{
return new AgentInfoAppService(_agentInfoRepository).GetAgentCampaignswithCode(agentCode, defaultCampaign);
}
}
When you run the project Abp creates all Methods from ApplicationService layer and you can use it. You don't need to create the method again in the controller.
Here is an example of what I'm saying
I have an ApplicationServie method like this
public class CiudadAppService : AsyncCrudAppService<AdozonaCiudad, CiudadDto, int, PagedAndSortedRequest, CiudadDto, CiudadDto>, ICiudadAppService
{
private readonly ICiudadManager _ciudadManager;
public CiudadAppService(IRepository<AdozonaCiudad> repository, ICiudadManager ciudadManager) : base(repository)
{
_ciudadManager = ciudadManager;
}
public override async Task<CiudadDto> CreateAsync(CiudadDto input)
{
var result = await _ciudadManager.RegistrarOActualizar(ObjectMapper.Map<AdozonaCiudad>(input));
return MapToEntityDto(result);
}
}
In this AppService I have one Method called CreateAsync this method can be used from javascript like this.
(function ($) {
var _ciudadService = abp.services.app.ciudad;
var _$form = $('form[name=Index]');
function save() {
_$form.validate({
invalidHandler: function (form, validator) {
var errors = validator.numberOfInvalids();
if (errors) {
var firstInvalidElement = $(validator.errorList[0].element);
$('html,body').scrollTop(firstInvalidElement.offset().top - 100);
firstInvalidElement.focus();
}
},
rules: {
Ciudad: "required"
},
messages: {
Ciudad: "El valor del campo es requerido"
},
highlight: function (element) {
$(element).parent().addClass('error-validation')
},
unhighlight: function (element) {
$(element).parent().removeClass('error-validation')
}
});
if (!_$form.valid()) {
return;
}
var ciudad = _$form.serializeFormToObject(); //serializeFormToObject is defined in main.js
abp.ui.setBusy(_$form);
_ciudadService.create(ciudad).done(function () {
location.reload(true); //reload page to see edited role!
abp.notify.success('Registro correcto');
$(".save-button").html('Guardar');
}).always(function () {
abp.ui.clearBusy(_$form);
});
}
}
As you can see there the important part is: var _ciudadService = abp.services.app.ciudad; with this you're creating a service and you can access to all the methods that you have in ApplicationService
I hope it could help you, if you need further help please leave a comment!

UWP how to change ListView binding Observable Colletion outside the page class

I have a Page, on which I have a list view and its item source is bound to a Observable Collection property, which is ofcourse non-static.
My question is that how can I access or change that collection outside of the Page class on which that listview resides.
I want to change that collection outside, because I wanna change or add new items to that ListView from other pages as well.
I'm solving this problem with a static Property in my App.xaml.cs.
So I can access my Repository/Collection from any Page I want.
Every time I navigate to the page, I'm going to access App.MyCollection through my binded property.
You can create a property where your getter would create a new ObervableCollection(App.MyCollection);
if the current private member is null.
App.xaml.cs
private static IEnumerable<OrderHistory> _orderHistory;
public static IEnumerable<OrderHistory> OrderHistory
{
get
{
if (_orderHistory == null)
_orderHistory = new ObservableCollection<OrderHistory>();
return _orderHistory;
}
set
{
if (_orderHistory != value)
_orderHistory = value;
}
}
}
CodeBehind.xaml.cs
private ObservableCollection<OrderHistory> _orderHistoryList;
public ObservableCollection<OrderHistory> OrderHistoryList
{
get
{
if (_orderHistoryList == null)
_orderHistoryList = new ObservableCollection<OrderHistory>(App.WorkOrderHistory);
return _orderHistoryList;
}
}
When you want to change something on the properties of the class in the list, then just access it in App.OrderHistory.
The only think you need to take care of is in your OrderHistory Class:
public class OrderHistory : INotifyPropertyChanged
{
private bool _isAvailable;
public bool IsAvailable {
get
{
return _isAvailable;
}
set
{
if(_isAvailable != value)
{
_isAvailable = value;
this.NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Component Attribute is none - timing issue

I want to do a HTTPRequest based upon a url, which is set in a component attribute. I tried it like shown below, but the dataUrl is always none. It seems as the constructor of the component is executed before the attributes, which are set in the html, are available to the component.
How can I tell the HTTPRequest to wait until the dataUrl variable is available?
component.dart
class TableData {
static List data = [];
TableData() {}
//GETTERS
List get getData => data;
}
#NgComponent(
selector: 'jstable',
templateUrl: 'jstable/jstable_component.html',
cssUrl: 'jstable/jstable_component.css',
publishAs: 'cmp'
)
class JSTableComponent {
#NgAttr('name')
String name;
#NgAttr('data-url')
String dataUrl;
TableData _table_data = new TableData();
final Http _http;
bool dataLoaded = false;
JSTableComponent(this._http) {
_loadData().then((_) {
dataLoaded = true;
}, onError: (_) {
dataLoaded = false;
});
}
//GETTERS
List get data => _table_data.getData;
//HTTP
Future _loadData() {
print("data url is");
print(dataUrl);
return _http.get(dataUrl).then((HttpResponse response) {
TableData.data = response.data['data'];
});
}
}
.html
<jstable name="myjstablename" data-url="table-data.json"></jstable>
Implement NgAttachAware and put your code in the attach method. The attributes are already evaluated when attach is called.

Inline Interface implementation in Actionscript

Is something like this possible in Actionscript?
Java:
URLFetcherFactory.setCreator(
new IURLFetcherCreator() {
public IURLFetcher create() {
return new URLFetcher();
}
}
);
Actionscript:
?
I've been wondering about this and have been unable to find anything that indicates it's possible. Figured if it was possible, I'd be able to find an answer here. Thanks! Stackoverflow rocks!
You cannot create an instance of an interface. You can, however, create a factory class:
public class URLFetcherCreator : IURLFetcherCreator {
private var _cls : Class;
public URLFetcherCreator(Class cls) {
this._cls = cls;
}
public function create() : IURLFetcher
{
return new cls();
}
}
Alternatively, change setCreator to accept a Function that returns an IURLFetcher:
URLFetcherFactory.setCreator(
function() : IURLFetcher {
return new URLFetcher();
}
);
Try this:
URLFetcherFactory.setCreator(
new IURLFetcherCreator() {
public function create():IURLFetcher {
return new URLFetcher();
}
}
);
You can't use anonymous inner classes in AS3.
For special cases like callbacks you can use Function instead of anonymous inner classes.
Java:
interface Callback {
void done(String info);
}
class Service {
void process(Callback callback);
}
...
myService.process(new Callback() {
void done(String info) {
// trace(info);
}
}
AS3:
class Service {
public function process(callback:Function):void;
}
...
myService.process(function(info:String):void {
trace(info);
});

Resources