How to use a data binding function of Knockoutjs inside JSON? - javascript

In the following code of Knockoutjs both the first name and last name works. The problem comes with displaying the full name. Its not getting the value of "this.firstName" and "this.lastName". How can i fix this problem.
var AppViewModel = {
firstName:ko.observable("Bert"),
lastName:ko.observable("Bertington"),
fullName:ko.pureComputed(function(){
return this.firstName()+ " "+this.lastName();
}, this, {deferEvaluation : false})
};
<h1>Introduction</h1>
<p>First name: <strong data-bind="text:firstName">todo</strong></p>
<p>Last name: <strong data-bind="text:lastName">todo</strong></p>
<p>First name: <input data-bind="value:firstName" /></p>
<p>Last name: <input data-bind="value:lastName" /></p>
<p>Full name: <strong data-bind="text:fullName"></strong></p>

i made some slightly changes as i am used to use knockout
var AppViewModel = function (data){
this.firstName=ko.observable("Bert"),
this.lastName=ko.observable("Bertington"),
this.fullName=ko.computed(function(){
return this.firstName() + this.lastName()
}, this, {deferEvaluation : false})
}
appModel = new AppViewModel();
ko.applyBindings(appModel);
here is a working fiddle:
http://jsfiddle.net/su3sfdff/
if you wanted the computed to be "live"
adjust the valueUpdate property this:
<p>First name: <input data-bind="value:firstName,valueUpdate:['afterkeydown', 'input']" /></p>
<p>Last name: <input data-bind="value:lastName,valueUpdate:['afterkeydown', 'input']" /></p>
http://jsfiddle.net/su3sfdff/1/

Related

Angular js change 'file' type to 'text' type [Safari]

I have inputs that build from objects in array.
Everything got right but when input.type = 'file', Angular change it to text type and i cant figure it out.
Did anything notice this?
My template:
<span ng-repeat="input in formInputs">
<label for="{{input.id}}">{{input.label}}</label>
<input type="{{input.type}}" id="{{input.id}}" name="{{input.name}}" ng-model="input.insert" ng-required="input.must">
</span>
My array:
var formInputs = [
{
label : 'first name',
id : 'id1',
type : 'text',
name : 'name1',
must : true,
insert : ''
},
{
label : 'upload file',
id : 'id2',
type : 'file',
name : 'name2',
must : true,
insert : ''
}
]
My result:
<span ng-repeat="input in formInputs">
<label for="id1">first name</label>
<input type="text" id="id1" name="name1" ng-model="input.insert" ng-required="input.must">
<label for="id2">upload file</label>
<input type="text" id="id2" name="name2" ng-model="input.insert" ng-required="input.must">
</span>
EDIT:
I have this flowing:
<input type="{{childInput.type}}" id="{{childInput.id}}" name="{{childInput.name}}">
And this array:
var formInputs = [
{
id : 'id',
type : 'file',
name : 'name',
}
]
The resolute [only in Safari]:
<input type="text" id="id" name="name">
Why its happening?
Thanks for your help!
From the AngularJS Documentation for input:
Note: Not every feature offered is available for all input types. Specifically, data binding and event handling via ng-model is unsupported for input[file].
So it looks like Angular falls back to type="text". There are a lot of answers which bring solutions to this, check out:
ng-model for <input type=“file”/>
From that answer, here's a way to deal with a file input.
.directive("fileread", [function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
var reader = new FileReader();
reader.onload = function (loadEvent) {
scope.$apply(function () {
scope.fileread = loadEvent.target.result;
});
}
reader.readAsDataURL(changeEvent.target.files[0]);
});
}
}
}]);
As mentionned by Hackerman, his jsfiddle seem to work (with Angular 1.0.1) at first sight, but it doesn't seem to populate the model correctly.

AngularJS: View is updated properly but not the model

