Ember set input value to JSON - json

I'd like to set the value attribute of an input the some JSON:
Ember.TextField.extend({
valueBinding: Ember.Binding.transform(function(val){
return JSON.stringify(val);
})
});
If val="test" (a string) The element in the DOM is rendered like this:
<input id="ember881" class="ember-view ember-text-field" type="text" value="" test""="">
Is there a way to return some "SafeEscaped" version?
Regards

To answer your question, yes. The following code should do the trick, though I'm sure there is a prettier solution somewhere:
return new Handlebars.SafeString(Handlebars.Utils.escapeExpression(JSON.stringify(val)).toString()).toString();
However (though I'm not sure what you're trying to achieve with that code), I'd rewrite the view to read:
App.test = Ember.TextField.extend({
valueBinding: 'test',
val: {"cat":"meow"},
test: function(){
return new Handlebars.SafeString(Handlebars.Utils.escapeExpression(JSON.stringify(this.val)).toString()).toString();
}.property('val')
});
This should output:
<input type="text" value="{"test":"meow"}" class="ember-view ember-text-field" id="ember239">
Note that I'm assuming you're using Handlebars.

Related

Working with custom components for input generates "No value accessor for form control with path X->0->Y"

I have a working form taking the following HTML markup. No errors or warnings.
<div class="input-element">
<div class="input-caption">Title</div>
<input type="text"
formControlName="targetField"
class="form-control">
</div>
I transformed it into a custom component, which also works, as shown below.
<app-input-text [info]="'Title'"
formControlName="targetField"
ngDefaultControl></app-input-text>
In my next view, I need to use FormArray as follows - still working code.
<div formArrayName="stuff">
<div *ngFor="let thing of form.controls.stuff.controls; let i = index;"
[formGroupName]=i>
<div class="input-element">
<div class="input-caption">Title</div>
<input type="text"
formControlName="targetField"
class="form-control">
</div>
</div>
</div>
Now, I expected that combining both (i.e. being able to use custom input component and being able to form array for components) would post no problem. However, the sample below doesn't work.
<div formArrayName="stuff">
<div *ngFor="let thing of form.controls.stuff.controls; let i = index;"
[formGroupName]=i>
<app-input-text [info]="'Title'"
formControlName="targetField"
class="col-sm-6"></app-input-text>
</div>
</div>
It generates the following error.
No value accessor for form control with path: 'stuff -> 0 -> targetField'
The custom component is design like this (although given that it works in the explicit markup example, I'm not sure if it's relevant information). The only (wild) guess I have might be that value isn't jacked into the form array field somehow.
export class InputTextComponent implements OnInit {
constructor() { this.value = new EventEmitter<string>(); }
#Input() info: string;
#Output() value: EventEmitter<string>;
onEdit(value: any): void { this.value.emit(value); }
}
The group and array creating in the current view is done like this (not sure if this is of any relevance neither, as it works for the explicit HTML markup case).
this.form = builder.group({
id: "",
stuff: builder.array([
builder.group({ targetField: "aaa" }),
builder.group({ targetField: "bbbb" }),
builder.group({ targetField: "cc" })
])
});
Is there a limitation in Angular in this regard that I'm not aware of? I'm rather sure there's not and that I'm just doing something fairly clever simply missing a tiny detail.
I do understand the error but I can't see how it relates to the code. The form can't find the 0th element in the array or that element has no field of that name. Since I do get to see a few rows, I know there must be a 0th element. Since I specified the name of the field, I know there is indeed such. What else am I missing?

Validate URL with AngularJS and HTML 5

