Spartacus Compiling error in new custom paragraph component - sap-commerce-cloud

Getting below error when compiling new custom paragraph component
**
ERROR in src/app/custom-page/new-para/new-para.component.html:7:12 - error TS2769: No overload matches this call.
The last overload gave the following error.
Argument of type 'CmsComponentData' is not assignable to parameter of type 'Promise'.
Type 'CmsComponentData' is missing the following properties from type 'Promise': then, catch, [Symbol.toStringTag], finally
7 <p *ngIf="(cmsComponent | async)?.data as data" [innerHTML]="data.content">
~~~~~~~~~
src/app/custom-page/new-para/new-para.component.ts:8:16
8 templateUrl: './new-para.component.html',
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Error occurs in the template of component NewParaComponent.
**
files used:
"new-para.component.ts"
import { Component, OnInit, ChangeDetectionStrategy } from '#angular/core';
import { Observable } from 'rxjs';
import { PromotionResult, CmsService, CmsParagraphComponent } from '#spartacus/core';
import { PromotionService, CmsComponentData } from '#spartacus/storefront';
#Component({
selector: 'app-new-para',
templateUrl: './new-para.component.html',
styleUrls: ['./new-para.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NewParaComponent {
cartPromotion$: Observable<PromotionResult[]> = this.promotionService.getOrderPromotionsFromCart();
cmsComponent: CmsParagraphComponent;
constructor(protected promotionService: PromotionService,
public component: CmsComponentData<CmsParagraphComponent>,
public cmsService: CmsService) { }
}
"new-para.component.html"
<p *ngIf="(cmsComponent | async) as data" [innerHTML]="data.name"></p>
"custom-page.module.ts"
ConfigModule.withConfig({
cmsComponents: {
CMSParagraphComponent: {
component: NewParaComponent,
}
}
new console error after trying both options:
core.js:6228 ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(AppModule)[CmsComponentData -> CmsComponentData -> CmsComponentData]:
NullInjectorError: No provider for CmsComponentData!
NullInjectorError: R3InjectorError(AppModule)[CmsComponentData -> CmsComponentData -> CmsComponentData]:
NullInjectorError: No provider for CmsComponentData!
at NullInjector.get (core.js:1085)
at R3Injector.get (core.js:16955)
at R3Injector.get (core.js:16955)
at R3Injector.get (core.js:16955)
at NgModuleRef$1.get (core.js:36329)
at Object.get (core.js:33972)
at getOrCreateInjectable (core.js:5848)
at Module.ɵɵdirectiveInject (core.js:21103)
at NodeInjectorFactory.NewParaComponent_Factory [as factory] (new-para.component.ts:12)

Thanks for posting the code snippets, that helps. Based on the error it looks like the code is missing a few bits and pieces, but I get the idea.
Here's what you should do:
Option 1
make the component constructor arguments private or protected as you don't use it in the template
Assign the data$ property from the component.data$ to the property cmsComponent. You can do this directly when creating the property or during het oninit lifecycle hook
Observe the data$ property
component$: Observable<CmsParagraphComponent> = this.component.data$;
constructor(protected component: CmsComponentData<CmsParagraphComponent>) { }
<p *ngIf="(component$ | async) as data" [innerHTML]="data.name"></p>
Option 2
Use the public service directly in the html
Bind to the data$ property of the service
constructor(public component: CmsComponentData<CmsParagraphComponent>) { }
<p *ngIf="(component.data$ | async) as data" [innerHTML]="data.name"></p>
While option 2 is shorter, I prefer option 1. It scales better, in case you need to operate on the data stream and bring other streams into the mix.

Related

Cannot find name 'FontFace' when using Vexflow in Angular

Im having trouble when trying to integrate VexFlow in my Angular 12 project.
First I installed the library using:
npm install vexflow
Then I created a simple component and simply used the native api example from the documentation to try out the library. Here is the component:
import {AfterViewInit, Component, ElementRef, OnInit} from '#angular/core';
import {Renderer, Stave} from "vexflow";
#Component({
selector: 'app-note-display',
templateUrl: './note-display.component.html',
styleUrls: ['./note-display.component.scss']
})
export class NoteDisplayComponent implements OnInit, AfterViewInit {
constructor(private elRef: ElementRef) { }
ngOnInit(): void {
}
ngAfterViewInit() {
const renderer = new Renderer(this.elRef.nativeElement.querySelector('#note-display'), Renderer.Backends.SVG);
// Configure the rendering context.
renderer.resize(500, 500);
const context = renderer.getContext();
// Create a stave of width 400 at position 10, 40.
const stave = new Stave(10, 40, 400);
// Add a clef and time signature.
stave.addClef('treble').addTimeSignature('4/4');
// Connect it to the rendering context and draw!
stave.setContext(context).draw();
}
}
But Im getting the following error when serving my application:
Error: node_modules/vexflow/build/types/src/font.d.ts:132:92 - error TS2304: Cannot find name 'FontFace'.
132 static loadWebFont(fontName: string, woffURL: string, includeWoff2?: boolean): Promise<FontFace>;
I tried this solution from another question, but it was not helpful in my case.
As the other solution suggest, #types/css-font-loading-module is probably what you need.
Install it with npm i -D #types/css-font-loading-module and make sure that it is included in your tsconfig.json.
It must be added to the types field, it should like similar to this:
{
"compilerOptions": {
/* other compiler options... */
"types": ["css-font-loading-module"]
}
}

