I have an array in my View Model. Items of this array are objects of Person that has two properties. when I bind this to a template it's okay. but when I change the state of one of the properties it does not reflect in UI.
what did I do wrong ?
<script type="text/html" id="person-template">
<p>Name: <span data-bind="text: name"></span></p>
<p>
Is On Facebook ?
<input type="checkbox" data-bind="checked: IsOnFacebook" />
</p>
</script>
<script type="text/javascript">
var ppl = [
{ name: 'Pouyan', IsOnFacebook: ko.observable(true) },
{ name: 'Reza', IsOnFacebook: ko.observable(false) }
];
function MyViewModel() {
this.people = ko.observableArray(ppl),
this.toggle = function () {
for (var i = 0; i < ppl.length; i++) {
ppl[i].IsOnFacebook = false;
}
}
}
ko.applyBindings(new MyViewModel());
</script>
when I press the button I want to make changes in People.IsOnFacebook property. the changes will be made successfully but the UI does not show.
You should call it like a function. Like:
ppl[i].IsOnFacebook(false);
This because the ko.observable() returns a function. It's not a property you call anymore but a function call. So in the background they will update your UI. To retreive a property that is observable. You should also use the function call.
Please see this tutorial: http://learn.knockoutjs.com/#/?tutorial=intro
Related
start html ----------------------
div id="pageWrapper"> //page wrapper
<div id="page-image"><img src="./images/lightHouseB.png"></div>
<div id="man-image"><img src="./images/sailor.png"></div>
<section>
<header>There Are Things in the Dark, can you Find them? </header>
<!-- basic html title page -->
<div id="textBox">
<a id="mousee" href="#">Hidden Ships</div></a>
</div>
</section>
html end point -----------------------------------
window.onload = eventMonitor();
function eventMonitor(){
document.getElementById('manimage').addEventListener('onmouseover', popMap(), false);
document.getElementById('mousee').addEventListener('click', shipsSlider(), false);
function popMap(url='shipsSlide.html',windowName, w, h) {
var left = (screen.width/2)-(w/2);
var top = (screen.height/2)-(h/2);
return window.open(url=" ", "Ship Pictures", toolbar='no', directories="no", status='no');
}
keep getting a null value - can not read property of eventlistener of null.
You need to reference the function to window.onload, and it will be called once the window loads.
See the difference here.
window.onload = onload;
function onload(){
console.log('DOM loaded');
}
And here I am referencing whatever onload will return, in this case a function.
window.onload = onload(); // This will return the anonymous function of onload
// __________________|^^|
function onload(){
return function(){
console.log('DOM loaded');
}
}
So what you want to do is to remove () so your code becomes:
window.onload = eventMonitor; // eventMonitor will be run once windows loads.
I'm using AureliaJS to build a dynamic forms scenario, where I have a parent form with the gross operations needed and multiple child's form's, that change based on user input.
These child's form's have only two specific things themselves. Their model and the validation rules for their model.
So my question is, how can the parent form call the validation rules from the current child form? From child I know that is possible call parent's view model. But from parent, how can I invoke any function from the child?
The scenario is similar off having one base class, that has one method and this method could be overriding on the child classes.
Any suggestion? I'm glad to change the approach if needed.
Here's an example: https://gist.run?id=1865041a15af60600cb7b538018bdccd
app.html
<template>
<span>This is an APP</span>
</p>
<compose view-model.bind="'parentForm'"></compose>
</template>
app.js
import { autoinject } from 'aurelia-framework';
#autoinject
export class App {
}
childForm1.html
<template>
<label> Price : </label>
<input value.bind="model.data.price">
<p/>
<label> VAT : </label>
<input value.bind="model.data.vat">
<p/>
</template>
childForm1.js
import { autoinject } from 'aurelia-framework';
#autoinject
export class ChildForm1 {
activate(model)
{
this.model = model;
}
validateRules (){
if(this.model.data.price != '' && this.model.data.vat == '' )
this.model.validateMessage = 'VAT is mandatory';
}
}
childForm2.html
<template>
<label>Address : </label>
<input value.bind="model.data.address">
<p/>
<label>Phone : </label>
<input value.bind="model.data.phone">
<p/>
</template>
childForm2.js
import { autoinject } from 'aurelia-framework';
#autoinject
export class ChildForm2 {
activate(model)
{
this.model = model;
}
validateRules (){
if(this.model.data.phone != '' && this.model.data.address == '' )
this.model.validateMessage = 'Address is mandatory';
}
}
index.html
<!doctype html>
<html>
<head>
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body aurelia-app>
<h1>Loading...</h1>
<script src="https://jdanyow.github.io/rjs-bundle/node_modules/requirejs/require.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/config.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/aurelia.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/babel.js"></script>
<script>
require(['aurelia-bootstrapper']);
</script>
</body>
</html>
parentForm.html
<template>
<button click.delegate="changeChildForm1()">Change Child Form 1</button>
<button click.delegate="changeChildForm2()">Change Child Form 2</button>
<p/>
<p/>
<form>
<label>User : </label>
<input value.bind="model.data.user">
<p/>
<compose view-model.bind="childFormVM" model.bind="model"></compose>
<button click.delegate="save()">Save</button>
<p/>
<span> Validation Message : ${model.validateMessage}</span>
</form>
<p/>
<span>Price : ${model.data.price}</span><p/>
<span>Vat : ${model.data.vat}</span><p/>
<span>Phone : ${model.data.phone}</span><p/>
<span>Address : ${model.data.address}</span><p/>
</template>
parentForm.js
import { autoinject } from 'aurelia-framework';
#autoinject
export class ParentForm {
model = {
validateMessage : '',
data : {
user : 'My Name'
}
};
childFormVM = 'childForm1';
validateMessage = '';
changeChildForm1() {
this.childFormVM = 'childForm1';
}
changeChildForm2() {
this.childFormVM = 'childForm2';
}
save(){
this.validateRules();
// How to call the validation rules from child ?
}
validateRules (){
this.model.validateMessage = 'Validate by parent';
}
}
Bind a function call to the child so that you have a handle to invoke it from the parent. I usually prefer to directly bind the child components rather than using compose, but you can make it work with compose by passing a complex model object rather than only the model, and passing the binding function as one of the model properties.
Parent View-Model:
class Parent {
model = {};
child1Validate = null;
changeChildForm1() {
if (typeof this.child1Validate === 'function') {
// the binding was successful; proceed with function call
let result = this.child1Validate();
console.log(result);
}
}
}
Parent View:
<my-child1 model="parentModel" go-validate="child1Validate"></my-child1>
Child View-Model:
class MyChild1 {
#bindable model;
#bindable goValidate;
bind() {
// bind the child function to the parent that instantiates the child
this.goValidate = this.runValidation.bind(this);
}
runValidation() {
// do the validation and pass result to parent...
return 'Success!';
}
}
That's how you can do it:
parent-form.html
<compose view-model.bind="childFormVM" view-model.ref="childFormInstance" model.bind="model"></compose>
parent-form.js
save() {
this.childFormInstance.currentViewModel.validateRules();
}
Helpful Notes
Only use <compose> when necessary. For example, in the app.html you should replace <compose> for:
<require from="parentForm"></require>
<parent-form></parent-form>
Use kebab-case instead of camel-case to name your files. For example, instead of parentForm.html and parentForm.js, use parent-form.html and parent-form.js. This won't change a thing in your code but you will be following nice javascript standards :)
When binding directly to a string you don't need to use .bind. For example, view-model.bind="'parentForm'" could be replaced for view-model="./parentForm"
Hope this helps!
One thing that immediately comes to mind is that you can inject the parent model into your child model in the constructor -- the injected instance will be the same, not a newly created one. This way, your parent can define a method that allows the child to register itself on the parent, and the parent can then invoke whatever methods exist on the child at the time of its choosing.
This creates a rather strong coupling between the components, though, so you will need to consider whether or not that is acceptable to you.
If it isn't, another way to approach the issue is to use the event aggregator. The parent form can dispatch an event on the aggregator, and the children will be subscribers listening for the event. In this case, depending on whether or not you host multiple such combinations on one page, you may want to include a unique identifier for the form that is sent along with the event and bind that ID to the child components, so they will know to only listen for events from their parent.
When I trigger the onClick even the event keeps triggering for about 1000+ times. I can't seem to figure where this is coming from. I have changed the onClick to an onMouseover to see if it keeps triggering but then the event only triggers once.
I'm using : react 0.13.3
Any idea's?
var React = require('react');
var AppActions = require('../../actions/app-actions.js');
var FileAmount = React.createClass({
getInitialState: function() {
return {
amount : this.props.amount,
config : this.props.config
};
},
handleClick: function(e){
var name = e.target.name;
if(name === 'decrease'){
if(this.state.amount > 1){
this.setState({
amount : (this.state.amount - 1)
});
AppActions.updateAmount(this.props.index, (this.state.amount - 1))
}
}else{
this.setState({
amount : (this.state.amount + 1)
});
AppActions.updateAmount(this.props.index, (this.state.amount + 1))
}
},
handleChange: function(e){
var amount = e.target.value;
this.setState({
amount : amount
});
AppActions.updateAmount(this.props.index, amount)
},
render: function() {
var config = this.state.config
return (
<div className="file-amount">
<span className="file-amount-text"> {config.filelist_quantity}: {this.state.amount} {config.filelist_pieces}</span>
<div className="file-amount-fields">
<i className="file-amount-decrease icon" name="decrease" onClick={this.handleClick} />
<input className="file-amount-input" type="number" value={this.state.amount} onChange={this.handleChange} />
<i className="file-amount-increase icon" name="increase" onClick={this.handleClick} />
</div>
</div>
);
}
});
module.exports = FileAmount;
I left a comment on the original post, but on second inspection, it looks quite possible that you pass down props.amount from a Flux Store. If that is the case you're creating an infinite loop.
handleClick increments state.amount, then after the AppAction is called, the Store updates the component with props.amount, then the onChange fires because it is tied to state.amount and then onChange changes state.amount and changes props.amount when it calls AppActions.updateAmount.
Every time props or state are updated, React will call the render() method. If there is any way that props or state get updated while the render() executes, then you are likely going to run into an infinite loop.
Perhaps adding a e.preventDefault(); to your handleClick method will stop this loop from being started.
I removed the added javascript for the browser-sync proxy settings. That somehow screwed around with my react.
Taken this form as example http://plnkr.co/edit/fHEBw6dDdG3IVgnmCLb7?p=preview
How can I put the $pristine state of the form to true after the SAVE DRAFT button is pressed?
You can call $setPristine on the form: http://plnkr.co/edit/wXaFXtuhNH6d4SP2uArm?p=preview
<button ng-click="reset(); form.$setPristine()">RESET</button>
<button ng-click="update(user); form.$setPristine()">SAVE</button>
Or you can call the method in your controller (after ensuring that the form exists):
$scope.update = function(user) {
$scope.master= angular.copy(user);
if ($scope.form) $scope.form.$setPristine();
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
if ($scope.form) $scope.form.$setPristine();
};
Demo: http://plnkr.co/edit/Mau7uuDfPlzcn418OdWh?p=preview
I've noticed that the reset() won't clear the email input unless it's valid.
I've tried another approach instead:
<button type="reset" ng-click="form.$setPristine()">RESET</button>
<button ng-click="update(user); form.$setPristine()">SAVE</button>
<FORM>
<DIV class="outer-class">
<INPUT class="toValidate" type = "text"/>
<INPUT class="somethingElse" type= "text"/>
<INPUT class="toValidate" type ="text"/>
</DIV>
<DIV class="outer-class">
<INPUT class="toValidate" type = "text"/>
<INPUT class="somethingElse" type= "text"/>
<INPUT class="toValidate" type ="text"/>
</DIV>
<INPUT type="submit"/>
</FORM>
My question is: How do I ensure that for the form to be valid, the nested toValidates have a unique value but only within the same outer div?
I am guessing this logic should go in an OuterClassDirective, but I can't seem to figure out what the logic should look like?
Any advice would be appreciated.
Thanks!
What about this. Your outerClassDirective should have a controller, which will store used values in an array. It will transclude the input fields in its body. Your toValidate directive requires outerClassDirective and adds the model value to the array, making it invalid if exists.
Here is a try (untested):
app.directive('outerClass', function() {
var values = [];
var valid = true;
return {
template: '<div ng-transclude></div>',
transclude: true,
replace: true,
require: 'ngModel',
controller: function() {
this.addValue: function(value) {
valid = values.indexOf(value) > -1;
values.push(value);
};
},
link: function(scope, elm, attrs, ctrl) {
ctrl.$setValidity('toValidate', valid)
}
};
});
app.directive('toValidate', function() {
return {
require: '^outerClass',
link: function(scope, elm, attrs, ctrl) {
ctrl.addValue(attrs.value);
}
}
};
});
The 'tabs' and 'pane' directives on the Angular home page solve a similar issue -- the child 'pane' directives need to communicate with the parent 'tabs' directive.
Define a controller on the outerclass directive, and then define a method on the controller (use this not $scope). Then require: '^outerclass' in the toValidate directive. In the toValidate link function, you can $watch for value changes and call the method on the outerclass controller to pass the value up. Do the validation in the outerclass directive.
See also 'this' vs $scope in AngularJS controllers.