Good morning all:
Looks like a very common question, but after googling for hours I am not able to figure this out: how to validate an URL including www without http.
These is what I did:
Used the input type url: it does not accept www.google.com;
Changed the input type to text and used ng-pattern: I still get the www.google.com invalid;
Changed different regex but still not working.
So when I click on the submit button, I show an alert if the form is invalid (true invalid, false valid). Here is my Plunker
Thanks for the help
Instead of binding the regex to scope, you could directly add the regex to ng-pattern attribute. Like this:
<input type="text" ng-pattern="/^(http[s]?:\/\/){0,1}(www\.){0,1}[a-zA-Z0-9\.\-]+\.[a-zA-Z]{2,5}[\.]{0,1}/" ng-model="website">
I have updated the plunkr. Please take a look at this. Plukr
The thing here is, if you want to bind ng-pattern from controller, your regex shouldn't contain the starting and ending /s. Like this:
$scope.regex = "^(http[s]?:\\/\\/){0,1}(www\\.){0,1}[a-zA-Z0-9\\.\\-]+\\.[a-zA-Z]{2,5}[\\.]{0,1}$"
But, if you directly specify pattern like ng-pattern="/^(http|https|...)$/", you need the extra /s as well.
working plunker
Try using the ng2-validation library. It can be used to perform most validations you should ever need. Angular2 custom validation, inspired by jQuery validation.
I think we can also use AngularJs builtin URL validator.
<script>
angular.module('urlExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.url = {
text: 'http://google.com'
};
}]);
</script>
<form name="myForm" ng-controller="ExampleController">
<label>URL:
<input type="url" name="input" ng-model="url.text" required>
<label>
<div role="alert">
<span class="error" ng-show="myForm.input.$error.required">
Required!</span>
<span class="error" ng-show="myForm.input.$error.url">
Not valid url!</span>
</div>
<tt>text = {{url.text}}</tt><br/>
<tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
<tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
<tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
</form>
`
if (this.resource.url.match("^(https:\/\/|http:\/\/)")) {
if (this.resource.url.match("^(https:\/\/www\.|http:\/\/www\.)?([da-z.-]+)\\.([a-z.]{2,6})")) {
}
else
{
errorMessages.push("url is invalid");
}
}
else {
errorMessages.push("url is invalid");
}
`

How do I reset a form including removing all validation errors?