Why can't I use my nestjs custom decorator in a constructor?

I want to create a custom decorator to return tenant information. I have the following code:
export type TenantInfo = {
token: string
id: string
}
export const TenantInfo = createParamDecorator(
(data: unknown, context: ExecutionContext): TenantInfo => {
// todo use context to get from auth header
return { id: '1', token: 'todo' }
},
);
Now i register it in the tenant service:
#Injectable({ scope: Scope.REQUEST })
export class TenantService {
private readonly _tenantInfo: TenantInfo;
constructor(
#TenantId() tenantInfo: TenantInfo,
) {
this._tenantInfo = tenantInfo;
logger.log(`Received GRPC message from tenant id ${tenantInfo.id}`);
}
}
And when I run my code it says:
Error: Nest can't resolve dependencies of the TenantService (?).
Please make sure that the argument Object at index [0] is available in
the TenantModule context.
Potential solutions:
If Object is a provider, is it part of the current TenantModule?
If Object is exported from a separate #Module, is that module imported within TenantModule? #Module({
imports: [ /* the Module containing Object */ ]
I'm not sure how to get around this. It's saying register it in the module but I get errors if I try putting TenantInfo in the providers array...
Type '(...dataOrPipes: unknown[]) => ParameterDecorator' is not
assignable to type 'Provider'
How do you do this? All I want is to get some details from the request context.
createParamDecorator only creates decorators that work for route handlers (Controllers, Resolvers, or Gateways). For something that can be injected at the constructor level of a service you can either inject the request, via #Inject(REQUEST), or you can create a custom request scoped provider and custom decorator like so:
export const InjectTenantId = () => Inject('TENANTID');
export const tenantIdProvider = {
provide: 'TENANTID',
inject: [REQUEST],
scope: Scope.REQUEST,
useFactory: (request) => getTenantIdFromRequest(request)
}
And now in your constructor you can do #InjectTenantId() private readonly tenantInfo: TenantInfo.

Angular 8 adding component dynamically "viewContainerRef of undefined" error

I'm learning Angular and attempting to add a component to my HTML programmatically. However, I get the following error:
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'viewContainerRef' of undefined
The following are the key parts of code.
My Placeholder directive:
import { Directive, ViewContainerRef } from '#angular/core';
#Directive({
selector: '[appPlaceholder]'
})
export class PlaceholderDirective {
constructor(public viewContainerRef: ViewContainerRef) {}
}
Main.component.ts:
export class MainComponent implements OnInit {
#ViewChild(PlaceholderDirective, {static: false, read: ViewContainerRef}) alertHost: PlaceholderDirective;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
ngOnInit(): void {
this.showMyComp();
}
private showMyComp() {
const testCmpFactory = this.componentFactoryResolver.resolveComponentFactory(TestComponent);
const hostViewContainerRef = this.alertHost.viewContainerRef;
hostViewContainerRef.clear();
hostViewContainerRef.createComponent(testCmpFactory);
}
}
Main.component.html:
<ng-template appPlaceholder></ng-template>
I have narrowed the issue down to const hostViewContainerRef = this.alertHost.viewContainerRef; in MainComponent.ts. The problem is that this.alertHost is null but I have been unable to figure out why.
I know this is an old question, but I had the same issue a few days ago:
Method that uses alertHost is invoked from ngOnInit().
To make sure that the references injected by #ViewChild are present, always write initialization code using ngAfterViewInit().
In addition to invoking showMyComp from ngAfterViewIinit, I had to update #ViewChild to following:
#ViewChild(PlaceholderDirective, {static: false}) alertHost: PlaceholderDirective;
(it is possible that this part is caused by different angular version)

Angular 6 Error... calendar.component has no exported member 'CalendarComponent'

