I have a field in my Xpage that I want to be readOnly and submitted to the database upon Save. This field gets a value from a Ajax call
Setting the property ReadOnly to true creates a <span> instead of a readonly field.
Setting the property "Show disabled control for readonly" creates a input text field with the property readonly=readonly.
<input type="text" class="xspInputFieldEditBox" readonly="readonly" name="view:_id1:_id2:_id3:_id28:callbackFieldControlSet:InstrumentShort" id="view:_id1:_id2:_id3:_id28:callbackFieldControlSet:InstrumentShort">
It will not be saved to the database.
According to my knowledge readonly fields are submitted but not disabled.
What I'm I doing wrong here?
/M
You can add the readonly attribute with the attr-property:
<xp:inputText id="inputText2" value="#{document1.ReadOnly}">
<xp:this.attrs>
<xp:attr name="readonly" value="true" />
</xp:this.attrs>
</xp:inputText>
Btw: The behaviour of the disabled and the readonly property is correct, because this is a definition on the server side. You want to edit the component with a value, that is why it must be allowed to accept values. Just disabling it on the client side has technically no effect.
I think this is a bug. You are right, the read only field should get saved. In version 8.5.1 when the property of "Show disabled control for readonly" was not present I used to set the field as readonly through JavaScript. Here's the code snippet:
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[function makeFieldReadOnly() {
document.getElementById("#{id:inputText2}").readOnly = true;
}
window.onload = makeFieldReadOnly;]]></xp:this.value>
</xp:scriptBlock>
In the above snippet the function makeFieldReadOnly marks the edit box inputText2 as readonly when page is loaded.
Related
I have p:textEditor like the following
<p:textEditor
id="editor"
widgetVar="editor"
value="#{xxxController.editorText}"
height="300"
placeholder="Enter your content"
toolbarVisible="false"/>
Have the following command button to add/append values to p:textEditor
<p:commandButton onclick="insertTag('[myValue]')" value="myValue" type="button" />
JavaScript
<script>
function insertTag( t )
{
PF( 'editor' ).insertText( t ) ;
}
</script>
But i get SCRIPT5007: Unable to get property 'insertText' of undefined or null reference when i try to click the <p:commandButton.
So how do we insert/append text using widgetVar or JavaScript to p:textEditor?
Version details
JSF 2.2,
PrimeFaces 6.2
You need to do this... Once you have the widget the "editor" variable is QuillJS object.
PF('editor').editor.insertText(0, 'Hello', 'bold', true);
See: https://quilljs.com/docs/api/#inserttext
See: https://quilljs.com/docs/api/#getlength
See Using the component ID as widgetVar name
[...] This will cause all original widget var functions to be completely unavailable because the variable editor is now referencing a HTMLDivElement instance which doesn't have the same functions as the original widget var like show(), etc. [...]
(Append a _vw to every widgetvar name, to eliminate that issue)
SCRIPT5007: Unable to get property 'insertText' of undefined or null reference
guess, that's exactly the cause, your code is not referencing the "editor"-widgetVar, but the "editor" html element, which does not know "insertText".
I'm learning Shopware and I got to something I can't figure out how to solve.
I'm writing a test plugin that adds an attribute to the customer. I've added the correspondent field to the Registration form and it saves its value to the db automatically, like I read somewhere in the docs.
Now I wanted to let the attribute be editable in the account profile page, after the password field. I managed to put the input there, and even show the value from the db. But when I change the value and save, the value its not updated. I don't know if it is just a matter of getting the field name right, or do I need to override something else. Or is it just not possible? Any help on how to achieve this would be greatly appreciated.
Relevant code below:
plugin bootstrap
public function install(InstallContext $context)
{
$service = $this->container->get('shopware_attribute.crud_service');
$service->update('s_user_attributes', 'test_field', 'string');
$metaDataCache = Shopware()->Models()->getConfiguration()->getMetadataCacheImpl();
$metaDataCache->deleteAll();
Shopware()->Models()->generateAttributeModels(['s_user_attributes']);
return true;
}
register/personal_fieldset.tpl
{extends file="parent:frontend/register/personal_fieldset.tpl"}
{block name='frontend_register_personal_fieldset_password_description'}
{$smarty.block.parent}
<div class="register--test-field">
<input autocomplete="section-personal test-field"
name="register[personal][attribute][testField]"
type="text"
placeholder="Test Field"
id="testfield"
value="{$form_data.attribute.testField|escape}"
class="register--field{if $errorFlags.testField} has--error{/if}"
/>
</div>
{/block}
account/profile.tpl
{extends file="parent:frontend/account/profile.tpl"}
{block name='frontend_account_profile_profile_required_info'}
<div class="profile--test-field">
<input autocomplete="section-personal test-field"
name="profile[attribute][testfield]"
type="text"
placeholder="Test Field"
id="testfield"
value="{$sUserData.additional.user.test_field|escape}"
class="profile--field{if $errorFlags.testField} has--error{/if}"
/>
</div>
{$smarty.block.parent}
{/block}
The form type that it's used on registration isn't the same you have on profile.
If you check \Shopware\Bundle\AccountBundle\Form\Account\PersonalFormType::buildForm, you can see
$builder->add('attribute', AttributeFormType::class, [
'data_class' => CustomerAttribute::class
]);
That means the attributes are included on form and they will be persisted. That's why you can save the value on registration form.
On profile you have \Shopware\Bundle\AccountBundle\Form\Account\ProfileUpdateFormType. And here the attribute isn't added to form builder.
How to extend the ProfileUpdateFormType?
Subscribe Shopware_Form_Builder on Bootstrap (or on a specific Subscriber class)
$this->subscribeEvent('Shopware_Form_Builder', 'onFormBuild');
Create the method onFormBuild to add your logic
public function onFormBuild(\Enlight_Event_EventArgs $event) {
if ($event->getReference() !== \Shopware\Bundle\AccountBundle\Form\Account\ProfileUpdateFormType::class) {
return;
}
$builder = $event->getBuilder();
$builder->add('attribute', AttributeFormType::class, [
'data_class' => CustomerAttribute::class
]);
}
With this approach all attributes are available on your profile form.
Other possibility you have is using the 'additional' property instead of 'attribute' and then subscribe a controller event or hook a controller action to handle your custom data.
I have a submit button, with the following HTML:
<button type="submit" class="back-button" data-direction="back">Back</button>
The controller method is defined like this:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(SearchModel searchModel, string SearchBy, string dataDirection)
{
// Code removed for simplicity...
}
When I click the button, my Model is populated, but the dataDirection property is not populated.
What is the correct way to access those data attributes?
This is not a ASP .NET MVC issue.For submit buttons, browsers only send the value of the "value" attribute when the form is submitted. The button's name is used as the parameter name and the value is used as the parameter value.
If you want to send additional parameters with the form based on which button is clicked, you have to use Javascript to listen for the 'click' event of the button and add your custom parameters to the post data or set some hidden field values before submission.
Easiest way is to put a hidden field in your form with name "Direction", listen button click set its value to 'back' or 'next'. And if you have a default value for the direction or you want the last direction value to be set to the hidden field after a postback, you may put a "Direction" property on your model.
Below code shows how you do it with jQuery:
<input type="hidden" name="Direction" value="#Model.Direction" id="fldDirection" />
<script type="text/javascript">
$("[type='submit']").on('click', function() {
if ($(this).data('direction')) {
$("#fldDirection").val($(this).data('direction'));
}
}
</script>
First, my scenario:
I have a form including several input fields. Some of them are hidden by default, because they are not really often needed:
<h:form>
<!-- with validators -->
<h:inputText>...</h:inputText>
<h:inputText>...</h:inputText>
<!-- show this fields only when the user wants to -->
<h:panelGroup rendered="#{!bean.hide}">
<h:inputText>...</h:inputText>
<h:inputText>...</h:inputText>
</h:panelGroup>
<h:commandButton value="Show / hide more fields" actionListener="#{bean.toogleHide}" />
<h:commandButton value="Submit" />
</h:form>
This would be the bean:
#SessionScoped
#ManagedBean
public class Bean {
// with getter
private boolean hide = true;
public void toogleHide(ActionEvent event) {
hide = !hide;
}
}
What i want to do:
Assume, a user fills the first to input fields with valid / invalid data. Now he wants to see the other inputfields that are hidden by default. So would press the command button "show / hide more fields".
=> is there a way the keep the non submitted values when pressing the "show / hide more fields" button?
My suggestion would be to always send the "hidden fields" from your JSF page, but make them display: none by default, using CSS. Then hide/unhide them with javascript only, e.g. jQuery or a small JS script on the page. The whole will be much more responsive, plus you don't have the issue you're fighting with.
There is a required field:
<xp:this.validators>
<xp:validateRequired
message="Required field. Please add some text.">
</xp:validateRequired>
</xp:this.validators>
Also, the value from this field is copied ( using the onChange event ) to other fields:
<xp:eventHandler event="onchange" submit="true"refreshMode="norefresh">
<xp:this.action><![CDATA[#{javascript:Cdoc.setValue("dlg_Localitate",Cdoc.getValue("txt_LocalitateCompanie"));
Cdoc.setValue("dlg_Localitate_1",Cdoc.getValue("txt_LocalitateCompanie"))}]]>
</xp:this.action>
</xp:eventHandler>
An inconvenient issue appears when I just click the field to fill it: the validation message appears. Is because the field initially is empty and the code I added is into the onChange event?
I'd like to use this field as required before users can save the doc.
I tried set the values by CSJS, but without a result...
var string = XSP.getElementById("#{id:inputText1}").value
XSP.getElementById("#{id:txt_LocalitateS}").value = string
XSP.getElementById("#{id:txt_LocalitateP}").value = string
Also, let say I enter a value for inputText1 and later on I enter a new value... How can I update automatically the other 2 fields with the new value?
I tried something like this:
<xp:inputText id="inputText1" value="#{Cdoc.txt_LocalitateCompanie}"
style="height:20.0px;width:122.0px;font-weight:bold;font-size:10pt;font-family:verdana"
required="true">
<xp:this.validators>
<xp:validateRequired message="Completarea localitatii este obligatorie.">
</xp:validateRequired>
</xp:this.validators>
<xp:typeAhead mode="full" minChars="1" ignoreCase="true"
id="typeAhead1">
<xp:this.valueList><![CDATA[#{javascript:#DbLookup(#DbName(),"vwLocalitati",Cdoc.txt_LocalitateCompanie,1,"[PARTIALMATCH]");}]]></xp:this.valueList>
</xp:typeAhead>
<xp:eventHandler event="onchange" submit="true"
refreshMode="norefresh">
<xp:this.action><![CDATA[#{javascript:Cdoc.setValue("dlg_Localitate",Cdoc.getValue("txt_LocalitateCompanie"));
Cdoc.setValue("dlg_Localitate_1",Cdoc.getValue("txt_LocalitateCompanie"))}]]></xp:this.action>
<xp:this.script><![CDATA[XSP.partialRefreshGet("#{id:txt_LocalitateS}", {
onComplete: function() {
XSP.partialRefreshGet("#{id:txt_LocalitateP}", {
onComplete: function(){ }
});
}
});]]></xp:this.script>
</xp:eventHandler>
</xp:inputText>
Thanks in advance
Two things here. First, you should disable validators for onChange event, therefore it won't display the validation error.
Second, when you use a CSJS script together with a SSJS, it will fire the CSJS one and if it returns true, proceed with the SSJS. So if you want your CSJS code run after SSJS, you can place it into oncomplete.
If I understood your question correctly, the following code would solve it.
<xp:inputText
id="inputText1"
value="#{Cdoc.txt_LocalitateCompanie}"
style="height:20.0px;width:122.0px;font-weight:bold;font-size:10pt;font-family:verdana"
required="true">
<xp:this.validators>
<xp:validateRequired
message="Completarea localitatii este obligatorie.">
</xp:validateRequired>
</xp:this.validators>
<xp:typeAhead
mode="full"
minChars="1"
ignoreCase="true"
id="typeAhead1">
<xp:this.valueList><![CDATA[#{javascript:#DbLookup(#DbName(),"vwLocalitati",Cdoc.txt_LocalitateCompanie,1,"[PARTIALMATCH]");}]]></xp:this.valueList>
</xp:typeAhead>
<xp:eventHandler
event="onchange"
submit="true"
refreshMode="norefresh"
disableValidators="true">
<xp:this.action><![CDATA[#{javascript:Cdoc.setValue("dlg_Localitate",Cdoc.getValue("txt_LocalitateCompanie"));
Cdoc.setValue("dlg_Localitate_1",Cdoc.getValue("txt_LocalitateCompanie"))}]]></xp:this.action>
<xp:this.onComplete><![CDATA[if(dojo.byId("#{id:txt_LocalitateP}")) {
XSP.partialRefreshGet("#{id:txt_LocalitateP}", {
onComplete: function() {
XSP.partialRefreshGet("#{id:txt_LocalitateS}", {
onComplete: function(){ }
});
}
});
}]]></xp:this.onComplete>
</xp:eventHandler>
</xp:inputText>
UPDATE: In your case, the field you want to refresh is on the second tab with partialRefresh="true". It means that at the time of partialRefreshGet, the target fields might not exist in the DOM. I have added a check now.
this is taken from my comments and put into an answer:
onChange events are generally frowned upon due to performance and user experience. If, however, the field is a listed control ie combobox it is not so dramatic. The following options/ideas are available
Take out the onChange() to test whether that makes a difference. If so, move your code.
Use an update button to change all the fields en masse also preventing information that is already inputted from being deleted unwanted-ly
create your own validation method and show/hide a label manually (hack-y)
Research how to manually put text into an errors control
If the field is in a dialog box, move the onChange() to the open/close methods of the dialog
FURTHER EDIT
An idea that I might suggest is using the xspDoc.getDocument(true) method to push all changes from the xpage to the background document. Something tells me that this might make a difference with the server reading the changes to the document and realizing that it is not empty.
ADDITIONAL IDEAS
I did not mention this because it is a bit more advanced, but should also get the job done assuming the refreshes are done. Even that is not that big of a deal. You could read all of your data from the document into a java bean. This bean is then the "data source" for your page and you bind all of your controls to the properties of this bean. You will then use EL to bind your controls to the bean. In the setters for those variables that trigger changes in other fields, change those values. So,
public PageBean(){
//read connection information out of the URL and get the correct information out of the document
//set all variables
firstName=doc.getItemValueString("firstName");
}
private String firstName;
public String getFirstName(){
return firstName;
}
public void setFirstName(String firstName){
this.firstName = firstName;
setLastName("Schmidt");
}
....
Once you register your bean with faces-config.xml, you can then use EL to access the data
#{PageBean.firstName}
#{PageBean.lastName}
Then you can get your document again in save and reset the values, save and release.