Calling Apex Class from Lwc only it is getting Saved - apex

I have create one custom object. Using a LWC component, I try to create one record but when try to save it from apex, only ID is getting printed not the Name.
I am not getting why only Id is getting printed not the name.
Could anybody please help me ? Would be Appreciable.
LWC Component
import { LightningElement, track, api } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import insertDe from '#salesforce/apex/insertEvent.insertDe';
import Detail_OBJECT from '#salesforce/schema/Detail__c';
export default class insertEvent extends LightningElement {
// #api childName;
#track conRecord = Detail_OBJECT;
handleChildNameChange(event) {
this.conRecord.childName = event.target.value;
}
createRec() {
insertDe({
de: this.conRecord
})
.then(result => {
// Clear the user enter values
this.conRecord = {};
// Show success messsage
this.dispatchEvent(new ShowToastEvent({
title: 'Success!!',
message: 'Contact Created Successfully!!',
variant: 'success'
}), );
})
.catch(error => {
this.error = error.message;
});
}
}
<template>
<lightning-card title="Create Contact Record">
<template if:true={conRecord}>
<div class="slds-m-around--xx-large">
<div class="container-fluid">
<div class="form-group">
<lightning-input
label="Child Name"
name="childName"
type="text"
value={conRecord.childName}
onchange={handleChildNameChange}
></lightning-input>
</div>
</div>
<br />
<lightning-button label="Submit" onclick={createRec} variant="brand"></lightning-button>
</div>
</template>
</lightning-card>
</template>
Apex code
public with sharing class insertEvent {
#AuraEnabled
public static void insertDe(Detail__c de) {
try {
insert de;
} catch (Exception e) {
System.debug('--->'+e);
}
}
}

If you're using an LWC component then I suggest to also use Lightning Data Service.
To answer your specific issue, after an insert DML, only the Id field is returned. If you need other fields, then you need to run a query. This is because trigger / workflow / process builder can change some field value.

My suggestion if you want insert record directly from LWC component, you should use Lightning Data Service. But you need to execute some custom code or insert record from apex method, then you should pass only the data LWC component and create object in apex method then insert it.
public static void insertDe(String name) {
Detail__c obj = new Detail__c();
obj.childName = name;
try {
insert obj;
} catch (Exception e) {
System.debug('--->'+e);
}
}
Only pass the name from lwc component according to your posting code.

Related

LWC Create and Dispatch Event Does Not Work

Attempting to simulate a mouse click on one button, by clicking on another button. The goal is to reuse the behavior of a single custom button throughout the page. Why is the dispatchEvent not working?
How can a click on <c-custom-button> be simulated?
parentApp.html
<template>
<div>
<c-custom-button
label="New">
</c-custom-button>
</div>
<div>
<lightning-button
label="Call New"
onclick={simulateClick}>
</lightning-button>
</div>
</template>
parentApp.js
import { LightningElement, track, api } from 'lwc';
export default class App extends LightningElement {
cButtonElement;
simulateClick() {
this.cButtonElement = this.template.querySelector('c-custom-button');
let clickEvent = new CustomEvent('click');
this.cButtonElement.dispatchEvent(clickEvent);
}
}
customButton.html
<template>
<lightning-button
label={label}
icon-name="utility:new"
onclick={handleClick}>
</lightning-button>
</template>
customButton.js
import { LightningElement, track, api } from 'lwc';
export default class App extends LightningElement {
#api label;
handleClick() {
this.label = 'CLICKED!'
}
}
Thanks to Nathan Shulman for helping with this.
Add call to child method in parentApp.js
simulateClick() {
this.cButtonElement = this.template.querySelector('c-custom-button');
this.cButtonElement.handleClick();
}
Add #api decorator to method in customButton.js
#api handleClick() {
this.label = 'CLICKED!'
}

How do I programmatically set focus to dynamically created FormControl in Angular2

