Basic binding with knockoutjs mapping plugin - javascript

It is my first time using knockoutjs so just trying to get my head around it.
I am trying to map some json to some html inputs with the knockoutjs mapping plugin.
I have the following json which I am receiving ok.
{"firstname":"it","lastname":"worked"}
I am mapping it with the code below:
var myProgram{
viewModel : {},
$.post(myServer, {}, function(data) {
viewModel = ko.mapping.fromJS(data);
});
}
And then trying to bind it...
<input type="text" data-bind="value: firstname" id="text-firstname" placeholder="Text input" value="" data-mini="true" required />
However it doesn't work, and I'm not really sure where to look next to debug it. Is anybody able to help?

fixed with:
ko.applyBindings(viewModel);
Which is missing from the mappings getting started docs.

Related

Creating new record in Aurelia -- error with JSON encoding?

I'm creating an Aurelia app and it successfully pulls records from an API, edits and saves changes to individual records, and deletes individual records. However, I cannot seem to fix a problem with creating new records.
In calendar.js, I have the function newCalendar() which is called by a form button.
newCalendar() {
this.selectedId = this.error = null;
this.calendar = [];
this.calendar.cal_name_tran = "New Calendar";
this.calendar.cal_name_orig = "Nuevo Calendario";
console.log(this.calendar);
console.log(JSON.stringify(this.calendar));
$('#edit_calendar').modal('show');
$('#cal_name_orig').focus();
}
When I click the button, I get the following text in the console log:
[cal_name_tran: "New Calendar", cal_name_orig: "Nuevo Calendario"]
[]
The text "New Calendar" and "Nuevo Calendario" are successfully bound to the View in the Modal form.
<form>
<div class="form-group">
<label class="control-label" for="cal_name_orig">Calendar name</label>
<input type="text" class="form-control" id="cal_name_orig" placeholder="${'Calendar_Example_Name'&t}" value.bind="calendar.cal_name_orig & validate">
</div>
<div class="form-group">
<label class="control-label" for="cal_name_tran">Translated name</label>
<input type="text" class="form-control" id="cal_name_tran" value.bind="calendar.cal_name_tran & validate">
</div>
</form>
However, when I try to save the calendar using the following function, it sends an empty string, same as what is showing up in the second line of the console log (above).
saveCalendar() {
console.log(JSON.stringify(this.calendar));
this.httpClient.fetch("/calendar/new", {
method: 'put',
body: json(this.calendar)
});
}
Why does JSON.stringify(this.calendar) results in an empty array when this.calendar shows the values of the array?
No need to give this answer an upvote since the actual question has already been answered. I wanted to mention an issue with your code.
In your newCalendarfunction, you have two lines that use jquery to work with elements in your view:
$('#edit_calendar').modal('show');
$('#cal_name_orig').focus();
You're using IDs here. I wanted to point out that this is both unnecessary, and discouraged when using Aurelia. Instead of using IDs to reference the elements, you can use the ref custom attribute provided by Aurelia. The ref attribute allows you to create a reference to a DOM element as a property on your VM. Let's look at the cal_name_orig textbox:
<input type="text"
class="form-control"
id="cal_name_orig"
placeholder="${'Calendar_Example_Name'&t}"
value.bind="calendar.cal_name_orig & validate">
You can simply replace the id attribute with the `ref attribute:
<input type="text"
class="form-control"
ref="cal_name_orig"
placeholder="${'Calendar_Example_Name'&t}"
value.bind="calendar.cal_name_orig & validate">
and now this element is available in your VM using this.cal_name_orig (you might consider renaming this to match JS naming conventions). So instead of:
$('#cal_name_orig').focus();
you can do:
$(this.cal_name_orig).focus();
And now you don't have to worry about some other part of your application having a id naming conflict!
This isn't the only type of reference you can do. To learn more, check out our docs here: http://aurelia.io/hub.html#/doc/article/aurelia/binding/latest/binding-basics/5
Thanks!
Since you want the calendar to contain key-value pairs, you can make it an object (instead of array).
this.calendar = {};
JSON.stringify expects a valid JSON (can be an array then as well). In your example, although you are creating the calendar as an array, you are using it as an object.
If you want it to be an array, you will have to use the push method instead:
this.calendar = [];
this.calendar.push("New Calendar", "Nuevo Calendario");

Create a button/tag with built in cancel sign at the end

I'm trying to create a page that requires buttons/tags similar to following image using html:
I'm not sure what these buttons are called. Ultimately I want to create them dynamically since what buttons need to be created are different from different users.
Any suggestions are greatly appreciated. Thanks.
These things are called Chips. If you are using angular, then you can look at the demo here.
You can implement such functionality using jQuery or core javascript.
You can use this jquery readymade plugin
Demo - http://xoxco.com/projects/code/tagsinput/example.html
GitHub Link : https://github.com/xoxco/jQuery-Tags-Input
A solution to this in Angularjs would be to create a controller for your form that determines whether or not the browser is allowed to display your cancel button. This can be created using the following code:
var app = angular.module('formExample', [])
app.controller('ExampleController', ['$scope', function($scope) {
$scope.canCancel = false;
$scope.updateCancelVal = function(myForm) {
$scope.canCancel = $scope.myForm.$valid;
};
}]);
And then in your html, it would look like:
<div ng-controller="ExampleController">
<form name="myFormName">
Name: <input ng-keyup="updatCancelVal(myFormName)" type="text" ng-model="user.name" /><br>
Email: <input ng-keyup="updatCancelVal(myFormName)" type="email" ng-model="user.email" /><br>
<button ng-show="canCancel" href="myCancelLink.html"> Cancel </button>
</form>
</div>
Ultimately, this makes it so that the Cancel button is only shown when canCancel is true. And on each key input in each of the separate inputs, this value is updated until the form is valid. If you need more aid in implementing this, lmk and more detail will be added!

JsViews how to make data binding happen on root object as well as its nested properties?

I am experiencing odd behavior when data linking an object to a form that led me to re-question what exactly is being data bound?
Basically I have a form that creates new Companies as well as updates them. The actual creation/update is done via ajax, which is why I am using the same form for both purposes. In the case when I have to create a company, everything works as I expect. However when I have to update a company, things don't work like how I expect them to. Please have a look at the following code.
Here is my sample Form HTML:
<div id="result"></div>
<script type="text/x-jsrender" id="CompanyFormTemplate">
<form>
<input type="text" data-link="Company.Name" />
</form>
</script>
Here is my Javascript code:
var app = new CompanyFormContext();
function CompanyFormContext() {
this.Company = {
Name: ''
};
this.setCompany = function (company) {
if (company) {
$.observable(this).setProperty('Company', company);
}
};
};
$(function () {
initPage();
...
if (...) {
// we need to update company information
app.setCompany({ Name: 'Company ABC' });
}
});
function initPage() {
var template = $.templates('#CompanyFormTemplate');
template.link("#result", app);
}
Instead of the form input showing 'Company ABC', it is empty. However if I enter anything in it, then the Company.Name value does change! But while I want the input to data bind to Name property of my Company object, I also want it to be aware of any changes made to the (parent) Company object and update it's data binding to it's Name property accordingly.
So my question is how should I change the way I am writing this code so that I can achieve a data bound both on the root object as well as the property?
The issue you were having was because in your scenario, you have paths like Company.Name for which you want to data-link to changes not only of the leaf property but also to changes involving replacing objects higher up in the path (in this case the Company).
For that you need to use the syntax data-link="Company^Path".
See the section Paths: leaf changes or deep changes in this documentation topic:
http://www.jsviews.com/#observe#deep.
See also the examples such as Example: JsViews with plain objects and array in this topic: http://www.jsviews.com/#explore/objectsorvm.
Here is an update of your jsfiddle, using that syntax: https://jsfiddle.net/msd5oov9/2/.
BTW, FWIW, in your fix using {^{for}} you didn't have to use a second template - you could alternatively have written:
<form class="form-horizontal">
{^{for Company}}
...
<input type="text" data-link="Name" />
{{/for}}
</form>
To respond also to your follow-up question in your comment below, you can associate any 'block' tag with a template. Using tmpl=... on the tag means you have decided to separate what would have been the block content into a separate re-usable template. (A 'partial', if you will). The data context for that template will be the same as it would have been within the block.
So specifically, {{include}} {{if}} and {{else}} tags do not move the data context, but {{for}} and {{props}} do. With custom tags you can decide...
So in your case you could use either {^{for Company tmpl=.../}} or {{include tmpl=.../}} but in the second case your other template that you reference would use <input type="text" data-link="Company^Name" /> rather than <input type="text" data-link="Name" />.
Here are some relevant links:
http://www.jsviews.com/#samples/jsr/composition/tmpl
http://www.jsviews.com/#includetag
http://www.jsviews.com/#fortag
I discovered one way to achieve this. It might seem complex at first but it will make sense once you understand it properly.
(PS: I wish there was a sample like this. I might just blog about it.)
HTML Markup:
<script type="text/x-jsrender" id="CompanyFormTemplate">
<form>
{^{for Company tmpl="#CompanyDetailsTemplate" /}
</form>
</script>
<script type="text/x-jsrender" id="CompanyDetailsTemplate">
<input type="text" data-link="Name" />
</script>
Javascript: No changes needed from code above.
Okay so as I said, the solution might look complicated but it turns out all I really had to do was to set up data binding first on the Company object, and then to it's property objects. I wonder if there is a more elegant solution (i.e. one in which all of this can be achieved in a single template) however this solution ensures that data-binding is happening both on the parent object as well as its' properties.
I have posted a JsFiddle for this solution, so if anyone comes across this problem and wants to understand how this solution would work for their particular problem, they will be able to play with a working solution.

jQuery find() returning undefined

I have a pretty typical form with some inputs and a submit button, however when I try to use jQuery's find() method, I get some annyingly inconsistent results. The are as follows:
$("#frmContact").find(":submit") -> undefined is not a function
$("#frmContact").find(".btn") -> works
$("#frmContact").find(".btn.secondary") -> undefined is not a function
$("#frmContact").find("input[type=submit]") -> works
$("#frmContact input:submit") -> undefined is not a function
$("input[type=submit]", "#frmContact") -> undefined is not a function
$("form").find("input") -> works
$("form").find("input[type=submit]") -> undefined is not a function
What's going on here and how do I fix this?
I'm using jQuery 1.11 after upgrading from 1.9 hoping that it will fix the issue. There's also Angular on the page and I'm calling this code from the console after everything is loaded.
My form, just in case you need it is as follows [EDIT] updated to show actual HTML output with Angular classes and directives:
<form ng-submit="saveContact($event, '/applicants/update_contact?id=1593')" class="ng-pristine ng-valid" _lpchecked="1" id="frmContact">
<h4 class="section-header">Applicant Contact Information</h4>
<field id="email" type="text" model="applicant.email" label="Email address" class="ng-isolate-scope">
<div ng-class="['field', isRequired()]" class="field"> <!-- Text/File/Password Field -->
<label ng-bind="label" class="ng-binding">Email address</label>
<input type="text" ng-model="model" class="ng-pristine ng-valid">
</div>
</field>
<div class="field">
<input type="submit" value="Save" class="btn secondary">
</div>
</form>
My JavaScript libraries are loaded in this order:
jQuery 11
jQuery UI
Angular
My app.js
Scripts are loaded at the bottom of the page.
AngularJS comes with JQLite, a lightweight smaller verison of jQuery, which is the one you are using here and that gives you unexpected results.
In order for Angular to works with the full jQuery, you need to make sure that you load jQuery before Angular. So watch your <script> tags order.
So it turns out the hand that held the knife to my throat was my own.
Earlier in the application's development it was useful to add a couple helper methods on the Object prototype by doing Object.prototype.findId = function() { ... }. Turns out this raised all sorts of hell when trying to perform the find() method in jQuery.
I fixed it by turning this:
Object.prototype.myMethod = function() { ... }
into this
var specialObject = function(object) {
return {
myMethod: function() { ... }
}
}
I didn't realize that my earlier approach was such bad practice until I read this: Extending Object.prototype JavaScript
Thank you all for your help, guys.

how to get value ng-model in html to javascript

Probably silly question, but I have my html form with simple input and password:
<li>
<input type="text" placeholder="Username" ng-model="user.username" />
<a class="iconani usera"></a>
</li>
<li>
<input type="password" placeholder="Password" ng-model="user.password" />
<a class="iconani locka"></a>
</li>
and i want to get value from ng-model to java script
query.equalTo("user", document.getElementById('value from ng-model'));
I use this from parse.com
Can you help me?
In AngularJS, you don't need (and want) to touch your DOM at all to get the data. ng-model directive creates an automated two-way binding between your <input> and your $scope.user variable's properties.
login($scope.user.username, $scope.user.password, ...);
You don't need to touch the form itself at all.
hon2a's answer is the right one ;-) I can try to explain it a bit differently as I also just recently started using angular. A good and simple intro to angular concepts of ng-model and controllers is given at http://www.w3schools.com/angular/angular_intro.asp.
So, all your javascript should be executing in Angular Controllers. In the corresponding controller (i.e. javascript code) the data from HTML form is bound using that angular directive "ng-model" and nothing else. You have your HTML part just fine, assuming you have the angular stuff somewhere linked properly (I would strongly recommend using Yeoman Angular generator to handle that...). At least there should be something like this:
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body>
<div ng-app="yourApp" ng-controller="yourController">
<!-- your app part goes here -->
</div>
And then the in the angular controller you actually have that data at hand automatically without doing anything else than just having a constructor/initialiser for it:
angular.module('yourApp').controller('yourController', function ($scope) {
$scope.user = {'username': '', 'userpassword': ''};
// And rest of your stuff goes here...
// In your functions, just use $scope.user.username and $scope.user.userpassword.
}
Hope this helps...

Categories