Aurelia Custom Elements data binding - javascript

I am using a custom-element with my Aurelia app. When I am binding data from my view-model with the custom-element, it is working fine. Even if I make some changes in the data in the custom-element control, the change is also reflected to the data in my view model, thanks to the two-way data binding.
However, if I make some changes in the data from the view model (using javascript), the data is not updated in the custom-element. I have replicated this problem for a simpler setting.
Simple View Model
export class Playground {
public testObj: any;
counter = 0;
constructor() {
this.testObj = {
prop1: "This is prop1 value"
, collection2: [{ id: "item21" }]
}
}
updateProp1() {
alert("before update: "+this.testObj.prop1);
this.testObj.prop1 = "Sayan " + this.counter++;
alert(this.testObj.prop1);
}
verifyChange() {
alert(this.testObj.prop1);
}
}
Simple View
<template>
<h1>
Playground
</h1>
<div >
<div repeat.for="item of testObj.collection2">
<div class="row">
<div class="col-sm-4">
<input type="text" class="form-control" placeholder="prop1"
value.bind="$parent.testObj['prop1']">
</div>
</div>
</div>
<button click.delegate="updateProp1()" class="btn btn-primary"> Update Prop1 </button>
<button click.delegate="verifyChange()" class="btn btn-primary"> Verify Change </button>
</div>
</template>
Now whenever I click Verify Change after changing the value in textbox, the changed value comes in the alert. But, if I click the Update Prop1 button, the prop1 value gets updated, but the change doesn't reflect in the view.
I am not sure exactly what I am missing.
Note: Instead of using $parent.testObj['prop1'], if $parent.testObj.prop1 is used, the databinding works as it should. However, my actual custom-element is of generic kind and the property name is not known before hand, hence it seems that I need to keep using $parent.testObj['prop1'] notation instead of dot notation: $parent.testObj.prop1.
At pointer/suggestion/feedback is appreciated.
Update: If anyone can point out the the difference between the dot notation and indexer notation w.r.t. aurelia data-binding (I have checked this already), that will be of great help.

This was an issue in earlier builds of the aurelia/binding module. Here's the related issue(s):
https://github.com/aurelia/binding/issues/75
https://github.com/aurelia/binding/pull/76
I tried your view/view-model in the latest version of aurelia and everything worked. Here's a screencast of what I saw: http://screencast.com/t/KqcuqXWjkU2
Make sure you have the latest version of the aurelia components installed- update steps here: http://blog.durandal.io/2015/05/01/aurelia-may-status-and-releases/

Related

Angular4 ngModel changes type of data from 'number' to 'string'

I have an Angular4 app that captures data from a form and stores in DynamoDB. It uses ngModel to support two-way data binding and on the display all looks good. Issue comes into play because of an input field typed as 'text' bound to Typescript 'number' field. Seems to be changing the type of the object value to 'string'. I would simply change the HTML Input type to 'number' except for the unnecessary and undesirable increment/decrement decorators on the form field (and hiding them seems to have limited support). So I was curious if there is another way to keep the data structure typed as desired... if this is a bug in ngModel... or if the input type just simply needs to be 'number'.
The structure in my sample.component.ts file looks like this:
export class Course {
Id: number;
Name: string;
}
...
courseInstance: Course;
saveCourse() {
JSON.stringify(this.courseInstance);
}
My sample.component.html file looks like this:
<div>
<label for="courseid">Course ID: </label>
<input type="text" class="form-control"[(ngModel)]="courseInstance.Id" name="courseid">
</div>
<div>
<label for="courseName">Course Name: </label>
<input type="text" class="form-control"[(ngModel)]="courseInstance.Name" name="courseName">
</div>
<div>
<button type="button" class="btn btn-info btn-lg" (click)="saveCourse()">Save</button>
</div>
Output of JSON.stringify(this.courseInstance) results in something like
{"Id":"100","Name":"Test course"}
Notice the value 100 represented as a string.
If I don't use the form, but simply create an instance such as
courseInstance: Course = {
Id: 100,
Name: 'Test course'
};
Then when outputing result of JSON.stringify(courseInstance); I get
{"Id":100,"Name":"Test course"}
If I try to use PutItem to store object in DynamoDB, the Id value fails type check when data comes from HTML form.
I would have thought that the typing in Typescript would have held precedence over the HTML 'text' input type.
Change the type text to the number to make the type number and don't let to write letters.
<input type="number" class="form-control"[(ngModel)]="courseInstance.Id" name="courseid">
The [(ngModel)] syntax can only set a data-bound property. If you need to do something more or something different, you can write the expanded form.
<div>
<label for="courseid">Course ID: </label>
<input type="text" #ref class="form-control" [ngModel]="courseInstance.Id" (ngModelChange)="onDataChange(ref.value)"name="courseid">
</div>
The ngModel data property sets the element's value property and the ngModelChange event property listens for changes to the element's value.use parseInt to get the desired result.
ngModelChange will be fired on every keystroke you need to debounce value else event will be emitted for every keystroke and To debounce values you can use a Subject with debounceTime() operator.A subject is both an observable and an observer. This means you can treat it as an observable and pass values to it as well. Apart from this use Template reference variable
In your component
import { Subject } from 'rxjs/Subject';
import {debounceTime } from 'rxjs/operators
debouncer= new Subject();
constructor() {
this.debouncer
.debounceTime(1000)
.subscribe((val) =>{
console.log(val);
this.courseInstance.id=ParseInt(val,10);
});
}
onDataChange(value) {
this.debouncer.next(value);
}
Angular currently doesn't support type checking in the property binding and event binding. As you can see here, there is an open issue in their github:
https://github.com/angular/angular/issues/16952
What I suggest you to do is to change your input to type number and add this css in your component:
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
EDIT: adding an application in the Stackblitz:
https://stackblitz.com/edit/angular4-ngmodel-changes-type-of-data-from-number-to-string

