I have a component that is entangling a livewire property.
<div x-data="{show: #entangle($attributes->wire('model'))}>
<form>
<input type="checkbox" wire:model.defer="story.show_name" name="show_name" />
</form>
</div>
I have a form within this component. If I update any field within the form it will send an update to the server bug it also modifies the wire:model. An example would be a checkbox. If I uncheck it then the component hides.
Example:
[
{
"type": "syncInput",
"payload": {
"name": "story.show_name",
"value": false
}
},
{
"type": "syncInput",
"payload": {
"name": "show",
"value": false
}
}
]
Any idea why this would suddenly be capturing all input/change events?
The problem was due to me adding all attributes to the component like so {{$attributes}}. This would add wire:model="show" to the element. Since my component wasn't an input field and just a div it would then accept all input events.
I replaced {{$attributes}} with {{$attributes->except('wire:model')}} to fix the problem.
Related
What is the solution to have a pre selected value in the sample below? Let's say that the third option should be selected by default when data is retrieved from JSON and the select box is shown (without user interaction).
<script async src="https://cdn.ampproject.org/v0.js"></script>
<script async custom-element="amp-list" src="https://cdn.ampproject.org/v0/amp-list-0.1.js"></script>
<select>
<amp-list width="auto" height="100" layout="fixed-height" src="https://ampproject-b5f4c.firebaseapp.com/examples/data/amp-list-urls.json">
<template type="amp-mustache">
<option value="{{url}}">{{title}}</option>
</template>
</amp-list>
</select>
The simplest way is adding the selected attribute in the option tags to make it the default.
<option selected value="{{url}}">{{title}}</option>
If you want to explore other options, this SO post mentioned binding. amp-bind adds custom interactivity with data binding and expressions.
There is a solution with "mustache inverted sections", but it does not work in my case due to other factors involved.
I got it thanks to [Cathy Zhu][1], on [AMP Issues support page][2].
I will provide it, in case it would be helpful for someone:
<amp-selector>
<amp-list width="auto" height="200"
layout="fixed-height"
src="//amp-list-selector.glitch.me/api/amp-list-data.json">
<template type="amp-mustache">
{{#selected}}
// content displayed if selected is true
<div class="story-entry" selected>
<amp-img src="{{imageUrl}}" width="100" height="100"></amp-img>
</div>
{{/selected}}
{{^selected}}
// content displayed if selected is false
<div class="story-entry">
<amp-img src="{{imageUrl}}" width="100" height="100"></amp-img>
</div>
{{/selected}}
</template>
</amp-list>
for the sample data:
{
"items": [
{
"title": "Cat",
"imageUrl": "https://lh3.googleusercontent.com/pSECrJ82R7-AqeBCOEPGPM9iG9OEIQ_QXcbubWIOdkY=w400-h300-no-n",
"selected": true
},
{
"title": "Dog",
"imageUrl": "https://lh3.googleusercontent.com/5rcQ32ml8E5ONp9f9-Rf78IofLb9QjS5_0mqsY1zEFc=w400-h300-no-n",
"selected": false
},
{
"title": "Mouse",
"imageUrl": "https://lh3.googleusercontent.com/pSECrJ82R7-AqeBCOEPGPM9iG9OEIQ_QXcbubWIOdkY=w400-h300-no-n",
"selected": false
},
{
"title": "Fish",
"imageUrl": "https://lh3.googleusercontent.com/5rcQ32ml8E5ONp9f9-Rf78IofLb9QjS5_0mqsY1zEFc=w400-h300-no-n",
"selected": false
}
]
}
I have a form which uses validation engine, but the input element that uses jquery datepicker wont display the error message or even validate as a require item. Is there a way to make both work? If not, is there another work around?
<input class="validate[required] datepicker" name="dt1" value="日付を選択" data-validation-engine="validate[required]" class="validate[required]"/>
<script>
//validate engine
$(document).ready(function(){
$("#form1").validationEngine('attach', {promptPosition : "topLeft", scroll: false});
});
//datepicker
$('.datepicker').datepicker({
constrainInput: true,
closeText: "閉じる",
prevText: "<前",
nextText: "次>",
currentText: "今日",
monthNames: [ "1月","2月","3月","4月","5月","6月",
"7月","8月","9月","10月","11月","12月" ],
monthNamesShort: [ "1月","2月","3月","4月","5月","6月",
"7月","8月","9月","10月","11月","12月" ],
dayNames: [ "日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日" ],
dayNamesShort: [ "日","月","火","水","木","金","土" ],
dayNamesMin: [ "日","月","火","水","木","金","土" ],
weekHeader: "週",
dateFormat: "yy/mm/dd",
firstDay: 0,
isRTL: false,
showMonthAfterYear: true,
yearSuffix: "年"
});
</script>
I specified the data attribute to the field as a "date", so it did finally worked.
<input class="validate[required,custom[date] datepicker" name="dt1" autocomplete="off" placeholder="日付を選択" data-validation-engine="validate[required,custom[date]" />
I had a similar issue where my datepicker was getting validated before the datepicker could assign the value, and the input would stay invalid after the datepicker assigned the value.
My issue:
I had to modify the classlist on my input, like the Jquery Validation Engine documentation describes:
class="validate[required] text-input datepicker"
I wrote this as an answer because I'm not entirely sure what happened in your case(Fernando) and your answer confused me.
This issue only happens in my commercial project. It does not happen in my other projects and there is no difference that I can see in set up other then my current project is running the latest update of Kendo (but others in the office are running with the latest version with no issues). Same MVC version, all using server side validation.
So this is the problem
Working scenario 1
Fill in form data
Select something in drop down
Hit Submit
Works
Working scenario 2
Fill in form data
Select something in drop down
Leave a text field unfilled
Hit Submit
Validation error on text field
Enter text
Hit submit
Works
Failing Scenario:
Fill in form data
Don't select something in drop down
Hit Submit
Validation error on drop down
Select something in drop down
Hit submit
Repeats validation error and sets drop down back to unselected
This is currently using server side validation.
When I compare one of our working kendo drop downs to the one that isn't there are two things that stand out.
The working drop down, after validation fail, when I change its selection adds a span with "k-input" that has the selected value in text form. (The broken one does not)
The broken drop down has a value field whereas the working one doesn't
The Kendo Razor from the none working project is this:
#Html.Kendo().DropDownListFor(model => model.EmployeeRecordId).OptionLabel("Please Select").DataTextField("FullName").DataValueField("EmployeeRecordId").DataSource(source => { source.Read(read => { read.Action("Get", "EmployeeRecord", new {area = "ClientArea", id = ViewBag.ClientId}); }).ServerFiltering(true); }).Events(e => { e.Change("employeeChanged"); })
The one that does work is a lot less complicated, it doesn't need to be able to update (there's a button to add a new employee on that page)
#Html.Kendo().DropDownListFor(model => model.CurrencyId).BindTo(ViewBag.Currencies).OptionLabel("Select Currency")
Below is the HTML for each, taken from the page source just after the second validation error and a value was re-selected:
Working
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input class="input-validation-error" data-val="true" data-val-number="The field Currency must be a number." data-val-required="The Currency field is required." id="CurrencyId" name="CurrencyId" type="text" />
<script>
jQuery(function() {
jQuery("#CurrencyId").kendoDropDownList({
"dataSource": [{
"Text": "GBP",
"Value": "1"
},
{
"Text": "EUR",
"Value": "3"
}
],
"dataTextField": "Text",
"dataValueField": "Value",
"optionLabel": "Select Currency"
});
});
</script>
<span class="field-validation-error" data-valmsg-for="CurrencyId" data-valmsg-replace="true">The Currency field is required.</span>
Not Working
<span class="field-validation-valid" data-valmsg-for="EmployeeRecordId" data-valmsg-replace="true"></span>
</div>
<div class="editor-field">
<input data-val="true" data-val-number="The field Employee Name must be a number." data-val-required="The Employee Name field is required." id="EmployeeRecordId" name="EmployeeRecordId" type="text" value="3032" />
<script>
kendo.syncReady(function() {
jQuery("#EmployeeRecordId").kendoDropDownList({
"change": employeeChanged,
"dataSource": {
"transport": {
"read": {
"url": "/ClientArea/EmployeeRecord/GetAllActiveByClientId/1003",
"data": function() {
return kendo.ui.DropDownList.requestData(jQuery("#EmployeeRecordId"));
}
},
"prefix": ""
},
"serverFiltering": true,
"filter": [],
"schema": {
"errors": "Errors"
}
},
"dataTextField": "FullName",
"dataValueField": "EmployeeRecordId",
"optionLabel": "Please Select"
});
});
</script>
<span class="field-validation-valid" data-valmsg-for="EmployeeRecordId" data-valmsg-replace="true"></span>
</div>
Does anyone have any suggestion why this might be happening.
--Update--
Another key point of the puzzle, when it is sent to the server, "EmployeeRecordId" is not included in the form collection of keys!
--UPDATE 2--
when VS is paused on the "Create" action (where i am checking the form collection) if I go back to the page, I can see that the drop down box is mysteriously not there, like its been removed via JS pre submit
After much digging I came across a similar issue.
https://www.telerik.com/forums/kendo-validation-does-not-work-the-second-time
Which although didn't provide me an answer for why the server side is failing. It did lead me to how to get client side validation working for kendo drop downs. Which as it happens before sever side seems to stop the error ever happening.
To enable client side validation on kendo drop downs you need to add this code to the very bottom of your page (so the very bottom of your layout file)
<script type="text/javascript">
$.validator.setDefaults({
ignore: ""
});
</script>
You will need to enable the usual mvc client side validation with the jquery scripts.
I'm hesitant to mark this as best answer as it doesn't answer the question of why this is happening at all but I have posted it to help others with the same problem :)
Not the ideal solution.
But you'll have to add following code for every form submission with validations.
Found the solution from the Kendo UI official Support page. It looks more like a bug.
To get it working, you can add the following code at the bottom of you page. (After the page is loaded)
$(document).ready(function () {
$(".k-widget").removeClass("input-validation-error");
});
I am trying to figure out how I can send some data to a server side API call, let that do the validation and then return back with some validation errors and those validation errors should be displayed along with the component that caused the validation error. Here is how my data model looks like:
export class Order {
orderNo: string;
items: Item[];
constructor() {
this.items = [];
}
}
export class Item {
recNo: string;
}
This is how the HTML form looks like
<div class="form-group">
<label name="orderNo">Order Number</label>
<input type="text" formControlName="orderNo" />
</div>
<div formArrayName="items">
<div *ngFor="let item of orderForm.controls.items.controls; let i=index" [formGroupName]="i">
<div class="form-group">
<label>Item # {{i}}</label>
<input type="text" formControlName="recNo" />
</div>
</div>
</div>
The validation errors that I receive from the server are in this form
[
{
"field": "orderNo",
"message": "order number is required"
},
{
"field": "items[2].recNo",
"message": "record number is required"
}
]
I am not able to figure out how, once I receive the validation errors, can I update the errors objects on the form controls or populate some other structure to show the errors. Its a bit simpler for static fields like orderNo but a lot more complex for nested array fields like items[0].recNo to update the errors programmatically. Any ideas would be appreciated.
This is what I ended up doing:
<div class="form-group" [class.has-danger]="!ctrl.valid && ctrl.enabled">
<input type="text" class="form-control" [formControl]="ctrl"
value="{{valueFormatter(ctrl)}}"
[class.form-control-danger]="!ctrl.valid && ctrl.enabled"
[ngbTooltip]="ctrl.errors ? ctrl.errors.e : falsy" />
</div>
Basically, setting up error classes on the form control based on the ctrl valid/error state and here is an example of specifying an error on a control:
this.ctrl.setErrors({ "e": this.label + " is required"});
I just used a predefined error key e to display any errors. This way, once I receive the error list from the backend, I can set the e error on a control and have that show up in a tooltip on the control.
Follow up to related question
here
For whatever reason I'm unable to access my model in my xml view when it's set through sap.ui.getCore().setModel(). If I set it on the this.getView() I have no problems at all.
My view XML
<mvc:View controllerName="ca.toronto.rcsmls.webapp.controller.Login"
xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:l="sap.ui.layout">
<Page title="{i18n>loginPageTitle}">
<content>
<Panel id="loginPanel" busyIndicatorDelay="0"
headerText="{i18n>loginPanelTitle}" class="sapUiResponsiveMargin loginPanel"
width="auto">
<l:VerticalLayout width="100%">
<Input type="Text" placeholder="{i18n>loginUidHolder}" value="{/mlsUser/uid}" />
<Input type="Password" placeholder="{app>/Password}"
value="{/mlsUser/password}" />
<Button text="{i18n>loginButtonText}" press="doLogin"
class="sapUiSmallMarginEnd customBold" width="100%" />
</l:VerticalLayout>
</Panel>
</content>
</Page></mvc:View>
My controller JS contains this for setModel()
onInit : function() {
sap.ui.getCore().setModel(new sap.ui.model.json.JSONModel("webapp/controller/app.json"), "app");
}
Again, if I set the model to this.getView().setModel() instead of getCore() XML and controller work fine together. I also added data-sap-ui-xx-bindingSyntax="complex" to my index.html but that didn't seem to make a difference. Any help would be appreciated.
Edited to include more information
My Component.js
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/model/json/JSONModel",
], function (UIComponent, JSONModel) {
"use strict";
return UIComponent.extend("ca.toronto.rcsmls.webapp.Component", {
metadata : {
manifest: "json"
},
init : function () {
// call the init function of the parent
UIComponent.prototype.init.apply(this, arguments);
// set data model
var oData = {
mlsUser : {
uid : "",
password : "",
}
};
var oModel = new JSONModel(oData);
this.setModel(oModel);
// create the views based on the url/hash
this.getRouter().initialize();
}
});
});
My manifest.json
{
"_version": "1.1.0",
"sap.app":
{
"_version": "1.1.0",
"id": "ca.toronto.rcsmls",
"type": "application",
"i18n": "i18n/i18n.properties",
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"applicationVersion":
{
"version": "1.0.0"
},
"ach": "CA-UI5-DOC"
},
"sap.ui":
{
"_version": "1.1.0",
"technology": "UI5",
"deviceTypes":
{
"desktop": true,
"tablet": true,
"phone": true
},
"supportedThemes":
[
"sap_bluecrystal"
]
},
"sap.ui5":
{
"_version": "1.1.0",
"rootView": "ca.toronto.rcsmls.webapp.view.App",
"dependencies":
{
"minUI5Version": "1.30",
"libs":
{
"sap.m":
{
}
}
},
"config":
{
"authenticationService": "http://172.21.226.138:9080/RcsMlsSvc/jaxrs/user/authenticate/",
"assignedWorkService": "http://172.21.226.138:9080/RcsMlsSvc/jaxrs/mls/searchAssignedWork"
},
"models":
{
"i18n":
{
"type": "sap.ui.model.resource.ResourceModel",
"settings":
{
"bundleName": "ca.toronto.rcsmls.webapp.i18n.i18n"
}
}
},
"routing":
{
"config":
{
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"viewPath": "ca.toronto.rcsmls.webapp.view",
"controlId": "root",
"controlAggregation": "pages"
},
"routes":
[
{
"pattern": "",
"name": "login",
"target": "login"
},
{
"pattern": "work",
"name": "work",
"target": "work"
}
],
"targets": {
"login": {
"viewName": "Login"
},
"work": {
"viewName": "Work"
}
}
},
"resources":
{
"css":
[
{
"uri": "css/style.css"
}
]
}
}
}
Model app.json
{
"BaseURL": "https://smp-pNNNNNNtrial.hanatrial.ondemand.com",
"ES1Root": "https://sapes1.sapdevcenter.com",
"AppName": "qmacro.myfirst",
"Username": "yourusername",
"Password": "yourpassword"
}
I found an example where core binding works here. It is a much simpler application. I'm still trying to figure out the what the differences are between this project and mine
I was made aware of this question by a similar question on Github
By default, UI5 components don't inherit models and binding contexts from their environment (the ComponentContainer where they are placed). This was done for the sake of isolation.
You can change that default behavior by setting the property propagateModel:true for the container:
new sap.ui.core.ComponentContainer({
name: "ca.toronto.rcsmls.webapp.Component",
propagateModel: true
}).placeAt("content");
Documentation for propagateModel can be found in the API reference as well as in the UI5 developer guide (the corresponding paragraph in the latter page was only added recently, it was not available when you raised your question).
But when the model is created in the scope of the component and shall be used inside the component, then there should be no need to add it to the Core. Just assign it to the component to share it between views inside that component:
Assign it either from some method in a view controller
onInit : function() {
this.getOwnerComponent().setModel(
new sap.ui.model.json.JSONModel(
"webapp/controller/app.json"), "app");
}
or during init of the Component.js itself
init : function() {
// create and set model to make it available to all views
this.setModel(
new sap.ui.model.json.JSONModel(
"webapp/controller/app.json"), "app");
// never forget to call init of the base class
UIComponent.init.apply(this, arguments);
}
The most modern approach is to configure the model in the manifest.json and to let the framework instantiate an assign it. See Walkthrough Tutorial - Step 10: Descriptor for Applications for an example.
Using any of these approaches, there should not even be a need to read and set the model in another view, as long as that view is a descendant of the component (part of the control tree returned by createContent).
I experienced the same behavior.
If I create a simple one-file application, without any complex UI elements, the core-based binding works like a charm.
If I create a complex container, like an App with a shell, this kind of binding will not work anymore. It seems that these containers hide the global model from the view.
As a workaround I used the following code snippet:
this.getView().setModel(sap.ui.getCore().getModel(modelName), "modelName");
Or even you can bind the model directly to the control where you want to use.
None of them are the best solution if you have to use the global model in several view/control, but that's working for me.
I the view above only the placeholder of the password field is using your app model. If this placeholder is not filled correctly then I guess the json file could not be loaded or the content is not matching the binding path you use in your view for the placeholder property of the password field. To find out more please post also the content of your json file. It should look somehow like this:
{ "Password" : "Enter your password", ... }
So according to the binding in the view there must be "Password" property at the root level of the data object.
Below you find a running example that should help you. As you can see you it work like a charm, so you can easily put a named model onto the Core and reference it in your view.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SAPUI5 single file template | nabisoft</title>
<script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-libs="sap.m"
data-sap-ui-bindingSyntax="complex"
data-sap-ui-compatVersion="edge"
data-sap-ui-preload="async"></script>
<!-- use "sync" or change the code below if you have issues -->
<!-- XMLView -->
<script id="myXmlView" type="ui5/xmlview">
<mvc:View
controllerName="MyController"
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:l="sap.ui.layout"
xmlns:mvc="sap.ui.core.mvc">
<App>
<Page title="My Page Title">
<content>
<Panel id="loginPanel" busyIndicatorDelay="0"
headerText="My Login Panel Title" class="sapUiResponsiveMargin loginPanel"
width="auto">
<l:VerticalLayout width="100%">
<Input type="Text" placeholder="{Enter User ID}" value="{/mlsUser/uid}" />
<Input type="Password" placeholder="{app>/Password}" value="{/mlsUser/password}" />
<Button text="{Login}" press="doLogin" class="sapUiSmallMarginEnd customBold" width="100%" />
</l:VerticalLayout>
</Panel>
</content>
</Page>
</App>
</mvc:View>
</script>
<script>
sap.ui.getCore().attachInit(function () {
"use strict";
//### Controller ###
sap.ui.controller("MyController", {
onInit : function () {
var oData, oModel;
// 1. app model is only used for the placeholder field in the view
oData = {
Password : "Enter your password"
};
oModel = new sap.ui.model.json.JSONModel(oData);
sap.ui.getCore().setModel(oModel, "app");
// 2. default model is used in the view as well
oData = {
mlsUser : {},
Login : "Login now"
};
oModel = new sap.ui.model.json.JSONModel(oData);
sap.ui.getCore().setModel(oModel);
// 3. we need this because of the relative binding
// of the text property of the login button
this.getView().bindElement("/");
}
});
//### THE APP: place the XMLView somewhere into DOM ###
sap.ui.xmlview({
viewContent : jQuery("#myXmlView").html()
}).placeAt("content");
});
</script>
</head>
<body class="sapUiBody">
<div id="content"></div>
</body>
</html>
In this thread some people have mentioned that "sap.ui.getCore() works with small stuff, but for whatever reason not in more complex applications".
#Marc has also posted the right link to the API docs, where you can find the following:
A ManagedObject inherits models from the Core only when it is a
descendant of an UIArea
Of course, you have to know what this means in order write code that does what you expect. Here is a little example that tells you what could happen in case you have a small "misunderstanding" (see below).
The code below creates two instances of sap.m.Text and binds their text properties to a JSONModel, which is available as a named model "core" directly on the Core (retrieved with sap.ui.getCore()). There are 2 buttons, one for each sap.m.Text instance. In the corresponding press handlers of the buttons I just display the text property of the corresponding sap.m.Text instance. As you can see, both sap.m.Text instances are bound to the same property in the JSONModel. However, only the second sap.m.Text is added to the DOM.
Now the interesting part, which might be related to the confusion of this thread:
Only the text property of the second sap.m.Text control contains the expected text "Hello World" from the JSONModel. The text property of the first sap.m.Text control does not have the value "Hello World" from the model! This is expected behavior of SAPUI5 and it is documented! So I guess in case you face similar issues in your own "complex" app, then it might be quite probable that you have a hard to find bug related to this "expected" behavior.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SAPUI5 single file template | nabisoft</title>
<script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-libs="sap.m"
data-sap-ui-bindingSyntax="complex"
data-sap-ui-compatVersion="edge"
data-sap-ui-preload="async"></script>
<!-- use "sync" or change the code below if you have issues -->
<script>
sap.ui.getCore().attachInit(function () {
sap.ui.define([
"sap/m/Text",
"sap/m/Button",
"sap/ui/model/json/JSONModel"
], function (Text, Button, JSONModel) {
"use strict";
var oModel = new JSONModel({
hello : "Hello World"
});
sap.ui.getCore().setModel(oModel, "core");
// not in DOM
var oText1 = new Text({
text : "{core>/hello}"
});
//oText1.placeAt("text1"); // uncomment this to enable the binding
// add to DOM
var oText2 = new Text({
text : "{core>/hello}"
});
oText2.placeAt("text2");
// action buttons to display text property of text controls
new Button({
text : "show oText1",
press : function(){
alert("oText1.getText() = " + oText1.getText());
}
}).placeAt("btn1");
new Button({
text : "show oText2",
press : function(){
alert("oText2.getText() = " + oText2.getText());
}
}).placeAt("btn2");
});
});
</script>
</head>
<body class="sapUiBody">
<div id="text1"></div>
<div id="text2"></div>
<span id="btn1"></span>
<span id="btn2"></span>
</body>
</html>
sap.ui.getCore() and this.getView() return not the same object i think it's obvious why this doesn't work.
You are trying to get a object(model) from another object (the core) although the desired model is bound to another object(the view)
it's like i have two colored boxes (one red, one blue) and im trying to get the color red from the blue colored box
Here is an similar issue but the cause in this one depends on the id handling of the views in the UI5 framework:
https://scn.sap.com/thread/3551589
You can see the core and the view does not return the same object.
Also checkout the data binding section on the openui5 website: https://openui5beta.hana.ondemand.com/#docs/guide/e5310932a71f42daa41f3a6143efca9c.html
Create Model in components.js:
var oModel= new sap.ui.model.json.JSONModel;
oModel.loadData("webapp/controller/app.json");
this.setModel(oModel, "app");
Get Model:
This will create a reference to the model which has been created in the Components.js
var oModel= this.getView().getModel("app");
Refer to the model with "{modelName(app)>desiredProperty}"
<Input type="Password" placeholder="{app>Password}"
value="{app>mlsUser/password}" />
could you post your json content?
Hope this was helpful