I have created LWC for showing accounts and created one Lighting web component and one apex class shown below.
Html file
<template>
<lightning-card title="Showing Account(tile)">
<template if:true={successResponse}>
<template for:each={accounts} for:item="account">
<li key={account.id}>
<div class="slds-p-around_medium lgc-bg">
<lightning-tile label={account.Name} href="/path/to/somewhere">
<p class="slds-truncate" title={account.Phone}>Account's Phone Number is :
{account.Phone}
</p>
</lightning-tile>
</div>
</li>
</template>
</template>
</lightning-card>
</template>
Js.file
import { LightningElement, wire, track } from "lwc";
import allAccount from "#salesforce/apex/AcountManager.getAccount"
export default class AccountManagerApex extends LightningElement {
#wire(allAccount)
accountRecrd;
successResponse (){
if(this.accountRecrd){
return true;
}
return false;
}
}
and Class is:
public with sharing class AcountManager {
#AuraEnabled( cacheable = true)
public static List<Account> getAccount(){
return [SELECT Id,Name,Phone,Website FROM Account Limit 10];
}
}
When I try to deploy to my org using VSCode I'm getting the below error.
Error
force-app\main\default\lwc\accountManagerApex\accountManagerApex.js No MODULE named markup://lgc:bg found : [markup://c:accountManagerApex]
Can anyone tell me how to fix this issue/
Thanks in advance,
You have a custom css class lgc-bg. Please make sure your css file has a period before the selector
/* incorrect, lwc tries to find lgc:bg component */
lgc-bg : {}
/* correct */
.lgc-bg {}
Try setting <isExposed>true</isExposed> inside meta.xml file of the component
Related
I have a record view for which the id is from apex class and then wired it and assigned to a variable, but the record is not displaying. below is my code.
apex class to get the record id of the record owned by the user
#AuraEnabled(cacheable=true)
public static Wallet__c getWalletInfo(){
String userId = UserInfo.getUserId();
return [SELECT Id From Wallet__c WHERE OwnerId =: userID LIMIT 1];
}
.js class - assigned the data to getId
import getWalletInfo from '#salesforce/apex/CustomWalletHandler.getWalletInfo';
import wallet from '#salesforce/schema/Wallet__c';
import walletName from '#salesforce/schema/Wallet__c.Name';
import walletBalance from '#salesforce/schema/Wallet__c.balance__c';
import walletFrom from '#salesforce/schema/Wallet__c.added_from__c';
export default class CustomWallet extends LightningElement {
objectName = wallet;
name = walletName;
balance = walletBalance;
from = walletFrom;
getId;
isUser = true;
#wire (getWalletInfo,{})
walletData({error, data}){ //data is record Id
if(data){
this.getId = data;
console.log(this.getId);
this.isUser = false;
}else if(error) {
// error handling
console.error(error.body.message);
}
}
}
html - if user has no recordc Create Wallet will display and if user has record only the record will display.
<template>
<lightning-card>
<lightning-tabset>
<template if:true={isUser}>
<lightning-tab label="Create a Wallet">
<lightning-record-form
object-api-name="Wallet__c"
columns="2"
layout-type="Full"
></lightning-record-form>
</lightning-tab>
</template>
<template if:false={isUser}>
<lightning-tab label="My Wallet">
<lightning-record-view-form
record-id={getId}
object-api-name={objectName}>
<div class="slds-box">
<lightning-output-field field-name={name}>
</lightning-output-field>
<lightning-output-field field-name={balance}>
</lightning-output-field>
<lightning-output-field field-name={from}>
</lightning-output-field>
</div>
</lightning-record-view-form>
</lightning-tab>
</template>
</lightning-tabset>
</lightning-card>
I think you overcomplicated it a bit. What do you get when you put simpler references to object & fields?
<lightning-record-view-form
record-id={getId}
object-api-name="Wallet__c">
<div class="slds-box">
<lightning-output-field field-name="Name"></lightning-output-field>
<lightning-output-field field-name="Balance__c"></lightning-output-field>
<lightning-output-field field-name="Added_From__c"></lightning-output-field>
</div>
</lightning-record-view-form>
And your apex method is returning whole Wallet__c object, not just the Id. With just 1 field populated but still. You can tell by seeing curly braces in your console.log's output. So you need either this.getId = data.Id; in JS or record-id={getId.Id} in HTML. Or make Apex return just an Id variable (and null if no results found I guess?)
I am trying to implement the prototype pattern in a Sharepoint Framework small example, I took the sample from here:
https://mertarauh.com/tutorials/typescript-design-patterns/prototype-pattern/
and adapted as below:
class Employee {
private totalperMonth: number;
constructor(public name: string, public hiredDate: Date, public dailyRate: number){
this.totalperMonth = dailyRate * 20 ;
}
public display(): string{
return "Employee " + this.name + " earns per month: " + this.totalperMonth;
}
public clone():Employee{
var cloned = Object.create(Employee || null);
Object.keys(this).map((key: string) => {
cloned[key]= this[key];
});
return <Employee>cloned;
}
}
export default Employee;
and the component
import * as React from 'react';
import styles from './Prototype.module.scss';
import { IPrototypeProps } from './IPrototypeProps';
import { escape } from '#microsoft/sp-lodash-subset';
import Employee from './Employee';
export default class Prototype extends React.Component<IPrototypeProps, {}> {
public render(): React.ReactElement<IPrototypeProps> {
const today = new Date();
let employee1: Employee = new Employee('Luis', today, 500);
let employee2 = employee1.clone();
employee2.dailyRate = 550;
return (
<div className={ styles.prototype }>
<div className={ styles.container }>
<div className={ styles.row }>
<div className={ styles.column }>
<span className={ styles.title }>Welcome to SharePoint!</span>
<p className={ styles.subTitle }>Customize SharePoint experiences using Web Parts.</p>
<p className={ styles.description }>{escape(this.props.description)}</p>
<span className={ styles.label }>{employee1.display()}</span>
<span className={ styles.label }>{employee2.display()}</span>
</div>
</div>
</div>
</div>
);
}
}
However I get in the console this error:
What am I missing?
There are several problems with this approach.
Classes are first-class citizens in TypeScript, alternative approaches to inheritance can make type safety much less straightforward.
Object.create is low-level tool, it isn't a good idea to use it unless you know what you're doing and why. The actual mistake here is that wrong prototype was chosen (Employee instead of Employee.prototype), so name property refers to function name property, which is read-only and shouldn't be reassigned. A proper way to implement clone would be:
public clone():Employee{
return Object.assign(Object.create(Employee.prototype), this);
}
The fact that it ignores class constructor is a doubtful decision because a constructor may contain logic that differs from one applied in clone.
React promotes functional programming as a substitute to OOP, it's beneficial to maintain application state as plain objects rather than class instances, this may help to avoid design flaws that will be costly to fix in future. There's nothing that would really require a class in this case. The same thing could be expressed as:
const createEmployee = (name, hiredDate, dailyRate) => ({
name,
hiredDate,
dailyRate,
totalperMonth: dailyRate * 20
});
...
let employee1 = createEmployee('Luis', today, 500);
let employee2 = {...employee1, dailyRate: 550};
Notice that totalperMonth isn't recalculated neither in Employee class nor in createEmployee factory function. This may be undesirable (see the note on a constructor above).
json array localstorage format
Hello, I'm developing an ionic app. I'm new in Ionic and Typescript.
As you can see in the image below I'm parsing from an API my data in a json array.
On ts file I'm writing this code
`
public categoryDetails:any;
const datacat = JSON.parse(localStorage.getItem('categoryData'));
this.categoryDetails = datacat.categoryData;`
And in my html file when I write this
<h1 class="business-top">Business of the category {{categoryDetails.name}}</h1>
I get the error message "TypeError: Cannot read property 'name' of undefined"
I know that I don't read the attribute "name" correctly. How can I do this one?
Moreover, how can I display Businesses which associate with the spesific term_id of the category?
In this example you need to do that
<h1 class="business-top">Business of the category {{categoryDetails?.name}}</h1>
public categoryDetails: any;
this.categoryDetails = localStorage.getItem('categoryData');
or you can use ionic storage. it is better if you are using ionic.
https://ionicframework.com/docs/storage/
import { Storage } from '#ionic/storage';
export class MyApp {
public categoryDetails: any;
constructor(private storage: Storage) { }
...
// set a key/value
storage.set('categoryData', 'Max');
// Or to get a key/value pair
storage.get('categoryData').then((val) => {
this.categoryDetails = val;
});
}
I finally fingured it out.
#Kilomat thanks for your help and your advice about using ionic storage. That saved me from futured problems.
About my json file that's what I did.
In my HTML code
<ion-grid no-margin>
<h1 class="business-top">Επιχειρήσεις της κατηγορίας {{businessData.name}} {{businessData.term_id}}</h1>
<ion-list>
<ion-item class="col col-50" *ngFor="let c of BusinessCategoryDetails" text-wrap (click)="product(c)">
<div *ngIf="c.term_id == businessData.term_id">
<h2>{{c.post_title}} {{c.term_id}}</h2> <img width="80" height="80" src="{{c.guid}}">
</div>
</ion-item>
</ion-list>
</ion-grid>
And in my TS code
`var businessCategory: categories = navParams.get("businessCategory");
console.log(businessCategory); /exports the Selected category/
var newData = JSON.stringify(businessCategory);
this.businessData = JSON.parse(newData);`
Which takes properties and values from my previous category page. TS code
`categories: [categoryDetails] = null;
gotobusiness(category: categories){
this.navCtrl.push(BusinessPage, { businessCategory: category});
}`
I've inject HTML from an object that I created and than using a pipe I by pass the angular security to display input fields, text area etc..
I am inject an input field, some text and a <p contenteditable="true"> change me </p> that you can edit.
3) How to than track changes when user is updating the inner of and push it back to the object ?
If the user changes the HTML inside [innerHTML]="item.htmldata" is there a way to track it?
obj: Array<any> = [
{ htmldata: '<div> <strong> There should be a input field below </strong> <input type="text" value="search" /></div>' },
{ htmldata: '<div> <strong> me to, how are you </strong></div>'}
]
I am running through an ngFor loop and binding the data through angular [innerHTML]="item.htmldata"
<div *ngFor="let item of obj" >
{{item.htmldata | json }}
<div [innerHTML]="item.htmldata" | safeHtml></div>
<br />
<hr>
</div>
**SafeHTML pipe**
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer, SafeResourceUrl, SafeUrl} from '#angular/platform-browser';
DomSanitizer
#Pipe({name: 'safeHtml'})
export class Safe {
constructor(private sanitizer: DomSanitizer){}
transform(style) {
return this.sanitizer.bypassSecurityTrustHtml(style);
//return this.sanitizer.bypassSecurityTrustStyle(style);
// return this.sanitizer.bypassSecurityTrustXxx(style); - see docs
}
}
Plunker: http://plnkr.co/edit/ADeAEz81a07Cl2yrDGqQ?p=preview
Replace your sanitizedHtmlProperty by this code.
Sample Code
public get sanitizedHtmlProperty() : SafeHtml {
return this._sanitizer.bypassSecurityTrustHtml(this._originalHtmlProperty);
}
Plunker
Edit
You can use Blur and keyup
import {Directive, ElementRef, Input, Output, EventEmitter, OnChanges} from "#angular/core";
#Directive({
selector: '[contenteditableModel]',
host: {
'(blur)': 'onEdit()',
'(keyup)': 'onEdit()'
}
})
export class ContentEditableDirective implements OnChanges {
#Input('contenteditableModel') model: any;
#Output('contenteditableModelChange') update = new EventEmitter();
constructor(
private elementRef: ElementRef
) {
console.log('ContentEditableDirective.constructor');
}
ngOnChanges(changes) {
console.log('ContentEditableDirective.ngOnChanges');
console.log(changes);
if (changes.model.isFirstChange())
this.refreshView();
}
onEdit() {
console.log('ContentEditableDirective.onEdit');
var value = this.elementRef.nativeElement.innerText
this.update.emit(value)
}
private refreshView() {
console.log('ContentEditableDirective.refreshView');
this.elementRef.nativeElement.textContent = this.model
}
}
Reference
Edit 2
Here is code for tracking changes Plunker
I have an Angular 2 component that displays a list of Items, and that registers to a service that publishes events. The problem is that even if I don't do anything when I receive an event, Angular updates the view (or at least do something when, I guess, it shouldn't).
Here is a plunker.
As you can see in the console, the "getTitle()" method of my items is called each time my service publishes a message.
Even if I don't register to my service and if my component doesn't implement the MyServiceListener interface, getTitle is called each time the service gets a message. If I don't give the service to my component in its constructor, everything is fine. So, something seems wrong with my dependency injection, but what?
Here is the relevant code of the plunker:
My service and its listeners interface:
export interface MyServiceListener {
onMessage(_message: any);
}
export class MyService {
private m_listener: MyServiceListener;
constructor() {
window.setInterval(() => {
if (this.m_listener !== undefined) {
this.m_listener.onMessage("Hi");
}
}, 500);
}
setListener(_listener: MyServiceListener) { this.m_listener = _listener; }
}
The Item class:
export class Item {
m_title: string;
constructor(_title: string) {
this.m_title = _title;
}
getTitle(): string { console.log("getTitle"); return this.m_title; }
}
My component:
#Component({
selector: 'my-app',
template : `
<div>
<ul>
<li *ng-for="#item of m_items">
{{item.getTitle()}}
</li>
</ul>
</div>
`
})
export class App implements TestBugAngularServiceListener {
private m_items: Array<Item> = new Array<Item>();
constructor(_communicationService: MyService) {
this.m_items.push(new Item("A"));
this.m_items.push(new Item("B"));
this.m_items.push(new Item("C"));
_communicationService.setListener(this);
}
onMessage(_message: any) {
}
}
bootstrap(App, [MyService]).catch(err => console.error(err));
Both articles : Change detection and Angular immutability explain a lot of thing about how Angular 2 detect changes of object, and how the tree of components in angular 2 is traversed to perform data binding...
In your sample, I think your component "my-app" can be considered to be "Immutable", so changing its "change detection strategy" to OnPush solve your problem.
You can write this :
#Component({
selector: 'my-app',
changeDetection: ChangeDetectionStrategy.OnPush,
template : `
<div>
<ul>
<li *ng-for="#item of m_items">
{{item.getTitle()}}
</li>
</ul>
</div>
`
})
And after adding the import to ChangeDetectionStrategy, databinding of "my-app" will not be computed after each browser event, but only when its input change, so never...