I'm trying to achieve a two way binding between an input field and a field in my JavaScript ViewModel. The binding has been wired up declarativly. Unfortunately the changes I do in the UI aren't reflected in my ViewModel.
My Code looks like that (written out of my head as I don't have the code here)
View:
<form data-win-bind="onsubmit: onCalculate">
<div class="field">
Product Name:
<input type ="number" data-win-bind="text:value1"/>
</div>
<div class="field">
Product Price:
<input type ="number" data-win-bind="text:value2"/>
</div>
<div class="field">
Result
<br />
<span data-win-bind="innerText: result" />
</div>
</form>
JavaScript
var model= WinJS.Class.define(
function() {
this.onCalculate = calculate.bind(this);
this.value1 = 0;
this.value2 = 0;
this.result = 0;
},{
value1: 0,
value2: 0,
result: 0
calculate: function() {
this.result = this.value1 + this.value2;
return false;
}
}, {});
// Make the model Observable
var viewModel = WinJS.Binding.as(new model());
WinJS.Binding.processAll(null, viewModel);
When I apply the binding, the ui shows my initial values. The form submition is correctly wired with the calculate function. The values of value1 and value2 however aren't updated with the users input.
What I'm trying to achive is to keep my JavaScript unaware of the underlying view. So I don't want to wire up change events for the html input fields in JavaScript.
Is there any way to achive this with pure WinJS? All samples I've found so far only do a one-way binding and use event listeners to update the ViewModel with changes from the UI.
WinJS only supports one-way binding for Win8. It is necessary to wire up listeners for change events in the UI elements, hence the nature of the samples you've seen. In other words, the implementation of WinJS.Binding's declarative processing doesn't define nor handle any kind of two-way syntax.
It would be possible, however, to extend WinJS yourself to provide such support. Since WinJS.Binding is just a namespace, you can add your own methods to it using WinJS.Namespace.define (repeated calls to this are additive). You could add a function like processAll which would also look for another data-* attribute of your own that specified the UI element and the applicable change events/properties. In processing of that you would wire up a generic event handler to do the binding. Since you have the WinJS sources (look under "References" in Visual Studio), you can see how WinJS.Binding.processAll is implemented as a model.
Then, of course, you'd have a great piece of code to share :)
This article provides a nice solution:
http://www.expressionblend.com/articles/2012/11/04/two-way-binding-in-winjs/
Related
I have a page with many required fields. So when I click submit button required validation is firing for the first field then the second then the third and so on...
What I need to do here is , When I click on submit I have to show all errors on a page in one shot.
My requirement is to achieve this only by validating client side.
I am using an .Net core MVC application.
Below is the screenshot of my page
Can I achieve this.. Please help me..
Thanks !!
I can give you an idea to do your job using jquery custom validation.Please refer my solution.
Add custom style class to your required fields.
Example :
<input type="text" class="req-cls" >
Write Jquery function to Check Validation
$(document).ready(function () {
$('#btn1').click(function (e) {
var isValid = true;
$('.req-cls').each(function () {
if ($.trim($(this).val()) == '') {
isValid = false;
$(this).css({
"border": "1px solid red",
"background": "#FFCECE"
});
}
else {
$(this).css({
"border": "",
"background": ""
});
}
});
if (isValid == false)
e.preventDefault();
});
});
See Example here : https://jsfiddle.net/Shalitha/q2n8L9wg/24/
Just add this line in your .cshtml
<div class="validation-summary-valid" data-valmsg-summary="true">
<ul>
<li style="display: none;"></li>
</ul>
</div>
Since you need client side we are talking about JS. But with razor you can validate a few results using the model annotations. For example let's say you have this object.
public class UserCreationVO
{
[Required]
[StringLength(255)]
public string Username { get; set; }
}
Now what you need to do in your frontend (meaning your .cshtml file) is to tell asp.net to use this properties to validate. So for example:
#model UserCreationVO
<form method="post">
<input asp-for="UserName" />
<span asp-validation-for="UserName"></span>
</form>
As you can see above using asp-for is a great way to create validations using your models. Be careful you must pass as a model the object you want to validate. The asp-for tag shows a model property. So you can't pass it in a Viewbag or something. This produces some automatic html and js for you and handles it.
Furthermore you should always validate the result nevertheless in the controller. Because client side validation is for performance reasons and user experience and doesn't offer any kind of security:
public IActionResult CreateUser(UserCreationVO user)
{
if(!ModelState.IsValid)
return your_error;
}
Last but not least: You must include the JQuery unobtrusive validation library. Furthermore if you have some extra requirements like checking if a username exists (Which can't be done without contacting the server) then you can use the [Remote] attribute.
More info and reading about front-end validation with razor: here
How to use a remote attribute: Using remote validation with ASP.NET Core
EDIT:
So generally I advise to use models and create them. As you say policy is required in one form but not in another. What you should do to have a maintanable code where you simply change the attribute of your model and the validation happens you need to create a different VO. For example:
public class CreatePolicyVO
{
[Required]
public string PolicyNumber {get; set;}
}
And another object for example updating:
public class UpdatePolicyVO
{
public string PolicyNumber {get; set;}
}
Because you also need to validate them in the controller. So passing a different object allows you to use ModelState.IsValid and other MVC and razor features. Generally if a field is required in one case and not in another then you need a different model.
First, we need to add the JQuery,jquery.validate & jquery.validate.unobtrusive in our views.
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.16.0/jquery.validate.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js"></script>
Then in View add required data-* attributes like:
<label for="Name">Name</label>
<input type="text" data-val="true" data-val-length="Length must be between 10 to 25" data-val-length-max="25" data-val-length-min="10" data-val-required="Please enter the name" id="Name" name="Name" value="" />
<span class="field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true"></span>
<br />
You could see that it has added the several attributes starting with data-*.
The data-* attributes are part of the HTML5, which allow us the add extra information (metadata) to the HTML element.
The Javascript unobtrusive library reads the data-val attributes and performs the client side validation in the browser when the user submits the form. These Validations are done before the form is sent over an HTTP. If there is a validation error, then the request will not be sent.
I'm using aurelia-validation#1.0.0-beta.1.0.1.
My scenario is:
I've got a form which has a validation controller and validation rules
There is a containerless custom element in the form which wraps a password input, and exposes the current password as a bindable property
The validate binding behavior is used on the form's binding to this custom element property
The custom element also raises the blur event, so that the validation binding is triggered when the wrapped password input loses focus
The validation lifecycle is working as expected.
The problem I'm running into is with the custom renderer I'm using, which currently assumes the element it receives is the actual DOM input element so that a class can be applied to the input, and a sibling error element can be injected next to it, but here it's receiving the custom element that wraps the input, which can't be handled in the same manner because it's just a comment node in the DOM.
Is there a strategy or API in aurelia-validation that could solve this sort of problem? I'm stumped, and can't find much out there on working with custom elements within validation.
EDIT:
Here is the custom element template:
<template>
<div class="input-group -password">
<div class="input-toggle-wrapper">
<label for="password" class="-hidden" t="fields_Password"></label>
<input
id="password"
type="${isPasswordVisible ? 'text' : 'password'}"
value.bind="password"
t="[placeholder]fields_Password"
maxlength="20"
focus.trigger="onInputFocus()"
blur.trigger="onInputBlur()" />
<div
class="toggle ${isPasswordVisible ? '-show' : ''}"
click.delegate="onToggleClick($event)"
mousedown.delegate="onToggleMouseDown($event)"></div>
</div>
</div>
</template>
I made it containerless because I don't want <password-box> emitted into the DOM as an outer element, as that breaks the current CSS rules for layout (and I don't want to change the CSS).
However if the custom element is containerless then I don't know how to access the first div inside the template using DOM navigation from the comment node that represents the custom element in the DOM.
Unfortunately, Aurelia team has identified this issue and (at least as of now) said they won't fix it. https://github.com/aurelia/templating/issues/140
There is a hacky workaround for the issue as follows:
if(element.nodeType === 8) {
element = this.getPreviusElementSibling(element)
}
If you add that within your render method for your renderer, it should work. Again, hacky, but it gets the job done in lieu of an official fix from the AU team.
I am trying to implement semantic zoom control in my application. Here is a fragment of one of my pages:
<div id="semanticZoomDiv" data-win-control="WinJS.UI.SemanticZoom">
<div id="zoomedInListView"
class="win-selectionstylefilled"
data-win-control="WinJS.UI.ListView"
data-win-bind="winControl.itemDataSource: groupedList.dataSource; winControl.groupDataSource: groupedList.groups.dataSource;"
data-win-options="{
itemTemplate: select('#mediumListIconTextTemplate'),
groupHeaderTemplate: select('#headerTemplate'),
selectionMode: 'none',
tapBehavior: 'none',
swipeBehavior: 'none'
}">
</div>
<div id="zoomedOutListView"
data-win-control="WinJS.UI.ListView"
data-win-bind="winControl.itemDataSource: groupedList.groups.dataSource;"
data-win-options="{
itemTemplate: select('#semanticZoomTemplate'),
selectionMode: 'none',
tapBehavior: 'invoke',
swipeBehavior: 'none'
}">
</div>
</div>
The problem is, that semanticZoomDiv is empty.
However, if I remove attribute
data-win-control="WinJS.UI.SemanticZoom"
from semanticZoomDiv two ListViews render and are correctly filled with data. It seems like WinJS has problems with binding data to nested controls? (ListView controls are inside SemanticZoom control - after removal of outer SemanticZoom control data binds correctly).
I managed to make semantic zoom work using binding to global namespace via data-win-options, but I want to provide data for my page through view model, hence my trials to use data-win-bind.
I believe this is an object scoping issue, but am not on Win8 at the moment to verify. If I'm right, you can expose groupedList from your JavaScript code similarly to this.
WinJS.Namespace.define("YourApp", {
groupedList: groupedList,
});
and then change your declarative binding to include the YourApp namespace. That way your data is not in the global namespace.
Alternatively, you can do the data binding in the .js file
zoomedInListView.winControl.itemDataSource = groupedList.dataSource;
zoomedInListView.winControl.groupDataSource = groupedList.groups.dataSource;
zoomedOutListView.winControl.itemDataSource = groupedList.groups.dataSource;
In a Windows store app using Javascript, I have a listview defined in the html.
<div id ="menuListView" data-win-control="WinJS.UI.ListView" data-win-options="{
itemDataSource: viewModel.items.dataSource,
itemTemplate: menuItemTemplate,
layout: {type: WinJS.UI.GridLayout}}"></div>
I could define a click event handler in Javascript something like this:
menuListView.addEventListener("selectionchanged", clickEventHandler, false);
But since, I'm trying to use the MVVM pattern, I would like to put this piece of code in the html view and let a viewmodel handle the click event. Would it be possible?
For full NVVM functionality in your WinJS app I would recommend using a framework like http://knockoutjs.com
You can probably try something like this if you are keep to declare an event handler in HTML view :
"<button id="button1" onselectionchange="clickEventHandler(event)">An HTML button</button>"
Hope this helps :)
and thanks in advance for your help!
Here's my situation: I have a set of divs whose IDs have an incrementing number applied to the end of the name using PHP. Each of these divs are added dynamically with PHP (They are a series of FAQ questions with a hidden div container with the answers, that slide down when the question is clicked.) [Live Example][1]
There is no limit to the number of questions that appear on the page, because this is being used for a Wordpress theme and my client wants to add new questions as they go along.
Here's an example of the structure for each FAQ question using the PHP:
<?php var $faqnum = 0; $faqnum++; ?>
<div id="faqwrap<?php $faqnum; ?>">
<h4>What data is shared?</h4>
<div id="faqbox<?php $faqnum; ?>" class="slidebox">
<p>Data sharing is defined by the type of service:</p>
<ul class="list">
<li>Third-party access to data (Enhanced Services only is strictly controlled and determined by the SDA)</li>
<li>All members must participate in points of contact and conjunction assessment but can choose whether to participate in other services</li>
<li>Participation in a service requires the member to provide associated data<br />
</ul>
</div>
</div>
Now this is what I have currently in jQuery, and it works, but only if I add a new one every time my client wants to add a new question.
$(document).ready(function() {
$('.slidebox*').hide();
// toggles the slidebox on clicking the noted link
$("#faqwrap1 a:not(div.slidebox a)").click(function() {
$("#faqbox1.slidebox").slideToggle('normal');
$('div.slidebox:not(#faqbox1)').slideUp('normal');
return false;
});
});
I thought of maybe doing something with a declared variable, like this:
for (var x = 0; x < 100; x++;) {
$('#[id^=faqwrap]'+ x 'a:not(div.slidebox a)')...
}
I hope this is clear enough for you! Again, I thank you in advance. :)
The best way to handle this is to not use the IDs, but use classes for the outer element. So your PHP would be altered like this:
<?php var $faqnum = 0; $faqnum++; ?>
<div id="faqwrap<?php $faqnum; ?>" class="question">
<h4>What data is shared?</h4>
<div id="faqbox<?php $faqnum; ?>" class="slidebox">
<p>Data sharing is defined by the type of service:</p>
<ul class="list">
<li>Third-party access to data (Enhanced Services only is strictly controlled and determined by the SDA)</li>
<li>All members must participate in points of contact and conjunction assessment but can choose whether to participate in other services</li>
<li>Participation in a service requires the member to provide associated data<br />
</ul>
</div>
</div>
Your JQuery would be rewritten with the selector for the class "question".
$(document).ready(function() {
$('.slidebox*').hide();
// toggles the slidebox on clicking the noted link
$(".question a:not(div.slidebox a)").click(function() {
/* close everything first */
$('div.slidebox').slideUp('normal');
/* selects and opens the the slidebox inside the div */
$(".slidebox", this).slideToggle('normal');
return false;
});
});
This will get you the effect you are looking for. The key differences in the JQuery is the way you get the slidebox inside the question that got clicked. I'm using the scoped selection $(".slidebox", this) to get just the slidebox inside the clicked ".question" element.
The subtle visual difference is that the slideUp() happens before the slideToggle(). This will essentially close any open queries before it opens the desired one. If you keep your animations fast, this will be more than fine. The advantage of this approach is that you don't have to worry about the count of questions on a page, and the selectors are most likely more optimized than the for loop.
Edit
I adjusted the PHP code to use a class for "slidetoggle" instead of an id. It's technically an HTML error to have multiple IDs that are the same. It can throw off some assistive technologies for people with dissabilities. I'm assuming that section of code was repeated several times on the page.
Without changing your current markup, this would work:
// toggles the slidebox on clicking the noted link
$("div[id=^faqwrap]").each(function () {
var $faqwrap= $(this);
$faqwrap.find("h4 > a").click(function () {
var $currentSlidebox = $faqwrap.children(".slidebox");
$currentSlidebox.slideToggle('normal');
$('div.slidebox').not($currentSlidebox).slideUp('normal');
return false;
});
});
Maybe you can find a few suggestions in the above code that help you.
Like #Berin, I'd also recommend giving a separate CSS class to the outer DIV and using that as a selector, instead of $("div[id=^faqwrap]").