Bind-attr "for" attribute in Ember.js - javascript

I'm trying to include a unique id for an and tag to get a custom checkbox. The {{input}} tag outputs it correctly but the <label {{bind-attr for=binding}} does not. I'm a frontend guy new to Ember so I'm sure this should be trivial.
<ul class="col-menu dropdown-menu">
{{#each columns}}
<li>
{{input type="checkbox" checked=checked id=binding}}
<label {{bind-attr for=binding}}><span></span></label>
<span>{{heading}}</span>
{{columns}}
</li>
{{/each}}
</ul>
Here is the output ...
<li>
<input id="activityTotal" class="ember-view ember-checkbox" type="checkbox" checked="checked">
<label data-bindattr-2689="2689">
<span></span>
</label>
<span>
<script id="metamorph-53-start" type="text/x-placeholder"></script>
Events
<script id="metamorph-53-end" type="text/x-placeholder"></script>
</span>
<script id="metamorph-54-start" type="text/x-placeholder"></script>
<script id="metamorph-54-end" type="text/x-placeholder"></script>

First of all you should wrap all this in a component.
You really should not be manually binding the id like this as ember uses it internally but I think it is acceptable in this case as I can't think of a better way but you need to maintain the uniqueness.
I would so something like this to ensure the id is unique and uses
checkBoxId: Ember.computed(function(){
return "checker-" + this.elementId;
}),
Here is the component's javascript file that will be executed when the component is ran:
App.XCheckboxComponent = Ember.Component.extend({
setup: Ember.on('init', function() {
this.on('change', this, this._updateElementValue);
}),
checkBoxId: Ember.computed(function(){
return "checker-" + this.elementId;
}),
_updateElementValue: function() {
this.sendAction();
return this.set('checked', this.$('input').prop('checked'));
}
});
Here is the component's template, I use unbound to bind the unique checkBoxId with the label's for:
<label for="{{unbound checkBoxId}}">
{{label}}
<input id="{{unbound checkBoxId}}" type="checkbox" {{bind-attr checked="checked"}}>
</label>
Here is how you might use it:
<ul>
{{#each contact in model}}
<li>{{x-checkbox label=contact.name enabled=contact.enabled}}</li>
{{/each}}
</ul>
And here is a working jsbin for ember 1.7.1 and here is a working jsbin for ember 1.11.1.

As of version 1.11, you don't need bind-attr. You should be able to bind the attribute like this:
<label for={{binding}}></label>

Related

How do I bind inputs to observables in an observableArray?

I have an object (which is observable) and it has keys, each of which is observable. One of those keys is an array that contains other observables.
I want to be able to modify these values using HTML input fields. What came to my mind to do was to simply foreach through the observable array, and do value/textInput bindings to the inputs to modify them.
However, when modifying the text inputs - the value in the original doesn't change! Am I doing something wrong? Why aren't the bound values updating the way I expect?
I've broken down what I'm doing into a more generic version: http://jsfiddle.net/veqr2q6q/
<div class='liveExample'>
<div class="line-container" data-bind="foreach: {data: text, as: 'line'}">
<input type="text" data-bind="textInput: line" /><br />
</div>
<h2>Hello,</h2>
<ul data-bind='foreach: {data: text, as: "line"}'>
<li data-bind="text: line"></li>
</ul>
</div>
// Here's my data model
var ViewModel = function(first, last) {
this.text = ko.observableArray([
ko.observable(first),
ko.observable(last)
])
};
ko.applyBindings(new ViewModel("Planet", "Earth")); // This makes Knockout get to work
I think in this scenario you want to use $rawData. I updated your fiddle here.
http://jsfiddle.net/veqr2q6q/2/
<div class='liveExample'>
<div class="line-container" data-bind="foreach: text">
<input type="text" data-bind="textInput: $rawData" /><br />
</div>
<h2>Hello,</h2>
<ul data-bind='foreach: {data: text, as: "line"}'>
<li data-bind="text: line"></li>
</ul>
</div>
As referenced in the Knockout JS docs
Usually [$rawData] will be the same as $data, but if the view model provided to Knockout is wrapped in an observable, $data will be the unwrapped view model, and $rawData will be the observable itself.

how to append input fields using push() function in angularjs?

I am creating add more field functionality in angularjs. I am using below code
Javascript
<script>
function FruitsController($scope){
var div = 'Apple';
$scope.fruits=[];
$scope.addFruit=function(){
$scope.fruits.push(div);
}
}
<script>
HTML
<ul><li ng-repeat="fruit in fruits">{{fruit}}</li></ul>
<button ng-click="addFruit()">Add</button>
Above code works successfully and appends 'Apple' string in body. But I want to append input type text field like string. So I am using below script that does not works.
<script>
function FruitsController($scope){
var div = '<input type="text">';
$scope.fruits=[];
$scope.addFruit=function(){
$scope.fruits.push(div);
}
}
<script>
I know above my code is totally wrong. Please give me some ideo that how to add input type in angularjs
You can render UI element in html. Your HTML looks like
<ul>
<li ng-repeat="fruit in fruits">
<input ng-model="fruit">
</li>
</ul>
<button ng-click="addFruit()">Add</button>
Your controller looks like
<script>
function FruitsController($scope){
var fruit = 'apple';
$scope.fruits=[];
$scope.addFruit=function(){
$scope.fruits.push(fruit);
}
}
<script>
Try this. For input text boxes you need to use ng-model attribute as it is two way data binding.
<ul>
<li ng-repeat="fruit in fruits">
<input type="text" ng-model="fruit">
</li>
</ul>
<button ng-click="addFruit()">Add</button>
<ul>
<li ng-repeat="fruit in fruits">
<input ng-model="fruit">
</li>
</ul>
<button ng-click="addFruit()">Add</button>
Keep your UI/HTML code in your HTML template, keep pure data structures in your controller. Through Angular's two-way data binding, ng-model will directly update your entries in $scope.fruits when your input changes.

DOM Scripting in Google Polymer

I just worked through the Google Polymer tutorial and I am building my first own element. And I am missing some DOM-Scripting Functions I know from Prototype and jQuery that made my life very easy. But maybe my methods are just not right. This is what I have done so far:
<polymer-element name="search-field">
<template>
<div id="searchField">
<ul id="searchCategories">
<li><a id="search-categories-text" data-target="text" on-click="{{categoryClick}}">Text</a></li>
<li><a id="search-categories-videos" data-target="videos" on-click="{{categoryClick}}">Videos</a></li>
<li><a id="search-categories-audio" data-target="audio" on-click="{{categoryClick}}">Audio</a></li>
</ul>
<div id="searchContainer">
<input id="searchText" type="text" />
<input id="searchVideos" type="text" />
<input id="searchAudio" type="text" />
</div>
</div>
</template>
<script>
Polymer({
ready: function() {
},
categoryClick: function(event, detail, sender) {
console.log(sender.dataset.target);
console.log(this.$.searchField.querySelector('#searchContainer input'));
this.this.$.searchField.querySelector('#searchContainer input');
}
});
</script>
</polymer-element>
What I want to do is to set an active class to the bottom input-fields when one of the above links are clicked. On jQuery I would just observe a link and deactivate all input fields and activate the one input field I want to have. But I am not sure how to do it without jQuery. I could just use all the native javascript functions with loops etc but is there anything polymer can offer to make things easier?
Does this example do what you want?
<script src="//cdnjs.cloudflare.com/ajax/libs/polymer/0.3.3/platform.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/polymer/0.3.3/polymer.js"></script>
<polymer-element name="search-field">
<template>
<style>
.hideMe {
display: none;
}
</style>
<div id="searchField">
<ul id="searchCategories">
<template repeat="{{category in searchCatergories}}">
<li><a on-click="{{categoryClick}}">{{category}}</a></li>
</template>
</ul>
<div id="searchContainer">
<template repeat="{{category in searchCatergories}}">
<div class="{{ { hideMe: category !== selectedCategory} | tokenList }}">
<label>Search for {{category}}</label>
<input id="search{{category}}" type="text">
</div>
</template>
</div>
</div>
</template>
<script>
Polymer({
searchCatergories: [
"Text",
"Video",
"Audio"
],
selectedCategory: 'Text',
categoryClick: function(event, detail, sender) {
// grab the "category" item from scope's model
var category = sender.templateInstance.model.category;
// update the selected category
this.selectedCategory = category;
// category
console.log("category", category);
// you can also access the list of registered element id's via this.$
// try Object.keys(this.$) to see registered element id's
// this will get the currently showing input ctrl
selectedInputCtrl = this.$["search" + category];
}
});
</script>
</polymer-element>
<search-field></search-field>
I've created an array for the categories and added two repeat templates.
I've setup a .hideMe class which is set on all input elements that aren't the currently selected category.
Info on dynamic classes - https://www.polymer-project.org/docs/polymer/expressions.html#tokenlist
Info on repeat - https://www.polymer-project.org/docs/polymer/binding-types.html#iterative-templates
Hope that helps

How to dynamically generate DOM with Angular.js?

I'm getting started with Angular.js and I'm wondering how to do something along the lines of this (pseudocode):
<li ng-repeat="item in items">
<# if(item.dataType == "string") { #>
<input type="text" />
<# } else if(...) { #>
<input type="password" />
<# } #>
</li>
I know the above code is not angularish, and I know that for simple processing I could use a conditional ng-hide or ng-show or something similar. But for complex behavior, if I had to perform various data checks and business logic, how could I dynamically generate DOM elements with Angular.js?
Within the angular world, DOM manipulation is accomplished using angularjs directives. Here is the angular documentation on directives: https://docs.angularjs.org/guide/directive, you would do well to read through this.
Here is some sample code that will accomplish the idea of your psuedo code:
var myApp = angular.module('myApp', []);
myApp.controller('MyController', function ($scope){
$scope.items = [
42, "hello, world!", 3.14, "i'm alive!"
]
});
myApp.directive('myInputDirective', function () {
return {
restrict: 'E',
replace: true,
template: '<div></div>',
link: function (scope, element, attrs) {
if (typeof scope.current === "string") {
element.append('<input type="text">');
} else {
element.append('<input type="password">');
}
}
}
});
and here's how the html would look:
<div ng-controller="MyController">
<ul ng-repeat="item in items" ng-init="current = item">
<my-input-directive></my-input-directive>
</ul>
</div>
Here is a plnkr with the working example: http://plnkr.co/edit/iiS4G2Bsfwjsl6ThNrnS?p=preview
Directives are how the DOM is manipulated in angular. First thing to notice is that angular has a set of directives that come out of the box, we're using a few above (ng-repeat, ng-init, ng-controller). Above we've created a custom directive that will analyze the data type of each item in the items array of our MyController controller, and append the correct html element.
I imagine that you already understand the ng-repeat directive, so I'll skip that. I'll explain what I'm doing with the ng-init directive though. The ng-init directive allows you to evaluate an expression in the current scope. What this means is that we can write an expression that is evaluated in our current controllers scope, in this case the MyController scope. I am using this directive to create an alias for our current item named current. We can use this inside our directive to check what type the current item in the array iteration is.
Our directive myInputDirective, is returning an object with a few different properties. I won't explain them all here (I'll let you read the documentation), but I will explain what the link function is and what I am doing with it. A link function is typically how we modify the DOM. The link function takes in the current scope (in this case the scope of MyController), a jqLite wrapped element that is associated with the directive, and the attrs which is a hash object with key-value pairs of normalized attribute names and values. In our case, the important parameters are the scope, which contains our current variable, and the element, which we will append the correct input onto. In our link function, we're checking the typeof our current item from our items array, then we are appending an element onto our root element based on what the type of the current item is.
For this particular problem, what I'm doing above is overkill. But based off of your question I figured you were looking for a starting point for more advanced uses of angular apart from the built in directives that angular provides. These are somewhat advanced topics in angular, so I hope that what I've said make some sense. Check out the plunker and play around with it a bit, and go through some of the tutorials on https://docs.angularjs.org/guide. Hope this helps!
You can use ng-show to conditionally hide and show elements e.g.:
<input ng-show="item.dataType === 'string'" type="text"/>
<input ng-show="..." type="password"/>
Assuming your object looks like this:
$scope.items = [
{
dataType: 'string',
value: 'André Pena'
},
{
dataType: 'password',
value: '1234'
},
{
dataType: 'check',
value: true
}
];
Option #1 - ng-switch plunker
<body ng-controller="MainCtrl">
<ul>
<li ng-repeat="item in items">
<div ng-switch="item.dataType">
<div ng-switch-when="string" ><input type="text" ng-model="item.value" /></div>
<div ng-switch-when="password" ><input type="password" ng-model="item.value" /></div>
<div ng-switch-when="check" ><input type="checkbox" ng-model="item.value" /></div>
</div>
</li>
</ul>
</body>
Option #2 - ng-show plunker
<body ng-controller="MainCtrl">
<ul>
<li ng-repeat="item in items">
<div ng-show="item.dataType == 'string'" ><input type="text" ng-model="item.value" /></div>
<div ng-show="item.dataType == 'password'" ><input type="password" ng-model="item.value" /></div>
<div ng-show="item.dataType == 'check'" ><input type="checkbox" ng-model="item.value" /></div>
</li>
</ul>
</body>
Option #3 - ng-hide plunker
<body ng-controller="MainCtrl">
<ul>
<li ng-repeat="item in items">
<div ng-hide="!(item.dataType == 'string')" ><input type="text" ng-model="item.value" /></div>
<div ng-hide="!(item.dataType == 'password')" ><input type="password" ng-model="item.value" /></div>
<div ng-hide="!(item.dataType == 'check')" ><input type="checkbox" ng-model="item.value" /></div>
</li>
</ul>
</body>
You should use the ng-if directive.
<input ng-if="item.dataType === 'string'" type="text"/>
<input ng-if="..." type="password"/>
The problem with using ng-show like #rob suggested, is that it only uses CSS to hide the element, which is not ideal if you want the two inputs to have the same name/ID.
ng-if will remove the element from the DOM if the condition is not true.
for a problem this simple there's no need to go and implement your own directive.

Getting value from input control using jQuery

I am using the teleriks treeview control (asp.net mvc extensions), where I may have up to three children nodes, like so (drumroll...... awesome diagram below):
it has its own formatting, looking a bit like this:
<%=
Html.Telerik().TreeView()
.Name("TreeView")
.BindTo(Model, mappings =>
{
mappings.For<Node1>(binding => binding
.ItemDataBound((item, Node1) =>
{
item.Text = Node1.Property1;
item.Value = Node1.ID.ToString();
})
.Children(Node1 => Node1.AssocProperty));
mappings.For<Node2>(binding => binding
.ItemDataBound((item, Node2) =>
{
item.Text = Node2.Property1;
item.Value = Node2.ID.ToString();
})
.Children(Node2 => Node2.AssocProperty));
mappings.For<Node3>(binding => binding
.ItemDataBound((item, Node3) =>
{
item.Text = Node3.Property1;
item.Value = Node3.ID.ToString();
}));
})
%>
which causes it to render like this. I find it unsual that when I set the value it is rendered in a hidden input ? But anyway:...
<li class="t-item">
<div class="t-mid">
<span class="t-icon t-plus"></span>
<span class="t-in">Node 1</span>
<input class="t-input" name="itemValue" type="hidden" value="6" /></div>
<ul class="t-group" style="display:none">
<li class="t-item t-last">
<div class="t-top t-bot">
<span class="t-icon t-plus"></span>
<span class="t-in">Node 1.1</span>
<input class="t-input" name="itemValue" type="hidden" value="207" />
</div>
<ul class="t-group" style="display:none">
<li class="t-item">
<div class="t-top">
<span class="t-in">Node 1.1.1</span>
<input class="t-input" name="itemValue" type="hidden" value="1452" />
</div>
</li>
<li class="t-item t-last">
<div class="t-bot">
<span class="t-in">Node 1.1.2</span>
<input class="t-input" name="itemValue" type="hidden" value="1453" />
</div>
</li>
</ul>
</li>
</ul>
What I am doing is updating a div after the user clicks on a certain node. But when the user clicks on a node, I want to send the ID not the Node text property. Which means I have to get it out of the value in these type lines <input class="t-input" name="itemValue" type="hidden" value="1453" />, but it can be nested differently each time, so the existing code I ahve doesn't ALWAYS work:
<script type="text/javascript">
function TreeView_onSelect(e) {
//`this` is the DOM element of the treeview
var treeview = $(this).data('tTreeView');
var nodeElement = e.item;
var id = e.item.children[0].children[2].value;
...
</script>
So based on that, what is a better way to get the appropriate id each time with javascript/jquery?
edit:
Sorry to clarify a few things
1) Yes, I am handling clicks to the lis of the tree & want to find the value of the nested hidden input field. As you can see, from the telerik code, setting item.Value = Node2.ID.ToString(); caused it to render in a hidden input field.
I am responding to clicks anywhere in the tree, therefore I cannot use my existing code, which relied on a set relationship (it would work for first nodes (Node 1) not for anything nested below)
What I want is, whenever there is something like this, representing a node, which is then clicked:
<li class="t-item t-last">
<div class="t-bot">
<span class="t-in">Node 1.1.2</span>
<input class="t-input" name="itemValue" type="hidden" value="1453" />
</div>
</li>
I want the ID value out of the input, in this case 1453.
Hope this now makes a lot more sense.
if possible would love to extend this to also store in a variable how nested the element that is clicked is, i.e. if Node 1.1.2 is clicked return 2, Node 1.1 return 1 and node 1 returns 0
It's a little unclear what you're asking, but based on your snippet of JavaScript, I'm guessing that you're handling clicks to the lis of the tree & want to find the value of the nested hidden field? If so, you want something like this:
function TreeView_onSelect(e) {
var id = $(e.item).find(".t-input:first").val();
}
Edit: In answer to your follow-up question, you should be able to get the tree depth with the following:
var depth = $(e.item).parents(".t-item").length;
In jQuery you can return any form element value using .val();
$(this).val(); // would return value of the 'this' element.
I'm not sure why you are using the same hidden input field name "itemValue", but if you can give a little more clarity about what you are asking I'm sure it's not too difficult.
$('.t-input').live('change',function(){
var ID_in_question=$(this).val();
});

Categories

Resources