I have a problem when implementing a nested list in Angular: the view gets updated properly but, on the other side, the code is not updated on change.
I think it will be much clearer with the code:
_this.categories = injections.map(function (category) {
return {
title: category.get('title'),
object: category,
criteria: category._criteria.map(function (oneCriteria) {
return {
object: oneCriteria,
type: oneCriteria.get("type"),
min: _this.range(oneCriteria.get("range")).min,
max: _this.range(oneCriteria.get("range")).max,
key: oneCriteria.get("key"),
value: _this.range(oneCriteria.get("range")).min,
defaultValue: _this.range(oneCriteria.get("range")).min,
selected: false
}
})
}
});
_this.category = _this.categories[0];
_this.job = {
title: '',
description: '',
salaryAmount: 0,
salaryTimeUnit: _this.salaryTimeUnits[0],
category: _this.category.object,
criteria: _this.category.criteria,
location: {latitude: 48.137004, longitude: 11.575928}
};
So and, very quick here is my HTML:
<div ng-repeat="category in controller.categories">
<input type="radio" name="group" ng-value="category.object.get('title')" id="{{category.object.get('title')}}"
ng-checked="controller.category == category" ng-click="controller.category = category">
{{category.title}}
</div>
<br>
Criteria:
<div ng-repeat="criterium in controller.category.criteria">
<div class="row vertical-align">
<div class="col-xs-9">
<span ng-click="criterium.selected = !criterium.selected"
ng-class="['list-group-item', {active:criterium.selected == true}]">{{criterium.key}}</span>
</div>
</div>
</div>
The problem is the following: the value are getting updated in the view (when you click on a radio button on the category, you see the corresponding criteria(s)). But the job is for one reason that I ignore not updated although it has the same reference as the HTML (a reference to this category.criteria).
Did I miss something?
controller.job.criteria is still just a reference to controller.categories[0]. Your code should successfully update controller.category to point at whichever category you clicked on, but that does not also update the reference in your job data structure.
What you want to do is make your ngClick event a bit more robust, i.e.:
<input type="radio" ng-click="controller.updateCategory(category)" />
and then in your js:
_this.updateCategory = function (category) {
_this.category = category;
_this.updateJob(category);
};
_this.updateJob = function (category) {
_this.job.category = category.object;
_this.job.criteria = category.criteria;
};
This will update the references in your job to match the new jazz.
I would, however, recommend leveraging ngModel and ngChange in your radios instead. Like:
<input type="radio" ng-model="controller.category" ng-value="category" ng-change="updateJob(category)" /> {{category.title}}

KnockoutJS not binding

I cant seem to get the binding to work on my KnockoutJS app.
JSFIDDLE -> http://jsfiddle.net/maylortaylor/pfqnkj17/
Here is the format of my JSON (generated by using <pre data-bind="text: ko.toJSON($root.forms,null,2)"></pre>)
[
{
"formTitle": "formTitle",
"formDescription": "formDesc",
"fieldTemplates": [
{
"fieldId": "text1",
"title": "title",
"description": "description fieldTemplate",
"isReq": true
},
{
"fieldId": "text2",
"title": "ttitle22",
"description": "description fieldTemplate 2",
"isReq": false
}
]
}
]
And here is how i am trying to call it in the page
<div id="MiddleColumn">
<input data-bind="textInput: $root.formTitle" type="text" placeholder="Title" class="span8 hideOffFocus input-full large-type">
<input data-bind="textInput: formDescription" type="text" placeholder="Description" class="hideOffFocus input-full">
</div
neither of those bindings work.
I create the forms object here
var FormModel = function (forms) {
var self = this;
self.forms = ko.observableArray(ko.utils.arrayMap(forms, function (form) {
return {
formTitle: form.formTitle, formDescription: form.formDescription,
fieldTemplates: ko.observableArray(form.fieldTemplates) };
}));
};
ko.applyBindings(new FormModel(initialData));
i hope your are expecting something like this
Working fiddle here
Now if you change something in textboxes in preview you can see automatic updates i.e mapping does make things back to ko way .
View Model :
var mapping = {
'fieldTemplates': {
create: function (options) {
return new FormModel(options.data);
}
}
}
function FormModel(forms) {
var self = this;
self.forms = ko.observableArray();
ko.mapping.fromJS(forms, mapping, self);
// other stuff
}
View :
<div id="MiddleColumn">
<input data-bind="textInput: $root.formTitle" type="text" />
<input data-bind="textInput: $root.formDescription" type="text"/><br/>
<div data-bind="foreach:$root.fieldTemplates">
<span data-bind="text:fieldId"></span>
<span data-bind="text:title"></span>
<span data-bind="text:description"></span>
<span data-bind="text:isReq"></span>
<br/>
</div>
</div>