I have an Angular form. The fields are validated using the ng-pattern attribute. I also have a reset button. I'm using the Ui.Utils Event Binder to handle the reset event like so:
<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
<div>
<label>
Area Code
<input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern">The area code must be three digits</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern">The phone number must be seven digits</div>
</div>
</div>
<br>
<div>
<button type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
As you can see, when the form is reset it calls the reset method on the $scope. Here's what the entire controller looks like:
angular.module('app').controller('mainController', function($scope) {
$scope.resetCount = 0;
$scope.reset = function(form) {
form.$setPristine();
form.$setUntouched();
$scope.resetCount++;
};
$scope.search = function() {
alert('Searching');
};
});
I'm calling form.$setPristine() and form.$setUntouched, following the advice from another question here on Stack Overflow. The only reason I added the counter was to prove that the code is being called (which it is).
The problem is that even after reseting the form, the validation messages don't go away. You can see the full code on Plunker. Here's a screenshot showing that the errors don't go away:
I started with the comment from #Brett and built upon it. I actually have multiple forms and each form has many fields (more than just the two shown). So I wanted a general solution.
I noticed that the Angular form object has a property for each control (input, select, textarea, etc) as well as some other Angular properties. Each of the Angular properties, though, begins with a dollar sign ($). So I ended up doing this (including the comment for the benefit of other programmers):
$scope.reset = function(form) {
// Each control (input, select, textarea, etc) gets added as a property of the form.
// The form has other built-in properties as well. However it's easy to filter those out,
// because the Angular team has chosen to prefix each one with a dollar sign.
// So, we just avoid those properties that begin with a dollar sign.
let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);
// Set each control back to undefined. This is the only way to clear validation messages.
// Calling `form.$setPristine()` won't do it (even though you wish it would).
for (let name of controlNames) {
let control = form[name];
control.$setViewValue(undefined);
}
form.$setPristine();
form.$setUntouched();
};
$scope.search = {areaCode: xxxx, phoneNumber: yyyy}
Structure all models in your form in one place like above, so you can clear it like this:
$scope.search = angular.copy({});
After that you can just call this for reset the validation:
$scope.search_form.$setPristine();
$scope.search_form.$setUntouched();
$scope.search_form.$rollbackViewValue();
There doesn't seem to be an easy way to reset the $errors in angular. The best way would probably be to reload the current page to start with a new form. Alternatively you have to remove all $error manually with this script:
form.$setPristine(true);
form.$setUntouched(true);
// iterate over all from properties
angular.forEach(form, function(ctrl, name) {
// ignore angular fields and functions
if (name.indexOf('$') != 0) {
// iterate over all $errors for each field
angular.forEach(ctrl.$error, function(value, name) {
// reset validity
ctrl.$setValidity(name, null);
});
}
});
$scope.resetCount++;
You can add a validation flag and show or hide errors according to its value with ng-if or ng-show in your HTML. The form has a $valid flag you can send to your controller.
ng-if will remove or recreate the element to the DOM, while ng-show will add it but won't show it (depending on the flag value).
EDIT: As pointed by Michael, if form is disabled, the way I pointed won't work because the form is never submitted. Updated the code accordingly.
HTML
<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
<div>
<label>
Area Code
<input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern" ng-if="searchForm.areaCode.$dirty">The area code must be three digits</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern" ng-if="searchForm.phoneNumber.$dirty">The phone number must be seven digits</div>
</div>
</div>
<br>
<div>
<button type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
JS
$scope.search = function() {
alert('Searching');
};
$scope.reset = function(form) {
form.$setPristine();
form.$setUntouched();
$scope.resetCount++;
};
Codepen with working solution: http://codepen.io/anon/pen/zGPZoB
It looks like I got to do the right behavior at reset. Unfortunately, using the standard reset failed. I also do not include the library ui-event. So my code is a little different from yours, but it does what you need.
<form name="searchForm" id="searchForm" ng-submit="search()">
pristine = {{searchForm.$pristine}} valid ={{searchForm.$valid}}
<div>
<label>
Area Code
<input type="tel" required name="areaCode" ng-model="obj.areaCode" ng-pattern="/^([0-9]{3})?$/" ng-model-options="{ allowInvalid: true }">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern">The area code must be three digits</div>
<div class="error" ng-message="required">The area code is required</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" required name="phoneNumber" ng-model="obj.phoneNumber" ng-pattern="/^([0-9]{7})?$/" ng-model-options="{ allowInvalid: true }">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern">The phone number must be seven digits</div>
<div class="error" ng-message="required">The phone number is required</div>
</div>
</div>
<br>
<div>
<button ng-click="reset(searchForm)" type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
And JS:
$scope.resetCount = 0;
$scope.obj = {};
$scope.reset = function(form_) {
$scope.resetCount++;
$scope.obj = {};
form_.$setPristine();
form_.$setUntouched();
console.log($scope.resetCount);
};
$scope.search = function() {
alert('Searching');
};
Live example on jsfiddle.
Note the directive ng-model-options="{allowinvalid: true}". Use it necessarily, or until the entry field will not be valid, the model value is not recorded. Therefore, the reset will not operate.
P.S. Put value (areaCode, phoneNumber) on the object simplifies purification.
Following worked for me
let form = this.$scope.myForm;
let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);
for (let name of controlNames) {
let control = form [name];
control.$error = {};
}
In Short: to get rid of ng-messages errors you need to clear out the $error object for each form item.
further to #battmanz 's answer, but without using any ES6 syntax to support older browsers.
$scope.resetForm = function (form) {
try {
var controlNames = Object.keys(form).filter(function (key) { return key.indexOf('$') !== 0 });
console.log(controlNames);
for (var x = 0; x < controlNames.length; x++) {
form[controlNames[x]].$setViewValue(undefined);
}
form.$setPristine();
form.$setUntouched();
} catch (e) {
console.log('Error in Reset');
console.log(e);
}
};
I had the same problem and tried to do battmanz solution (accepted answer).
I'm pretty sure his answer is really good, but however for me it wasn't working.
I am using ng-model to bind data, and angular material library for the inputs and ng-message directives for error message , so maybe what I will say will be useful only for people using the same configuration.
I took a lot of look at the formController object in javascript, in fact there is a lot of $ angular function as battmanz noted, and there is in addition, your fields names, which are object with some functions in its fields.
So what is clearing your form ?
Usually I see a form as a json object, and all the fields are binded to a key of this json object.
//lets call here this json vm.form
vm.form = {};
//you should have something as ng-model = "vm.form.name" in your view
So at first to clear the form I just did callback of submiting form :
vm.form = {};
And as explained in this question, ng-messages won't disappear with that, that's really bad.
When I used battmanz solution as he wrote it, the messages didn't appear anymore, but the fields were not empty anymore after submiting, even if I wrote
vm.form = {};
And I found out it was normal, because using his solution actually remove the model binding from the form, because it sets all the fields to undefined.
So the text was still in the view because somehow there wan't any binding anymore and it decided to stay in the HTML.
So what did I do ?
Actually I just clear the field (setting the binding to {}), and used just
form.$setPristine();
form.$setUntouched();
Actually it seems logical, since the binding is still here, the values in the form are now empty, and angular ng-messages directive is triggering only if the form is not untouched, so I think it's normal after all.
Final (very simple) code is that :
function reset(form) {
form.$setPristine();
form.$setUntouched();
};
A big problem I encountered with that :
Only once, the callback seems to have fucked up somewhere, and somehow the fields weren't empty (it was like I didn't click on the submit button).
When I clicked again, the date sent was empty. That even more weird because my submit button is supposed to be disabled when a required field is not filled with the good pattern, and empty is certainly not a good one.
I don't know if my way of doing is the best or even correct, if you have any critic/suggestion or any though about the problem I encountered, please let me know, I always love to step up in angularJS.
Hope this will help someone and sorry for the bad english.
You can pass your loginForm object into the function ng-click="userCtrl.login(loginForm)
and in the function call
this.login = function (loginForm){
loginForm.$setPristine();
loginForm.$setUntouched();
}
So none of the answers were completely working for me. Esp, clearing the view value, so I combined all the answers clearing view value, clearing errors and clearing the selection with j query(provided the fields are input and name same as model name)
var modelNames = Object.keys($scope.form).filter(key => key.indexOf('$') !== 0);
modelNames.forEach(function(name){
var model = $scope.form[name];
model.$setViewValue(undefined);
jq('input[name='+name+']').val('');
angular.forEach(model.$error, function(value, name) {
// reset validity
model.$setValidity(name, null);
});
});
$scope.form.$setPristine();
$scope.form.$setUntouched();

