I'm pretty new to angular (as you might guess). I'm trying to understand the best way to change content depending on what gets input in fields.
For example, I have
<div ng-app = "">
<p>Enter variable 1: <input type = "number" ng-model = "var1"></p>
<p>Enter variable 2: <input type ="number" ng-model ="var2"></p>
And I'm trying to display the product of the two variables, once both have been entered:
<p>Your total value is
{{ (var1 * var2).isFinite? (var1 * var2) : 'Please enter a number' }} </p>
However, this doesn't work - it always displays 'Please enter a number'.
What is the best way to do this?
I would keep that logic out of the HTML and in your controller if possible and add another function to set a new value in your view (either num1 * num2 or "Please enter a number").
$scope.isFinite = function(num1, num2) {
//Your logic for this function
}
$scope.checkFinite() {
$scope.answer = null;
if($scope.isFinite($scope.num1, $scope.num2)) {
$scope.answer = $scope.num1 * $scope.num2;
};
else {
$scope.answer = "Please enter a number"
};
}
Then update your html as so:
<p>Your total value is {{answer}} </p>
You will need to run $scope.checkFinite() when your input changes, there are a couple of ways you can do that:
<div ng-app = "">
<p>Enter variable 1: <input type = "number" ng-change="checkFinite()" ng-model = "var1"></p>
<p>Enter variable 2: <input type ="number" ng-change="checkFinite()" ng-model ="var2"></p>
Basically, if the values change in the inputs, it will run $scope.checkFinite(), its a nifty little directive in Angular.
Another way you can do this, where you run the function ($scope.checkFinite()) when either inputs change is through a couple of watches in your controller:
$scope.$watch('num1', function() {
$scope.checkFinite();
});
$scope.$watch('num2', function() {
$scope.checkFinite();
});
Let me know if you have any other questions!
PS Don't go crazy with $scope.$watch, it's easy to fall into that trap.
The simplest method would be to use ng-if:
<p>Enter variable 1: <input type = "number" ng-model = "var1"></p>
<p>Enter variable 2: <input type ="number" ng-model ="var2"></p>
<p>
<div ng-if="var1 && var2">Your total value is {{(var1 * var2)}}</div>
<div ng-if="!var1 || !var2">'Please enter a number'</div>
</p>
Related
I tried using the Asp.Net Core TagHelper but it doesn't seem to work. However, when using HtmlHelpers it works as expected. My issue is that it always display the error message although the ModelState is valid. Am I doing something wrong or can someone reproduce this error?
<label class="control-label" asp-for="Firstname">Firstname</label>
<input type="text" class="form-control" asp-for="Firstname">
<span class="form-control-feedback" asp-validation-for="Firstname"> This field has an error. </span>
The property Firstname has a Required attribute in the ViewModel.
It works like this:
<label class="control-label" asp-for="Firstname">Firstname</label>
<input type="text" class="form-control" asp-for="Firstname">
#Html.ValidationMessageFor(x => x.Firstname)
Edit:
It seems to work if I don't add the custom error message to the Html element but instead to the ViewModel DataAnnotation, like this:
<label class="control-label" asp-for="Firstname">Firstname</label>
<input type="text" class="form-control" asp-for="Firstname">
<span class="form-control-feedback" asp-validation-for="Firstname"></span>
Model:
[Required(ErrorMessage = "This field has an error.")]
public string Firstname { get; set; }
TL;DR:
Consider putting text inside the tag helpers in scenarios when you really want something
different from the generated value.
Full answer
You practically find the solution on your own, but I think I can still throw in my two cents here.
Most tag helpers work in a manner of generating content on a condition when its content is empty or contain only whitespace characters. For example, the ValidationMessageTagHelper checks it in this way:
var tagHelperContent = await output.GetChildContentAsync();
// We check for whitespace to detect scenarios such as:
// <span validation-for="Name">
// </span>
if (!tagHelperContent.IsEmptyOrWhiteSpace)
{
message = tagHelperContent.GetContent();
}
It gets tag content and then fills up message variable if the content is null, empty or whitespace. The message variable is then used to generate the validation message:
var tagBuilder = Generator.GenerateValidationMessage(
ViewContext,
For.ModelExplorer,
For.Name,
message: message,
tag: null,
htmlAttributes: htmlAttributes);
If the message is null or empty then the generator will provide the model error (see line 858 of DefaultHtmlGenerator);
if (!string.IsNullOrEmpty(message))
{
tagBuilder.InnerHtml.SetContent(message);
}
else if (modelError != null)
{
modelExplorer = modelExplorer ?? ExpressionMetadataProvider.FromStringExpression(
expression,
viewContext.ViewData,
_metadataProvider);
tagBuilder.InnerHtml.SetContent(
ValidationHelpers.GetModelErrorMessageOrDefault(modelError, entry, modelExplorer));
}
The GetModelErrorMessageOrDefault() of ValidationHelpers:
public static string GetModelErrorMessageOrDefault(
ModelError modelError,
ModelStateEntry containingEntry,
ModelExplorer modelExplorer)
{
Debug.Assert(modelError != null);
Debug.Assert(containingEntry != null);
Debug.Assert(modelExplorer != null);
if (!string.IsNullOrEmpty(modelError.ErrorMessage))
{
return modelError.ErrorMessage;
}
// Default in the ValidationMessage case is a fallback error message.
var attemptedValue = containingEntry.AttemptedValue ?? "null";
return modelExplorer.Metadata.ModelBindingMessageProvider.ValueIsInvalidAccessor(attemptedValue);
}
So yes, if you put any text inside the <span> validation tag, the tag helper will choose your text over validation error from model state. Similar behaviour occurs if you put text inside the <label> tag as you did:
<label class="control-label" asp-for="Firstname">Firstname</label>
The tag helper will not overwrite the Firstname value you put inside the tag. It may not seem as bad behaviour, but if you would like to use display name for the Firstname property:
[Display(Name = "Fancy first name")]
public string Firstname { get; set; }
you would not see it work! Because the tag helper would again choose the text you put in-between <label> tags over the display name for Firstname.
What you should do is leave it as simple as i can be:
<label class="control-label" asp-for="Firstname"></label>
Consider putting text inside the tag helpers in scenarios when you really want something
different from the generated value.
At the begging I said that most tag helpers work that way. Most of them do, but not all of them. For example SelectTagHelper allows you to put any custom text inside the tag and if you provide a select list, it will generate the options by appending them to the existing content. It is extremely handy for adding custom <option> tags. For example I can easily add a selected and disabled option, so the dropdown does not have initial value, therefore the user is forced to manually select an option. These lines of code:
<select asp-for="LevelId" asp-items="#Model.Levels" class="custom-select">
<option selected disabled>Select option</option>
</select>
will result in:
<select class="custom-select" data-val="true" data-val-required="'Level Id' must not be empty." id="LevelId" name="LevelId">
<option selected disabled>Select parking level</option>
<option value="9">-2</option>
<option value="8">-1</option>
<option value="7">0</option>
</select>
I have a Spring MVC application using Thymeleaf for templating. I am using enums to generate checkboxes dynamically. So if my enum file has 3 values it will generate 3 checkboxes:
My enum file:
public enum Foods {
PIZZA("Pizza"),
PASTA("Pasta"),
MAC_CHEESE("Mac and Cheese"),
ICE_CREAM("Ice Cream"),
BURGER("Burger"),
private String type;
Foods(String type) {
this.type = type;
}
public String getType() {
return this.type;
}
}
This is my checkbox generation:
<label for="decision">What is your favorite food?</label>
<div id="decision" class="row" style="margin-top:1%;">
<div class="col-md-4" th:each="option : ${T(in.app.model.enums.Foods).values()}">
<div class="checkbox checkbox-custom checkbox-circle">
<input name="decision" type="checkbox" th:id="${option.toString()}" th:value="${option}" />
<label th:for="${option.toString()}" th:text="${option.type}"></label>
</div>
</div>
</div>
This code will generate 5 checkboxes for each of the food type. All works till here. The issue I am facing is how to set the checked attribute when reading a saved record.
I am getting back an object via the model view controller. The object has a food property with its value as the array of the chosen food types.
user = {
.
.
food : ["PIZZA", "BURGER", "PASTA"],
.
.
}
Now I want to loop through this array and if the value match then set the checkbox.
I am trying to do something like this:
<label for="decision">What is your favorite food?</label>
<div id="decision" class="row" style="margin-top:1%;">
<div class="col-md-4" th:each="option : ${T(in.app.model.enums.Foods).values()}">
<div class="checkbox checkbox-custom checkbox-circle">
<input
name="decision"
type="checkbox"
th:id="${option.toString()}"
th:value="${option}"
th:each="food : ${user.food}"
th:attr="checked = ${food} == ${option} ? 'checked'"
/>
<label th:for="${option.toString()}" th:text="${option.type}"></label>
</div>
</div>
</div>
I know its wrong (since its not working) but I am unable to figure out how to loop over two arrays to show the checkboxes and to check them.
You might want to try using th:checked instead of th:attr if you can, so:
th:checked="${food == option.type}"
This post might also be helpful when looking into that. If you can't use th:checked, switching to the below statement should also work.
th:attr="checked=${food == option.type} ? 'checked'"
It also seems like you may run into some issues with checking this data due to case sensitivity while comparing, in which case this post might be helpful.
The safe option is to go with
th:attr
and do compare like #Artichoke
th:attr="checked=${food == option.type} ? 'checked'"
The problem with "th:checked" is, its simply do not go well when you need to post the data unless you change the value. You see its value as null if you do not switch it.
I have a form in which I have 2 fields, ssn and phone. I would like the user to enter anyone of the field. I'm using semantic validation, here is my code, can you please let me know how to validate the form using Semantic?
<form class="ui error form basic segment" role="form" method="POST" action="{{ url('/username/email') }}">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="hidden" name="_method" value="patch">
<div class="ui info message">
Please enter either SSN or phone to email you the username.
</div>
<div class="field">
<label for="ssn">SSN</label>
<div class="ui icon input">
<input type="text" class="form-control" name="ssn" value="{{ old('ssn') }}">
</div>
</div>
<div class="field">
<label for="phone">Phone</label>
<div class="ui icon input">
<input type="text" class="form-control" name="phone" value="{{ old('phone') }}">
</div>
</div>
<input type="submit" value="Email Username" class="ui primary button">
</form>
<script type="text/javascript">
$('.ui.form')
.form({
inline : true,
on: 'blur',
fields: {
username: {
identifier : 'ssn',
rules: [
{
type : 'empty',
prompt : 'Please enter a SSN'
}
]
},
}
})
;
</script>
`
Here's a little bit more elegant solution that follows Semantic UI fields identification standard.
Field could be identified not only via input[name="…"] CSS selector offered in Oniisaki's accepted answer, but also by DOM element id or data-validation attribute:
/**
* Checks whether current field value or at least one of additionally
* given fields values is not empty, neither blank string.
* #param {string} value Current field value.
* #param {string} fieldIdentifiers Comma separated field identifiers.
* #return {boolean}
*/
$.fn.form.settings.rules.allEmpty = function(value, fieldIdentifiers) {
var $form = $(this);
return !!value || fieldIdentifiers.split(',').some(function(fieldIdentifier) {
return $form.find('#' + fieldIdentifier).val() ||
$form.find('[name="' + fieldIdentifier +'"]').val() ||
$form.find('[data-validate="'+ fieldIdentifier +'"]').val();
});
};
// Using newly created custom validation rule.
// Notice how multiple fields are defined, if required.
$('.ui.form').form({
ssn: {
identifier: 'ssn',
rules: [{
// Multiple field identifiers could be defined,
// like `allEmpty[phone,email,skype]`.
type: 'allEmpty[phone]',
prompt: 'SSN or Phone (at least one field) must be filled.'
}]
}
});
I would create a Semantic UI custom validation function that accepts parameters for your purpose.
Here's the link: http://jsfiddle.net/owcfuhtq/
The code:
$(document).ready(function(){
// function to check if at least one text is not empty for a collection of elements
// text is the value of the input device
// csv is the argument as string. It's the string inside "[" and "]"
$.fn.form.settings.rules.isAllEmpty = function(text,csv){
//If the text of the field itself isn't empty, then it is valid
if (text)
return true;
var array = csv.split(','); // you're separating the string by commas
var isValid = false; // return value
$.each(array,function(index,elem){
// for each item in array, get an input element with the specified name, and check if it has any values
var element = $("input[name='"+elem+"']");
//If element is found, and it's value is not empty, then it is valid
if (element && element.val())
isValid = true;
});
return isValid;
};
var formValidationRules =
{
ssn: {
identifier: 'ssn',
rules: [{
type: "isAllEmpty[phone]",
//If you got additional fields to compare, append it inside the [] with a "," separator
//E.g. isAllEmpty[field1, field2]
prompt: 'An error occurred'
}]
}
}
$('.ui.form').form(formValidationRules);
});
If you want to include select box you can use it sth like this :
$.fn.form.settings.rules.isAllEmpty = function (text, csv) {
if (text) {
return true;
}
var array = csv.split(',');
var isValid = false;
$.each(array, function (index, elem) {
var element = $("input[name='" + elem + "']");
if (element.length == 0) {
element = $("select[name='" + elem + "']")
}
if (element && element.val()) {
isValid = true;
}
});
return isValid;
};
We have a three dropdown date of birth field in our form, which, right now is producing three separate error messages if the fields are left blank and user clicks submission.
We would like to either make it just one of those error messages, or show some other display beneath the submission button.
Any guidance would be fantastic!
Although not mentioned, I'd have to imagine you're using JavaScript to validate the form, and if so, try something like the following, whereby in your HTML you have a single span tag (or p) that's accessible via an id tag (like #error or something). In your JavaScript, simply set the #error tag to whichever error was last seen by the JavaScript. As you'll note below, if both "birth date" and "name" are missing, the span tag will only display the missing name error text. That said, you could easily concatenate the strings if need be.
Pseudocode (JS)
function validateForm() {
var isValid = true,
var myErrorText;
if (birth date is missing) {
isValid = false;
errorText = "You must enter a birth date";
}
if (name is missing) {
isValid = false;
errorText = "You must enter a name";
}
// Display the error message if the form is invalid
if (!validForm) {
$('span#error').text(myErrorText);
}
return validForm;
}
HTML
<form method="post" action="/whatever" onSubmit="return validateForm()">
<input type="text" id="birthDate" name="birthDate" >
<input type="text" id="name" name="name">
<button type="submit" id="submit" class="btn">Submit</button>
</form>
<span id="error"></p>
I'm working with share forms in alfresco and trying to read the values of ticked checkboxes and checked radio buttons form a form. I extended both the user creation and userprofile form with these input controls and so far I have been unsuccessful at reading the textual values of said controls. Below is a snippet of code:
<div class="row">
<span class="label"><input id="${el}-input-spokenEnglish" type="checkbox" name="spokenLanguages" value="${msg("label.anglais"!"")?html}" /> ${msg("label.anglais")}</span>
<span class="label"><input id="${el}-input-spokenSpanish" type="checkbox" name="spokenLanguages" value="${msg("label.espagnol"!"")?html}" /> ${msg("label.espagnol")}</span>
<span class="label"><input id="${el}-input-spokenGerman" type="checkbox" name="spokenLanguages" value="${msg("label.allemand"!"")?html}" /> ${msg("label.allemand")}</span>
<span class="label"><input id="${el}-input-spokenChinese" type="checkbox" name="spokenLanguages" value="${msg("label.chinois"!"")?html}" /> ${msg("label.chinois")}</span>
<br/>
<span class="label">${msg("label.otherLanguages")} : </span>
<span class="input"><input id="${el}-input-spokenLanguages" type="text" size="30" maxlength="256" value="" <#immutablefield field="spokenLanugages" /> /> </span>
</div>
unfortunately I get nothing so far from whatever is returned and would gladly appreciate some insight into this.fre
If you look at userprofile.get.html.ftl, you'll see the following snippet:
<script type="text/javascript">//<![CDATA[
var userProfile = new Alfresco.UserProfile("${args.htmlid}").setOptions(
{
This means it's triggering a client-side JS file from Alfresco, in this case profile.js (see the head file). So just adding some input fields isn't enough.
You need to extend the client-side JS file.
In the function onEditProfile it gets the Dom elements.
But that's just for showing the actual fiels 'after' it's saved.
In profile.js you'll see: form.setSubmitAsJSON(true); that you have a json object from which you can get your fields.
And in userprofile.post.json.ftl it does a loop on the user.properties:
for (var i=0; i<names.length(); i++)
{
var field = names.get(i);
// look and set simple text input values
var index = field.indexOf("-input-");
if (index != -1)
{
user.properties[field.substring(index + 7)] = json.get(field);
}
// apply person description content field
else if (field.indexOf("-text-biography") != -1)
{
user.properties["persondescription"] = json.get(field);
}
}
user.save();
This probably means that you haven't extended the contentmodel of the cm:person object with your new properties.