Knockout JS Using IF statement and With Statement listen for click event - if-statement

The input field should be empty on page load.
When the user clicks the 'Edit Post' then I call KO click and 'select' function (all working) .. when I do this call the row selected is bound correctly.
Current code automatically binds on page load so the first record is in the input field.
<div data-bind="with: Selected">
<input type="text" data-bind="value: Name" />
</div>
<i title="Edit Post" data-bind="click: $parent.select"></i>
Example hack
<div data-bind="if **click: $parent.select then** with: Selected">
<input type="text" data-bind="value: Name" />
</div>
<i title="Edit Post" data-bind="click: $parent.select"></i>
How do I write a data-bind if 'click' then do 'with: Select' ?
Update
Add example code: http://jsfiddle.net/uC8Vt/70/

Generally you would just want this to work off of the Selected observable. If it is not populated, then it won't render the area. If it is pooulated, then whatever object that Selected holds will be used.
So, when you call $parent.select you would want to populate Selected with your object.
In fact, observables are functions, so unless you need to run other logic, you can even take a shortcut and bind your click directly against the Selected observable. The current data is passed as the first argument, which sets the value of the observable.

You would change the Selected property depending on the item clicked...
So an example viewModel might be like...
var items = [{ Name: 'item1' }, { Name: 'item2' }];
var viewModel = {
items: items,
Selected: ko.observable(items[0])
}
viewModel.select = function(selectedItem) {
// The first arg is the context of the item clicked
// Selected in an observable
viewModel.Selected(selectedItem);
};
Then, as Selected changes... your Name binding will automatically update.

Related

How to clear the typeahead input after a result is selected?

I'm using the ng-bootstrap typeahead component to search a customer database. When the user selects a customer from the typeahead results list, I navigate to a customer details page. I've got this working, but I want to clear the input field after navigation has taken place. I've tried setting the model to null or an empty string in the selectItem event logic, but this isn't working:
customer-search-typeahead.component.html
<template #resultTemplate let-r="result" let-t="term">
<div>
<div>
{{r.resource.name[0].given}} {{r.resource.name[0].family}}
</div>
<div>
{{r.resource.birthDate | date: 'dd/MM/yyyy'}}
</div>
</div>
</template>
<input type="text" class="form-control" [resultTemplate]="resultTemplate" (selectItem)="onSelect($event)"
[(ngModel)]="model" placeholder="Start typing a customer name..." [ngbTypeahead]="search"/>
customer-search-typeahead.component.ts
#Component({
selector: 'customer-search-typeahead',
template: require('./customer-search-typeahead.component.html'),
styles: [`.form-control { width: 300px; }`]
})
export class CustomerSearchTypeaheadComponent {
model: any;
searching: boolean;
constructor(private customerService: CustomerService, private router: Router) {}
onSelect($event) {
this.router.navigate(['/customers', $event.item.resource.id]);
this.model = null;
};
search = (text$: Observable<string>) =>
//omitted for brevity
}
The typeahead input looks like this after a selection has been made:
Solution
customer-search-typeahead.component.html
<input type="text" class="form-control" #input [ngbTypeahead]="search" (selectItem)="onSelect($event); input.value='' ">
customer-search-typeahead.component.ts
onSelect($event, input) {
$event.preventDefault();
this.router.navigate(['/customers', $event.item.resource.id]);
};
The issue you witnessing arises from the fact that the NgModel directive is updating model binding asynchronously and the actual model is updated after the onSelect method gets executed. So your model update gets overridden by the NgModel functionality.
Fortunately we (ng-bootstrap authors) got all the flex points in place to cover your use-case :-) There are a couple of things that you could do.
Firstly the $event object passed to the onSelect method has the preventDefault() method and you can call it to veto item selection (and as a result writing back to the model and input field update).
$event.preventDefault() will make sure that the model is not updated and the input field is not updated with the selected item. But text entered by a user will still be part of the input so if you want to clear up this as well you can directly update the input's value property.
Here is code demonstrating all those techniques together:
onSelect($event, input) {
$event.preventDefault();
this.selected.push($event.item);
input.value = '';
}
where input argument is a reference to the input DOM element:
<input type="text" class="form-control" #input
[ngbTypeahead]="search" (selectItem)="onSelect($event, input)">
Finally here is a plunker showing all this in practice: http://plnkr.co/edit/kD5AmZyYEhJO0QQISgbM?p=preview
The above one is template ref value solution.
This is for ngModel solution.
Html code:
<input type="text" class="form-control" [resultTemplate]="resultTemplate" (selectItem)="onSelect($event)"
[(ngModel)]="model" placeholder="Start typing a customer name..." [ngbTypeahead]="search"/>
Component code:
onSelect($event) {
$event.preventDefault();
this.model = null;
this.router.navigate(['/customers', $event.item.resource.id]);
};
$event.preventDefault();
for ngModel value change empty

Ember.Select cannot observe change to selection variable of dynamically generated control