Binding a parameter to an ember component and handle it from the controller

I want to create the following component in ember (templates / components / echo-hlabel-tf.hbs)
<div id="echo-hlabel-tf">
<span id="tf-label">{{label}}</span>
<span id="tf-value">
<input type="text" value={{textValue}}
{{action "validateRegExp" textValue regExp post on="focusOut"}}
{{action "showText" textValue post on="mouseUp"}}
/>
</span>
</div>
Just a textfield and a label placed in the same row . I have created the functions in the controller of the component (components / echo-hlabel-tf.js):
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
validateRegExp(text,regExpStr){
let regExpObj = new RegExp(regExpStr)
if(regExpObj.test(text)){
console.log('entry matches')
}else{
console.log('entry is wrong');
}
},
showText(text){
console.log(text);
}
}
});
I want to use this component from another template (template / programmers.hbs) :
<ul>
{{people-list title="List of programmers" people=model}}
</ul>
<div>
{{echo-hlabel-tf
label="Textfield horizontal"
textValue="..."
regExp="^([a-z0-9]{5})$"
}}
</div>
The thing is that , even the actions are fired as the events trigger, the variable that represents the value of the input (textValue) always stands the same ("...") . seems that the binding between the outer template, and the components is created at the beginning, but if I put more letters in the textfield , the value of textValue remains the same and doesn't change . I would like to, as I'm adding letters to the textBox, print them with the console with the method showText, but I always print "..."
You are using pure html5 input that does not do two way binding by itself. That is even if you type something, it is not reflected to textValue attribute. You can use ember's builtin input component as in this twiddle. I believe this will solve your problem.

Interact with object passed to Angular2 component as Input

(to save some reading -- the problem boiled down to a missing 'this' when referencing the #Input'ed variables)
I have a parent class - this will (eventually) display a list of "QuestionComponents".
The relevant part of the parent class template is as follows:
<question [(justText)]="testText" [(justObj)]="testObj" [(myQuestion)]="singleQuestion"></question>
Suffice it to say I'm getting the "singleQuestion" object from a http service call. the Question class, of which "singleQuestion" is an instance, has a basic "toString" method for easier output debugging.
testText, textObj, and singleQuestion are all just objects of increasing complexity for me as I tested. For now it's very basic and only has one "question" as I work on my understanding.
The child "QuestionComponent" looks like this:
#Component ({
selector:
'question',
templateUrl:
'./app/components/question/question.component.html',
directives: [FORM_DIRECTIVES],
})
export class QuestionComponent {
#Input() myQuestion:USGSQuestion
#Input() justText:string
#Input() justObj:object
constructor() {
}
onClick() {
myQuestion.generalInfo.label = "changed";
console.log("change attempted");
}
}
Note that as time moves on, I'll need to be able to do some heavy interaction with the objects that are passed into the QuestionComponent. In fact, my preference was to just pass the Question object into the constructor, but passing data into the component constructor doesn't seem to work for some reason. (I digress) To attempt to check how I can interact with passed data , I built the onClick button and tried to change something in one of the #Input'ed variables.
The template for the child object is as so:
<div *ngIf="!justText">
no text passed
</div>
<div *ngIf="justText">
<b>Here is the passed Text:</b><br>
{{justText}} <br>
</div>
<br>
<div *ngIf="!justObj">
no obj passed
</div>
<div *ngIf="justObj">
<b>Here is the passed JSON:</b><br>
Foo: {{justObj.foo}} <br>
Baz: {{justObj.baz}}
</div>
<br>
<div class="question">
<i>I am a question spot</i>
<div *ngIf="!myQuestion">
Loading Question...
</div>
<div *ngIf="myQuestion">
<b>Here is the passed question:</b><br>
{{myQuestion}} <br>
</div>
</div>
<button (click)="onClick()">Clickit</button>
How do I get a reference to the #Input'ed objects inside the class? (in this case, how would I modify "myQuestion" in the 'onClick()' function? ... and, secondarily, how would I ensure changes to the objects got passed to the view and updated?
I should note I already tried to pass the value from the 'view' back through the onClick call, like so:
(change button to:)
<button (click)="onClick(myQuestion)">Clickit</button>
(and change onClick to:)
onClick(q) { q.generalInfo.label = "changed"; }
This did not work.
I'm an idiot.
After a few hours of research and searching (Before asking) it all came down to adding "this".
... as in "myQuestion.generalInfo.label = "changed";" should have been "this.myQuestion.generalInfo.label = "changed";"
Some days you gotta love programming, eh?