I don't seem to be able to set focus on a input field in dynamically added FormGroup:
addNewRow(){
(<FormArray>this.modalForm.get('group1')).push(this.makeNewRow());
// here I would like to set a focus to the first input field
// say, it is named 'textField'
// but <FormControl> nor [<AbstractControl>][1] dont seem to provide
// either a method to set focus or to access the native element
// to act upon
}
How do I set focus to angular2 FormControl or AbstractControl?
I made this post back in December 2016, Angular has progressed significantly since then, so I'd make sure from other sources that this is still a legitimate way of doing things
You cannot set to a FormControl or AbstractControl, since they aren't DOM elements. What you'd need to do is have an element reference to them, somehow, and call .focus() on that. You can achieve this through ViewChildren (of which the API docs are non-existent currently, 2016-12-16).
In your component class:
import { ElementRef, ViewChildren } from '#angular/core';
// ...imports and such
class MyComponent {
// other variables
#ViewChildren('formRow') rows: ElementRef;
// ...other code
addNewRow() {
// other stuff for adding a row
this.rows.first().nativeElement.focus();
}
}
If you wanted to focus on the last child...this.rows.last().nativeElement.focus()
And in your template something like:
<div #formRow *ngFor="let row in rows">
<!-- form row stuff -->
</div>
EDIT:
I actually found a CodePen of someone doing what you're looking for https://codepen.io/souldreamer/pen/QydMNG
For Angular 5, combining all of the above answers as follows:
Component relevant code:
import { AfterViewInit, QueryList, ViewChildren, OnDestroy } from '#angular/core';
import { Subscription } from 'rxjs/Subscription';
// .. other imports
export class MyComp implements AfterViewInit, OnDestroy {
#ViewChildren('input') rows: QueryList<any>;
private sub1:Subscription = new Subscription();
//other variables ..
// changes to rows only happen after this lifecycle event so you need
// to subscribe to the changes made in rows.
// This subscription is to avoid memory leaks
ngAfterViewInit() {
this.sub1 = this.rows.changes.subscribe(resp => {
if (this.rows.length > 1){
this.rows.last.nativeElement.focus();
}
});
}
//memory leak avoidance
ngOnDestroy(){
this.sub1.unsubscribe();
}
//add a new input to the page
addInput() {
const formArray = this.form.get('inputs') as FormArray;
formArray.push(
new FormGroup(
{input: new FormControl(null, [Validators.required])}
));
return true;
}
// need for dynamic adds of elements to re
//focus may not be needed by others
trackByFn(index:any, item:any){
return index;
}
The Template logic Looks like this:
<div formArrayName="inputs" class="col-md-6 col-12"
*ngFor="let inputCtrl of form.get('phones').controls;
let i=index; trackBy:trackByFn">
<div [formGroupName]="i">
<input #input type="text" class="phone"
(blur)="addRecord()"
formControlName="input" />
</div>
</div>
In my template I add a record on blur, but you can just as easily set up a button to dynamically add the next input field. The important part is that with this code, the new element gets the focus as desired.
Let me know what you think
This is the safe method recommend by angular
#Component({
selector: 'my-comp',
template: `
<input #myInput type="text" />
<div> Some other content </div>
`
})
export class MyComp implements AfterViewInit {
#ViewChild('myInput') input: ElementRef;
constructor(private renderer: Renderer) {}
ngAfterViewInit() {
this.renderer.invokeElementMethod(this.input.nativeElement,
'focus');
}
}
With angular 13, I did it this way:
import { Component, OnInit, Input } from '#angular/core';
import { FormGroup, Validators, FormControl, FormControlDirective, FormControlName } from '#angular/forms';
// This setting is required
const originFormControlNgOnChanges = FormControlDirective.prototype.ngOnChanges;
FormControlDirective.prototype.ngOnChanges = function ()
{
this.form.nativeElement = this.valueAccessor._elementRef.nativeElement;
return originFormControlNgOnChanges.apply(this, arguments);
};
const originFormControlNameNgOnChanges = FormControlName.prototype.ngOnChanges;
FormControlName.prototype.ngOnChanges = function ()
{
const result = originFormControlNameNgOnChanges.apply(this, arguments);
this.control.nativeElement = this.valueAccessor._elementRef.nativeElement;
return result;
};
#Component({
selector: 'app-prog-fields',
templateUrl: './prog-fields.component.html',
styleUrls: ['./prog-fields.component.scss']
})
export class ProgFieldsComponent implements OnInit
{
...
generateControls()
{
let ctrlsForm = {};
this.fields.forEach(elem =>
{
ctrlsForm[elem.key] = new FormControl(this.getDefaultValue(elem), this.getValidators(elem));
});
this.formGroup = new FormGroup(ctrlsForm);
}
...
validateAndFocus()
{
if (formGroup.Invalid)
{
let stopLoop = false;
Object.keys(formGroup.controls).map(KEY =>
{
if (!stopLoop && formGroup.controls[KEY].invalid)
{
(<any>formGroup.get(KEY)).nativeElement.focus();
stopLoop = true;
}
});
alert("Warn", "Form invalid");
return;
}
}
}
Reference:
https://stackblitz.com/edit/focus-using-formcontrolname-as-selector?file=src%2Fapp%2Fapp.component.ts
Per the #Swiggels comment above, his solution for an element of id "input", using his solution after callback:
this.renderer.selectRootElement('#input').focus();
worked perfectly in Angular 12 for an element statically defined in the HTML (which is admittedly different somewhat from the OP's question).
TS:
#ViewChild('licenseIdCode') licenseIdCodeElement: ElementRef;
// do something and in callback
...
this.notifyService.info("License Updated.", "Done.");
this.renderer.selectRootElement('#licenseIdCode').focus();
HTML:
<input class="col-3" id="licenseIdCode" type="text" formControlName="licenseIdCode"
autocomplete="off" size="40" />
If you are using Angular Material and your <input> is a matInput, you can avoid using .nativeElement and ngAfterViewInit() as follows:
Component Class
import { ChangeDetectorRef, QueryList, ViewChildren } from '#angular/core';
import { MatInput } from '#angular/material/input';
// more imports...
class MyComponent {
// other variables
#ViewChildren('theInput') theInputs: QueryList<MatInput>;
constructor(
private cdRef: ChangeDetectorRef,
) { }
// ...other code
addInputToFormArray() {
// Code for pushing an input to a FormArray
// Force Angular to update the DOM before proceeding.
this.cdRef.detectChanges();
// Use the matInput's focus() method
this.theInputs.last.focus();
}
}
Component Template
<ng-container *ngFor="iterateThroughYourFormArrayHere">
<input #theInput="matInput" type="text" matInput>
</ng-container>

Error reporting from a LoadableDetachableModel doesn't work

If an error occurs while loading a model, what is the correct way to present that to the user? It seems like adding a feedback component to the page and using error() is the correct way to do this, but when I do that, I get this error:
Caused by: org.apache.wicket.WicketRuntimeException: Cannot modify component hierarchy after render phase has started (page version cant change then anymore)
at org.apache.wicket.Component.checkHierarchyChange(Component.java:3572)
at org.apache.wicket.Component.addStateChange(Component.java:3501)
at org.apache.wicket.Component.error(Component.java:1254)
at com.prosc.wicket.LoadableDetachableModelErrorTest$1.load(LoadableDetachableModelErrorTest.java:21)
at com.prosc.wicket.LoadableDetachableModelErrorTest$1.load(LoadableDetachableModelErrorTest.java:17)
at org.apache.wicket.model.LoadableDetachableModel.getObject(LoadableDetachableModel.java:121)
at org.apache.wicket.Component.getDefaultModelObject(Component.java:1605)
Here is my Java code:
package com.prosc.wicket;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.LoadableDetachableModel;
public class LoadableDetachableModelErrorTest extends WebPage {
public LoadableDetachableModelErrorTest() {
add( new FeedbackPanel( "feedback" ) );
add( new Label("dynamicText", new LoadableDetachableModel<String>() {
#Override
protected String load() {
String result = "Hello World";
error( "Uh oh, some imaginary problem happened" );
return result;
}
}));
}
}
Here is my HTML markup:
<html>
<body>
<div wicket:id="feedback" />
<div wicket:id="dynamicText" />
</body>
</html>
The expected result is to see a blank page with the error message displayed to the user.
I'm not really sure what would be the 'best' way, but you can set the error on the Session. But you will still need to think about what you want to display in your components that use your model..
public class LoadableDetachableModelErrorTest extends WebPage {
public LoadableDetachableModelErrorTest() {
add( new FeedbackPanel( "feedback" ) );
add( new Label("dynamicText", new LoadableDetachableModel<String>() {
#Override
protected String load() {
if (failure) {
Session.get().error( "Uh oh, some imaginary problem happened" );
return null;
}
else
return result;
}
}));
}
}
You can also throw a nice typed Exception and handle it in a custom RequestCycle (see full info here: http://wicketinaction.com/2008/09/building-a-smart-entitymodel/)
public class MyRequestCycle extends WebRequestCycle
{
#Override
public Page onRuntimeException(Page page, RuntimeException e)
{
if (e instanceof EntityNotFoundException)
{
return new EntityNotFoundErrorPage((EntityNotFoundException)e);
}
else
{
return super.onRuntimeException(page, e);
}
}
}
When Wicket starts rendering components, they are no longer allowed to change their state.
You'll have to check for errors in your component's #onConfigure().

Play framework 2.3.2: How to render List or Map in scala template

I am trying to display a list of strings as a repeatable input text control in my view. Here is my model:
public class User {
#Required
public String email;
public String password;
public List<String> products;
}
Controller:
public static Result index() {
Form<User> userForm = Form.form(User.class);
Map<String,String> anyData = new HashMap<String,String>();
List<String> listProduct = new ArrayList<String>();
listProduct.add("p1");
listProduct.add("p2");
userForm = userForm.fill(new User("bob#gmail.com", "secret", listProduct));
return ok(views.html.index.render(userForm));
}
View:
#(userForm: Form[models.User])
#import helper._
#import models.User
#import scala._
#main("Welcome to Play") {
<form id="formUser" action="/user/apply" method="post">
#inputText(userForm("email"))
#inputText(userForm("password"))
#for(product <- userForm("products")) {
<input type="text" name="#product" value="#product">
}
<input type="submit" value="submit"/>
</form>
}
Error is:
value map is not a member of play.data.Form.Field
I also tried form helper #repeat. But its just not working.
#repeat(userForm("products"), min = 0) {
product => #inputText(product)
}
Error:
not found: value product
I am using Play 2.3.2 in Java.
Any idea whats going wrong?
Suraj
You just need to remember that view templates are parsed to Scala functions and code is escaped with # character. Your second solution works fine. In this case you just need to format your code in proper way and it works like a charm.
#repeat(userForm("products"), min = 0) { product =>
#inputText(product)
}