I report here a issue, hoping some pious souls could help..
Here's the problem, Node gives me this ERROR:
ERROR in src/app/app.module.ts(11,10): error TS2305: Module
'"V:/Projects/Kompany/source/kompany-war/src/main/websrc/test2/test-kompany/src/app/components/calendar/calendar.component"'
has no exported member 'CalendarComponent'.
src/app/components/calendar/calendar.component.ts(27,3): error TS2390:
Constructor implementation is missing.
src/app/components/oculus/oculus.component.ts(2,34): error TS2307:
Cannot find module 'components/calendar/calendar.component'.
src/app/components/top-menu/top-menu.component.ts(2,10): error TS2305:
Module
'"V:/Projects/Kompany/source/kompany-war/src/main/websrc/test2/test-kompany/src/app/components/calendar/calendar.component"'
has no exported member 'CalendarComponent'.
Here's the sitauation..
I have this on the app module...
`import { BrowserModule } from '#angular/platform-browser';`
`import { NgModule } from '#angular/core';`
`import { CalendarModule, DateAdapter } from 'angular-calendar';`
`import { adapterFactory } from 'angular-calendar/date-adapters/date-fns';`
import { CalendarComponent } from'./components/calendar/calendar.component';
What I'm trying to do is putting a calendar library into my site.
The error is on a component I created ( Calendar Component ) and on which I put all the information I found on the web in order to try it..
https://mattlewis92.github.io/angular-calendar/docs/index.html
this is where I downloaded the library.
I'm new to Angular so I'm giving all the info I have, hope not to be boring.
Into Calender Component here is what's there...
import { Component, ChangeDetectionStrategy, OnInit } from '#angular/core';
import { CalendarEvent } from 'angular-calendar';
#Component({
selector: 'app-calendar',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './calendar.component.html'
})
export class DemoComponent {
view: string = 'month';
viewDate: Date = new Date();
events: CalendarEvent[] = [
{
title: 'Click me',
start: new Date()
},
{
title: 'Or click me',
start: new Date()
}
];
constructor ()
ngOnInit() {
}
eventClicked({ event }: { event: CalendarEvent }): void {
console.log('Event clicked', event);
}
}
I read on the site that this error is due to Angular's infrastructure as I got..
And some answers to similar topics where about ordering the imports, but they are already in order.
I really don't know what to do.. hope someone will be helpful..
Lots of smiles
I see a number of errors, all of which angular is complaining about.
There is no exported class called 'CalendarComponent' in the file
you listed - in that file you export a class called 'DemoComponent'
instead.
In line 27 of your component.ts file you have no definition for the
constructor. You have constructor () but you should have
constructor() {} if you want it to be empty.
It looks like you are also attempting to import the wrongly named
class in two other components, named 'oculus.component.ts', and
'top-menu.component.ts'

Using WinJS with Angular2

Trying to use a property to configure a WinJS control from within Angular2, so far I couldn't find a solution, e.g. this code below is throwing 'Can't bind to 'dataWinOptions' since it isn't a known property of the '' element'.
#View({
template: `<div id="rating" data-win-control='WinJS.UI.Rating' [data-win-options]='jsonRating'></div>`
})
class MyRating {
rating: number;
get jsonRating() {
return '{averageRating: ' + this.rating + '}';
}
constructor() {
this.rating = 1.5;
}
}
Any hint?
#ericdes about your last comment I think this would be the best option. Assuming you have Nth WinJS controls
Consider the following code. I'm specifying differents values for the averageRating property in options.
<winjs-control [options]="{averageRating: '1.5', someMoreOptions : 'x'}"></winjs-control>
<winjs-control [options]="{averageRating: '1.4', differentOptionsForThisOne :'Z'}"></winjs-control>
<winjs-control [options]="{averageRating: '1.3'}"></winjs-control>
<winjs-control [options]="{averageRating: '1.2'}"></winjs-control>
<winjs-control [options]="{averageRating: '1.1'}"></winjs-control>
// more and more...
The component will read this options property and will pass it to the view. Forget about the directive, it isn't necessary after all.
We pass options through attr.data-win-options since it isn't a property of div but an attribute.
#Component({
selector : 'winjs-control',
properties : ['options']
})
#View({
template : `<div data-win-control="WinJS.UI.Rating" [attr.data-win-options]="jsonRating"></div>`,
})
class WinJSComponent implements OnInit, AfterViewInit {
constructor() {}
// We specify onInit so we make sure 'options' exist, at constructor time it would be undefined
// And we stringify it or otherwise it will pass an object, we need to convert it to a string
onInit() {
this.jsonRating = JSON.stringify(this.options);
}
// We process WinJS after view has been initialized
// this is necessary or 'data-win-options' won't be fully processed
// and it will fail silently...
afterViewInit() {
WinJS.UI.processAll();
}
}
Here's a plnkr for this case.
That's one option and IMHO I think this is the easiest one. Another one, having the same HTML content, would be to communicate the parent with its children and I haven't tested your case with that approach.

Resources