ng-click doesn't take parameters from the DOM

I have the following code:
<input id="id">
<button data-action="bea" ng-click="Create($('#id1')[0].value);" class="btn">Insert ID</button>
<button data-action="bea" ng-click="Create($('#id2')[0].value);" class="btn">Insert ID</button>
In the JS I have:
$scope.Create = function (id){
if (id === undefined) {
$scope.data = "You must specify an id";
} else {
$scope.data = data;
console.log(data);
});
}
};
When the call gets into the Create function the value of the id is undefined.
If I add the following line at the beginging of the Create function everything works ok:
id = $('#id')[0].value;
If I send a constant value it works:
<button data-action="bea" ng-click="Create('SomeID');" class="btn">Insert ID</button>
Why is this happening and how can I do that without putting the line of value into the method?
Thanks
This is just an extension of comments and other answers, You could achieve this in many ways using angular, one simple example could be:-
<!-- Add a controller -->
<div ng-controller="MainCtrl">
<!-- Give a model binding to your text input -->
<input ng-model="userEntry" type="text"/>
<!-- ng-click pass which ever argument you need to pass, provided it is an expression that can be evaluated against the scope or any constants -->
<button data-action="bea" ng-click="Create(userEntry);" class="btn">Insert ID</button>
<!-- Some simple data binding using interpolation -->
{{data}}
<!-- Just for demo on repeater on a list of items on the scope -->
<div ng-repeat="item in items track by $index">{{item}}</div>
</div>
Example Demo
My 2 cents on the lines of what were originally trying to do:-
Use angular bindings instead of accessing DOM directly for getting the data, it really helps you deal with just the data without worrying about how to access or render it in DOM. If you think you need to access DOM for implementing business logic re-think on the design, if you really need to do it, do it in a directive. Angular is very opinionated on the design and when where you do DOM access.
ng-model
ng-binding
controller
all about ngmodel controller
This is not the way you should do in AngularJS. You should really think in Angular if you want to use AngularJS. Refer this post ("Thinking in AngularJS" if I have a jQuery background?)
All DOM manipulation should be done in Directive. Refer this page that I found really clear.
(http://ng-learn.org/2014/01/Dom-Manipulations/)
My guess is that $ is not bound to the jQuery function when the ng-click value is evaluated, because it is not exposed in the Angular scope.
Solutions to adress this:
expose the jQuery function in scope somewhere, e.g $scope.$ = $; in a controller.
make the Create function parameterless as you suggested, with a var id = $('#id')[0].value; at the beginning
my favorite : avoid using jQuery. If you put some data in the #id element, there's probably a more natural and AngularJS-idiomatic way of retrieving it than querying the DOM (e.g an Angular service).
In particular, if the element you're targeting is an <input> element, then use the ngModel directive to link the value to a $scopeproperty that will be accessible in the controller :
<input ng-model="inputData"/>
The JavaScript you are trying to pass as a parameter of the create function is not available in the scope of the Create function.
Try to target the element a different way.
Does that help?

knockout js: parameters in templated bindings

Im trying to template some components often used in my project. To omit the introduced redundancy in html content. However i didnt figure it out if it is even possible to do so.
I have a template like:
<script type="text/x-jquery-tmpl" id="somefieldtemplate" >
<input name=" Prefix" type="text" data-bind="value: ${ $item.fieldName}SomeField" />
..... mor contents ...
</script>
The parameter bound to the input component shall be adjustable as someone may see via template options so i have an entry like
<div data-bind="template: { name: 'somefieldtemplate',
templateOptions:
{ fieldName:'somefield', displayName:'somefieldlabel' } }">
The error message in my console log is:
SyntaxError: Unexpected token {
I narrowed the problem down to the fact that if i remove $item.fieldName with its value it works.
Has anybody an enlightening solution to this problem maybe ?
Edit:
As information im currently using knockout.js in version: knockout-latest
I had the same problem and after struggling a lot, I solved it by moving to knockout 1.3 beta (now it's in RC) which doesn't use external templating engines. It doesn't support templateOptions either but that's not a problem. I just constructed a custom data for the template which contains the main data as a property plus other properties that contain the stuff I would have passed in templateOptions in 1.2. I passed it in the data parameter, and everything worked fine.
Try
data-bind="value: ${fieldName} + 'SomeField'"
Sadly i need to answer it myself. But i managed to get it working. Thanks to the tip of RP Niemeyer.
For everyone interested in this:
Observables are generated dynamically in the model someModel with a register component method. This observables have always names like <field>SomeField
Template:
<script type="text/x-jquery-tmpl" id="somefieldtemplate" >
<input name="${$item.fieldName}Prefix" type="text" data-bind="value: someModel[$item.fieldName + 'SomeField']" />
..... more contents ...
</script>
Template-Binding:
<div data-bind="template: { name: 'somefieldtemplate',
templateOptions:
{ fieldName:'somefield', displayName:'somefieldlabel' } }">

Categories

Resources