Reusing wicket component in a form

I have built a wicket component that contains input/labels and methods to change presentation (required, enabled, etc.). The components render fine, but what happens is when the form submits I see only 1 form parameter 'input', and it's the last InputRow component.
InputRow.html
<html xmlns:wicket="http://wicket.apache.org">
<head>
<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
<wicket:panel>
<label wicket:id="label">abc: <span class="req">*</span></label>
<span class="input">
<input wicket:id="input" type="text" id="name"></input>
</span>
<span wicket:id="input_feedback"></span>
</wicket:panel>
</body>
</html>
InputRow.java
package com.wicket;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.feedback.FeedbackMessage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
public class InputRow extends Panel{
#SuppressWarnings("unused")
private String id;
public InputRow(String id, String label) {
super(id);
this.id = id;
Label memberIdLabel = new Label("label",label);
memberIdLabel.setEscapeModelStrings(false)
.add(new AttributeAppender("for", new Model<String>(id),""));
add(memberIdLabel);
TextField<String> name = new TextField<String>("input");
name.setType(String.class)
.setMarkupId(id)
.setOutputMarkupId(true);
add(name);
add(new Label("input_feedback",""));
}
public InputRow disable()
{
get("input")
.setEnabled(false)
.add(new AttributeAppender("class", new Model<String>("disabled"),""));
get("label")
.add(new AttributeAppender("class", new Model<String>("disabled"),""));
return this;
}
public InputRow required()
{
Model model = (Model)get("label").getInnermostModel();
StringBuffer label = new StringBuffer((String)model.getObject());
label.append(" <span class=\"req\">*</span>");
model.setObject(label);
((TextField)get("input")).setRequired(true);
return this;
}
#Override
protected void onBeforeRender() {
super.onBeforeRender();
Label feedback = (Label)get("input_feedback");
if (get("input").getFeedbackMessage() != null)
{
feedback.setDefaultModel(new Model<String>("Required"));
}
}
}
Adding to the form component
add(new InputRow("name","Name:").required());
edit
I didn't set up a ListView or repeater since I know what rows / fields I want to add to the form at build time.
Your InputFields are missing their models. This way, wicket doesn't know where to store the formdata. If you add models to the fields they will be populated automatically.
There's not just one form parameter submitted. The submits are of the named like name:input, name2:input, ...
But as Nicktar suggests in the comment you should use a model to bind the value of the form component to your entity object. You have to accept an IModel in the constructor and use it in the constructor of TextField.
A better approach to what you are trying to do is to write a Behavior which adds decorating markup for your FormComponent. That way it works for more than just simple text input fields and you can fully customize the instances of your FormComponents.
It could look like this:
public class FormComponentBehavior extends Behavior {
#Override
public void bind(Component component) {
if (!(component instanceof FormComponent)) {
throw new IllegalArgumentException();
}
}
#Override
public void beforeRender(Component component) {
FormComponent<?> fc = (FormComponent<?>) component;
Response r = component.getResponse();
r.write("<label" + (fc.isRequired() ? " class='required'" : "") + ">");
r.write(fc.getLabel().getObject());
r.write("</label>");
r.write("<span class='input'>");
}
#Override
public void afterRender(Component component) {
component.getResponse().write("</span>");
// if feedback errors write them to markup...
}
}
Then you have to add this behavior to your FormComponent instances.
Maybe the problem with your form is that your input text fields have all the same id. Try using attribute 'name' instead of 'id'