I am dynamically building a number of select controls. I want to know when an option has changed so I am doing the following:
The idea there is that I dynamically create properties on the selections object which is used to hold the selected values.
//Add observer
Ember.defineProperty(selections, camelizedModelClass, null);
selections.addObserver(camelizedModelClass, self, 'selectionChanged');
Here is the function that should be called when selection has changed.
selectionChanged : function(sender, key, value, rev) {
console.log('worked!');
},
In my template I create Ember.Select's as follows.
{{#each control in controls}}
<div {{bind-attr class=":form-group control.width"}}>
<label for="exampleInputPassword1">{{control.label}}</label>
{{view "select"
content=control.content
selection=control.selection
optionValuePath=control.optionValuePath
optionLabelPath=control.optionLabelPath
class = "form-control"
prompt=control.prompt
}}
<p class="help-block"></p>
</div>
{{/each}}
Each control in the controls array has the following property.
selection: 'selections.' + camelizedModelClass,
The observer method is never called, however, if I manually enter the selection as follows it is in fact called.
{{view "select"
content=control.content
selection=selections.manager <-------- manually specifying the selection
optionValuePath=control.optionValuePath
optionLabelPath=control.optionLabelPath
class = "form-control"
prompt=control.prompt
}}
Why is this not working? The other weird thing is that if I put {{control.selection}} in my handlebars template I can see the model changing for the first method but not the second.
Try changing the Ember.SelectView selection property to value.
{{view 'select' value=selections.manager}}
Here is an example.
http://emberjs.jsbin.com/boluba/1/edit

Inputs on Ember refresh page when a user hits Enter key

I'm using Ember CLI and have noticed odd behaviour. When the user clicks into the input and presses the enter key, the page refreshes.
My page has a basic element like this that is NOT part of any form:
<input type="text" class="form-control" id="per_page" value="50">
I am currently serving the page via:
ember cli
So node is hosting and has the fancy live reload thing going on so that when I update a page that is part of the underlying app.
So what is causing a page reload the enter key pressed inside an input? Could it be node or live reload? Are inputs just supposed to refresh a page when a user presses the enter key and I missed that in my HTML for dummies book?
**Better still, how can I intercept and instead call a function via:
{{action** "myFunction"}}
That happens because when you hit Enter, form gets submitted which results in page reload. what you need to do is set onsubmit="return false" on the form so nothing happens during submit. you can bind input to execute some action by adding action attribute action="doSomething"
<form onsubmit="return false">
{{input type="text" action="createComment" value=topic id="inputTopic"}}
</form>
Edit: In Ember 3+ you now use the {{on}} modifier to setup events on elements.
<form {{on 'submit' this.submitForm}}>
<!-- the rest of your form here -->
<button type='submit'>Submit</button>
</form>
And the action defined like so
#action
submitForm(event) {
event.preventDefault();
// Your code
}
Historically Ember has handled this use case with the following code:
<form {{action 'submitForm' on='submit'}}>
<!-- the rest of your form here -->
<button type='submit'>Submit</button>
</form>
This prevents the form from refreshing the page.
There is another method that gives you more control, by giving you the event so you can manage that yourself:
<form onsubmit={{action 'submitForm'}}>
<!-- the rest of your form here -->
<button type='submit'>Submit</button>
</form>
In this case, you will get an event and will have to call event.preventDefault() to stop the page refresh.
actions: {
submitForm(event) {
event.preventDefault();
}
}
This is a running example of the two: https://ember-twiddle.com/827820958e054f7af57b7677630729fc?openFiles=controllers.application.js%2C
I had the same problem - what worked for me, was to overwrite the keyPress Event in the input component like this:
keyPress: function (e) {
var keyCodeEnter = 13;
if (e.keyCode === keyCodeEnter) {
return false;
}
}
Hope it will help someone in the future! :)

How to Defer Ember views from synchronously updating from bound input fields

Let's say I have a group of editable users, when one goes to edit that user, ember will synchronously update all views as you type into the bound input text field.
This is cool and all but from an UX point of view it can be misleading.. those values hadn't changed on the server at all.. What I'd like to do is to defer the view update until I set it in the corresponding action method based on a success message from the server.
I have found that when I use {{bind-attr value=firstName}} instead of {{input value=firstName}} that indeed ember no longer updates the view on changing the input field, however the newly typed value is no longer accessible in the actions submit method via this.get('firstName')?
Example.hbs
<script type="text/x-handlebars" id="user">
<h3>Edit {{fullName}}</h3>
<p>
<label>First Name</label>
{{input value=firstName}}
</p>
<p>
<label>Last Name</label>
<input {{bind-attr value=lastName}} />
</p>
<p>
<button {{action 'submit'}}>Submit</button>
</p>
</script>
Example Controller
App.UserController = Ember.ObjectController.extend({
actions: {
submit: function(){
// call to server, on confirmation set 'Globally' first and last names
console.log(this.get('firstName') + " - " + this.get('lastName'));
}
}
});
Here's my jsbin:
http://jsbin.com/jipik/2/edit?html,js,console,output
All you need is a secondary set of variables. Add to your controller a variable named firstNameEdited and set its value initially to firstName. Value bind your input field to this new value and submit this new value to your api call. On a successful return of the API call, update firstName with the value of firstNameEdited.

How do I create a reusable partial for duplicate markup in ember.js?

Given this chunk of HTML:
<div id="email_field" class="control-group">
<label class="control-label" for="account.email">Email</label>
<div id="email_input" class="controls">
<input id="account.email" name="account.email" type="text" placeholder="jpublic#example.com">
<span class="help-block">We just need a valid email address.</span>
</div>
</div>
How do I turn this into a re-usable partial for whatever attribute I want? IE: email, password, password confirmation, etc.
I would assume some sort of view hierarchy but I'm not quite sure.
EDIT: After further exploration I've knocked out {{view}} and {{render}} and figured out exactly what I need:
I want to:
1. Use a specific view (InputView)
2. Use a specific controller (Preferably similarly named: InputController) ({{view}} doesn't do this I think)
3. Be able to use this multiple times ({{render}} can't do this)
4. Be able to pass in values ({{render}} can't do this)
Example:
<!-- templates/application.hbs -->
{{foo "input" name="Email" id="account.email" placeholder="jpublic#email.com"}}
// controllers/input.js
Application.InputController = Ember.ObjectController.extend({
type: "text"
});
// views/input.js
Application.InputView = Ember.View.extend({
templateName: "form/input"
});
<!-- templates/form/input.hbs -->
<input {{bindAttr id="id" name="name" type="type" placeholder="placeholder"}}>
I would create a view that takes all the parameters that are variable. Such as:
{{view App.FormEntity
name="email"
placeholder="My placeholder..."
help="We just need a valid email address."
valueBinding="value"
}}
From there you could extract the label, the various class names, and then use Ember.TextField to bind the value to.
Once you have all of those arguments passed into the view, it should be nice and easy to create the markup using a mixture of bindAttrs, a couple of computed properties, and Ember helpers (such as the Ember.TextField).
I am new to Emberjs and looking for pretty much the same thing, but couldn't you also simply use http://emberjs.com/guides/templates/writing-helpers/ for that?
I will try it myself, so can give more updates if that works out.
Update:
Ok, I got it to work. I created a new Helpers folder with FormgeneratorHelper.js and the following code:
Ember.Handlebars.registerBoundHelper('control-group', function (options) {
var name = options.hash.test.capitalize();
console.log(name);
return new Handlebars.SafeString('<div class="control-group"> \
<label class="control-label" for="input' + name + '">' + name + '</label> \
<div class="controls"> \
<input type="text" id="input' + name + '" placeholder="' + name + '" /> \
</div> \
</div>');
});
An then, no matter in which template you can do:
{{control-group test="email"}}
I really like the idea of using helpers, but if you are using plain Javascript (as opposed to CoffeScript) and have more than one line of code, then it gets a bit ugly unfortunately. But will probably still use that method.
How do I turn this into a re-usable partial for whatever attribute I want? IE: email, password, password confirmation, etc.
What you want is the experimental {{control}} helper. The control helper is currently under development and is considered experimental. To enable it, set ENV.EXPERIMENTAL_CONTROL_HELPER = true before requiring Ember.
I want to:
1. Use a specific view (InputView)
2. Use a specific controller (Preferably similarly named: InputController) ({{view}} doesn't do this I think)
Out-of-box the control helper expects to be passed a template name. That template name is used to lookup a matching view and controller. So for example:
App.InputView = Ember.View.extend()
App.InputController = Ember.Controller.extend()
{{control input}}
See:
A control renders a template with a new instance of the named controller and view
A control's controller and view are lookuped up via template name
Be able to use this multiple times ({{render}} can't do this)
A control can be used multiple times
Be able to pass in values ({{render}} can't do this)
Like the {{view}} helper, {{control}} will accept arbitrary name/value pairs. So as in your example, one could manually pass options to the control helper. Like the {{view}} helper these options become properties on the view instance:
<!-- templates/form/input.hbs -->
<label class="control-label" {{bindAttr for="view.inputId"}}>
{{view.label}}
</label>
<div class="controls">
<input {{bindAttr id="view.inputId" name="view.name" type="type" placeholder="view.placeholder"}}>
<span class="help-block">{{view.help}}</span>
</div>
// controllers/form_input.js
App.FormInputController = Ember.ObjectController.extend({
type: "text"
});
// views/form_input.js
App.FormInputView = Ember.View.extend({
classNames: ["control-group"]
});
<!-- templates/application.hbs -->
{{control "form/input"
inputId="account.email"
name="email"
label="Email"
placeholder="jpublic#email.com"
help="We just need a valid email address."
}}
See this jsbin for working example
Also keep in mind that A control can specify a model to use in its template - with this in place we can bind properties to model data. Also if a controller's model changes, its child controllers are destroyed so the control will reset as expected if the model is swapped out.