knockout array binding not working

monkeyStuff does what i want, it updates the span content if i write in the input field.
But why doesn't it work with the voteStuff?
See it in Action: Fiddle
<body>
<div id="monkeyStuff">
<input type="text" data-bind="value:monkey" />
<span data-bind="text:monkey"></span>
</div>
<hr>
<div id="voteStuff">
<div data-bind="text: test"></div>
<ul data-bind="foreach: voters">
<li>
<input type="text" data-bind="value:name" />
<span data-bind="text:name"></span>
</li>
</ul>
</div>
<script>
var vm = {
monkey: ko.observable()
};
vm.monkey("Quak");
ko.applyBindings(vm, document.getElementById('monkeyStuff'));
var model = {
test: 'Test address text',
voters: ko.observableArray([
{ name: 'First Voter' },
{ name: 'Second Voter' }
])
};
ko.applyBindings(model, document.getElementById('voteStuff') );
</script>
</body>
EDIT: OK it works like this:
voters: ko.observableArray([
{ name: ko.observable('First Voter') },
{ name: ko.observable('Second Voter') }
])
But is there a way to do it automatic for each property in the voters array?
You need to make the name property of the elements in your voters ko.observableArray also observable, which would thus allow you to alter these properties with the bindings you have implemented:
voters: ko.observableArray([
{ name: ko.observable('First Voter') },
{ name: ko.observable('Second Voter') }
])
Working example: http://jsfiddle.net/he2zoa3d/2/

AngularJS server form validation / Javascript: Parsing javascript variable "path"

I'm building an Angular app connected to an REST service which does server-side input validation.
e.g. if I send a object to the server with JSON like:
entry: { id: 5, name: "Test", locales: ["de","en"] }
I will get an response like:
{ id: 5, name: "Test", countries: ["de","en"],
__errors__: [
{ field: "entry.name", message: "Test already in use" },
{ field: "entry.countries[1]", message: "'en' is not a country" }
]
}
(quotation marks omitted for better reading)
The field value is the "path" in javascriptish notation to the original value which caused the problem.
I'm somewhat free in what notation I will choose but I like this one because it's easy to read and integrates with the rest of the system. But I'm open to better suggestions.
The Question:
Now I want Angular to show which field failed with which message. What's the best way of doing this?
I tried things like $scope.EditForm.$setValidity( field, message ) but it has no effect.
(nb: I'm using Angular with Bootstrap)
Why not return an errors object that you bind to in your markup?
Here's a fiddle: http://jsfiddle.net/edeustace/UMRU9/2/
Here's a snippet:
<div ng-app="App" ng-controller="Ctrl">
<input id="name" type="text" ng-model="name"></input>
<span ng-show="errors.name" style="color: red">{{errors.name}}</span>
<br/>
<input id="lastName" type="text" ng-model="lastName"></input>
<span ng-show="errors.lastName" style="color: red">{{errors.lastName}}</span>
<div ng-repeat="c in countries">
<input ng-model="c" type="text"></input>
<span style="color: red" ng-show="errors.countries[$index]">{{errors.countries[$index]}} </span>
</div>
<button ng-click="submit()">Submit</button>
</div>
Js:
var app = angular.module('App', []);
app.controller('Ctrl', function($scope){
$scope.name = "Ed";
$scope.lastName = "Eustace";
$scope.countries = ["Ireland", "England"];
$scope.submit = function(){
$scope.errors = {name: "Name in use", lastName: "", countries: ["Ireland is not available"] };
}
});
That way you only need to wire up the ui and the update will happen for free due to data binding.

Categories

Resources