Input field set as 'Value=' instead of 'value='

I have a project written in C# MVC using Razor templates. On one of my pages I have several input fields that contain numeric values. The Razor code that sets the values of these input fields looks like this:
#Html.Editor(Model.DesignParams[i].ParamId,
new {
htmlAttributes = new
{
#Value = Model.DesignParams[i].DefaultValue,
#class = "form-control text-right",
#type = "text",
id = "_" + Model.DesignParams[i].ParamId,
uomid = Model.DesignParams[i].UOMId,
measureid = Model.DesignParams[i].MeasureId
}
})
The above code works fine using FireFox and Chrome and generates an input field that looks like this:
<input type="text" uomid="MBH" name="HeatOfRejection" measureid="HeatLoad"
id="_HeatOfRejection" class="form-control text-right text-box single-line"
value="5000.0">
But the same Razor code, identical #Model values viewed with IE generates this:
<input Value="5000" class="form-control text-right text-box single-line"
id="_HeatOfRejection" measureid="HeatLoad" name="HeatOfRejection"
type="text" uomid="MBH" value="" />
As you can see, there is a difference between the value= attribute generated for IE in that the value attribute that gets my actual value begins with an uppercase 'V' and the lowercase value is an empty string. I'm stumped on this...
Can anyone tell me why this is happening and possibly how to handle it?
This difference effects jQuery's ability to return the input's value with:
var value = $(inputfield).attr("value");
Maybe .val() will retrieve the input field value, but this is going to require a rewrite of core jQuery code that supports this page and others, so I wanted to ask if anyone can tell me why this 'Value=' gets created for IE only and if there is a way of overcoming the problem.
Update:
Changing #Value to #value (or just value) results in an empty value attribute in Firefox and IE:
<input type="text" value="" uomid="MBH" name="HeatOfRejection" measureid="HeatLoad"
id="_HeatOfRejection" class="form-control text-right text-box single-line">
As StuartLC points out, you are trying to get Html.Editor to do something it wasn't designed to do.
What happens when you pass a #value or #Value key to the htmlAttributes is that the rendering engine produces an attribute with that name in addition to the value attribute it's already generating:
<input type="text" name="n" value="something" value="somethingElse" />
or
<input type="text" name="n" value="something" Value="somethingElse" />
In both cases, you're giving the browser something bogus, so it can't be expected to exhibit predictable behavior.
As alluded above, Html.Editor has functionality to generate the value attribute based on the expression argument you pass to it. The problem is that you are using that incorrectly as well. The first argument to Html.Editor() needs to be an expression indicating the model property that the editor should be bound to. (e.g. the string value "DesignParams[0].ParamId") Nowadays, the preferred practice is to use the more modern EditorFor that takes a lambda function, as StuartLC showed in his post:
#Html.EditorFor(model => model.DesignParams[i].ParamId, ...)
You are "capitalising" the value html attribute. Change this to lower case...
#Value = Model.DesignParams[i].DefaultValue
as below ...
#value = Model.DesignParams[i].DefaultValue
IE is not the smartest of web browsers and there's definitely something wrong in the way Trident (they're parsing engine) validates elements' attributes as seen in these threads...
https://github.com/highslide-software/highcharts.com/issues/1978
Highcharts adds duplicate xmlns attribute to SVG element in IE
Also, as already noted somewhere else. What's the need for the Editor extension method? Isn't it simpler to just use TextBoxFor instead?
#Html.TextBoxFor(model => model.DesignParams[i].ParamId
, new
{
#class = "form-control text-right"
, uomid = Model.DesignParams[i].UOMId
, measureid = Model.DesignParams[i].MeasureId
})
Editor works with metadata. then you need to more about this,
http://aspadvice.com/blogs/kiran/archive/2009/11/29/Adding-html-attributes-support-for-Templates-2D00-ASP.Net-MVC-2.0-Beta_2D00_1.aspx
But the easiest way is go with
#model Namespace.ABCModel
#using (Html.BeginForm("Action", "Controller", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.TextBoxFor(model => model.DesignParams[i].ParamId, new { #class = "form-control text-right", uomid = Model.DesignParams[i].UOMId, measureid = Model.DesignParams[i].MeasureId })
}
You shouldn't be using invalid Html attributes in this way. Use the data- attributes in Html 5.
Also, your use of #Html.Editor(Model.DesignParams[i].ParamId (assuming ParamId is a string) deviates from the helper's purpose, which is to reflect the property with the given name off the Model, and use the value of this property as the Html value attribute on the input. (MVC will be looking for a property on the root model with whatever the value of ParamId is, which seems to silently fail FWR)
I would do the defaulting of Model.DesignParams[i].ParamId = Model.DesignParams[i].DefaultValue in the Controller beforehand, or in the DesignParams constructor.
#Html.EditorFor(m => m.DesignParams[0].ParamID,
new {
htmlAttributes = new
{
// Don't set value at all here - the value IS m.DesignParams[0].ParamID
#class = "form-control text-right",
#type = "text",
id = "_" + Model.DesignParams[i].ParamId,
data_uomid = Model.DesignParams[i].UOMId,
data_measureid = Model.DesignParams[i].MeasureId
}
Note that this will give the input name as DesignParams[0].ParamID, which would be needed to post the field back, if necessary.
Here's a Gist of some example code
(The underscore will be converted to a dash)
Use data() in jQuery to obtain these values:
var value = $(inputfield).data("uomid");

override the "id" attribute of Html.EditorFor - not working

Trying to override the "id" attribute of Html.TextBoxFor (MVC 3) so that it should look like:
<input type="text" name="Password" id="#idPasswordTextBox" value="#Model.Password" />
where "idPasswordTextBox" is defined as:
string idPasswordTextBox = "passwordText_"+#Model.Key; in the same cshtml file.
This is working fine if I use as :
<input type="text" name="Password" id="#idPasswordTextBox" value="#Model.Password" />
but not working if I do it this way:
#Html.TextBoxFor(model => model.Password, new { id = "#idPasswordTextBox" })
Looks like the "id" attribute is getting messed up. What am I missing? Can anybody help? I am new bee in ASP.net.
Thanks in advance.
Sorry, i should've looked more carefully. You don't want quotes around #idPasswordTextBox in your TextBoxFor method. That is run on the server, so when you put quotes around the name it is being treated as a literal string. Remove the quotes, and remove the # sign in front of id, and it will work.
Its important to always remember what is running on the server, and what is running on the client.
#Html.TextBoxFor(model => model.Password, new { id = #idPasswordTextBox })