I am migrating segments of Spring MVC code into AngularJS and hit the following problem:
In Spring, there is a nice tag that will take a Collection (or Map) of items and a property path to magically generate a list of checkboxes and have the selected ones checked;
<form:checkboxes path="selectedItems" items="${items}" />
where selectedItems is a List of value and items is Map of value and name.
Yes I can display all the checkboxes using this code:
<span ng-repeat="(key, value) in items" >
<input type="checkbox" ng-value="key" > <label class="label" >{{value}}</label>
</span>
But the trick is how we can auto select the checkboxes based on the values in the selectedItems and then bind it when the user select/unselect other items?
Directive give your html tag more power. I wrote a simple directive which will take a property "items" to generate a list of checkboxes and checked the selected ones according to item's status.
HTML: define data in your controller and add tag < checkboxes >
<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="utf-8">
<title>Angular test</title>
</head>
<body>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js"></script>
<script src="js/app.js"></script>
<div ng-controller="CheckboxesCtrl">
<checkboxes items="items"></checkboxes>
<button ng-click="changeData()">change data</button>
</div>
</body>
</html>
App.js define controller and directive
var app = angular.module('myApp',[]);
app.controller('CheckboxesCtrl',function($scope){
//fake data
$scope.items = [{label:"A",checked:true},{label:"B",checked:true},{label:"C",checked:false}];
//data binding test
$scope.changeData = function(){
$scope.items[0].checked=false;
$scope.items[0].label="changed A";
}
});
//checkboxes directive
app.directive('checkboxes',function(){
return {
restrict: "E",
scope:{
items: "="
},
template: '<div ng-repeat="item in items">'+
'<input type="checkbox" ng-value="{{item.label}}" ng-checked="item.checked" />'+
' <lable class="label"> {{item.label}} </label>'+
'</div>'
};
});
I used ng-checked directive to process checkbox status binding. You could try my test JSFiddle.
Hope this is helpful for you.
Related
The documentation on source binding has an aside which states:
Important: A single root element should be used in the template when
binding to an array. Having two first level DOM elements will result
in an erratic behavior.
However, I'm finding that this is the case even for non arrays.
I have the following HTML, which sets up two div's populated by two templates. The only difference is that the working template wraps that databound spans in a div.
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdn.kendostatic.com/2013.3.1119/js/kendo.all.min.js"></script>
<title>JS Bin</title>
<script id="broken-template" type="text/x-kendo-template">
Foo: <span data-bind="text: foo"></span><br/>
Foo Again: <span data-bind="text: foo"></span>
</script>
<script id="working-template" type="text/x-kendo-template">
<div>
Foo: <span data-bind="text: foo"></span><br/>
Foo Again: <span data-bind="text: foo"></span>
</div>
</script>
</head>
<body>
<div id="broken-div" data-template="broken-template" data-bind="source: this">
</div>
<br/>
<br/>
<div id="working-div" data-template="working-template" data-bind="source: this">
</div>
</body>
</html>
And the JavaScript simply creates a view model with a single property and binds it to both divs:
var viewModel = kendo.observable({foo: "bar"});
kendo.bind($("#broken-div"), viewModel);
kendo.bind($("#working-div"), viewModel);
In both cases, only the first root element and it's children are being bound properly. This suggests that every time I databind to template with more than one element I need to make sure it is wrapped in a single root.
Is this behavior documented somewhere? Is there a bug in Kendo or in my sample code? An explanation for why Kendo requires a single root would be great to hear as well.
(Sample code as a jsfiddle)
It's not documented except in the one place you mentioned. Such is the state of Kendo UI documentation - it's less than complete. I've been using Kendo UI for three years and as far as I can tell you, this is its default behavior and not a bug. Unfortunately, it's one of the many quirks you simply learn (stumble upon) from experience.
Basically the template wont render to a ScrollView using kendo.render(template, response) but WILL work with content = template(response) - BUT this has no styling in view -- see comment below
How to make the template render with kendo stylign in view?
BTW response from api call is JSON:
{"event_id":"5","stamp":"2013-01-24 06:00:00","type":"Event Type","loc":"Location","status":"1"}
<!-- eventDetail view -------------------------------------------------------------------------------------------------->
<div data-role="view" id="view-eventDetail" data-show="getEventDetailData" data-title="eventDetail">
<header data-role="header">
<div data-role="navbar">
<span data-role="view-title"></span>
<a data-align="right" data-role="button" class="nav-button" href="#view-myEvents">Back</a>
</div>
</header>
<div id="eventDetail" data-role="page"></div>
</div>
<script id="eventDetail-template" type="text/x-kendo-template">
--><form id="addEventForm"><p>
<input name="event_type" id="event_type" data-min="true" type="text" value="#= type #" />
</p>
<p>
<input name="event_loc" id="event_loc" data-min="true" type="text" value="#= loc #" />
</p>
<p>
<input name="event_date_time" id="event_date_time" data-min="true" type="datetime" value="#= stamp#" />
</p>
<p>
Share this
<input data-role="switch" id="event_share" data-min="true" checked="checked" value="1"/></p>
<p>
<input type="button" id="eventCancelButton" style="width:30%" data-role="button" data-min="true" value="Cancel" />
<input type="submit" id="eventDoneButton" style="width:30%" data-role="button" data-min="true" value="Done" />
</p></form><!--
</script>
<script>
//eventDetail engine
function getEventDetailData(e) {
$.ajax({
url: 'http://localhost/mpt/website/api/event_details.php?',
type: "GET",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: { userID: 2, eventID: e.view.params.id },
success: function(response) {
console.log(response);
var template = kendo.template($("#eventDetail-template").html()),
content = template(response);//works but no kendo css
//content = kendo.render(template, response);not working
$("#eventDetail")
.kendoMobileScrollView()
.data("kendoMobileScrollView")
.content("<!--" + content + "-->");
}
});
}</script>
The widget classes (like km-button) are not added until the widget is initialized.
The template() and render() functions just return the template as a string with the data replaced (replaces #=foo# with the value of the foo property) but does not init all the widgets. In fact, it coldn't initialize the widgets if it wanted to singe it just returns a text string, not DOM elements. The initialization of the widgets is usually done by the parent widget that is using the template.
render() is not working in your case because its 2nd argument is supposed to be an array. All it does is call the given template function once per item in the array and concatenate the results. If you instead did:
var content = kendo.render(template, [response]); // wrap response in an array
it would return the same text string as template(response). It just provides a way to apply the same template to many items at once.
Normally when you create a widget, in your case calling .kendoMobileScrollView() you would expect it to turn any HTML contents of that element into widgets too, but it looks like the ScrollView widget doesn't do this. I think its intent may have been to just display pages of static content, not other widgets.
There is a Kendo method that isn't listed in the docs, kendo.mobile.init(contents); that you might be able to use to turn your template string into widgets. When I tried it in a jsFiddle it threw some error for me, but you could try something like:
var content = template(response); // apply response to template
var contentElements = $(content); // turn the string into DOM elements
kendo.mobile.init(contentElements); // turn elements into widgets (this throws error for me)
$("#eventDetail").html(contentElements); // add contents to the desired element
$("#eventDetail").kendoMobileScrollView(); // create the scroll view
Also, what is with the end and begin comment bits hanging off the ends of the template? I don't see why those are needed. Might be better to remove them.
The ScrollView widget is supposed to take a series of <div> elements as its children. It then pages between them as you swipe left/right across the control. I don't see you adding a series of <div>s anywhere.
I made a simple example showing the problem: the form below submits to another page using jQuery .post().
Form
$('form').submit(function(){
$.post(
$(this).attr('action'),
$(this).serialize(),
function(data) {
var ret = data;
var final_data = $(ret).find('#holder-1').html();
alert(final_data);
}
)
return false;
})
Action
<div id="holder-1">
<h1>Content 1</h1>
</div>
<div id="holder-2">
<h1>Content 2</h1>
</div>
<div id="holder-3">
<h1>Content 3</h1>
</div>
The log shows that the content of data looks fine. But once i try to find #holder-1 with .find(), it returns undefined.
This is almost the same as the last example in the jQuery api page: http://api.jquery.com/jQuery.post/
Here is the full (not) working example hosted in my website, with some console.log() along the code:
Form: http://www.peixelaranja.com.br/tmp/post.php
I tried nearly anything: passing the dataType as 4th parameter, using $.ajax() instead of .post(), using .filter() instead of .find(), different versions of jQuery... Nothing seems to work.
All the files are UTF-8.
Any ideas?
I know you mentioned that you used filter instead of find, however, that should work. jQuery is stripping out the html, head,body tags, etc. Once this is done, #holder-1 is now at the top level of the hierarchy.
This fiddle demonstrates that filter does work:
http://jsfiddle.net/68jEt/
var html = '<!doctype html><html dir="ltr" lang="pt-BR"><head> <meta charset="UTF-8"> <title>Return Test</title></head><body> <div id="holder-1"> <h1>Content 1</h1> </div> <div id="holder-2"> <h1>Content 2</h1> </div> <div id="holder-3"> <h1>Content 3</h1> </div></body></html>';
alert($(html).filter("#holder-1").html());
So I am making a test app using RequireJs, Mustache and Backbone.js. I had some success with rendering the collection of models with the Mustache template. But my Mustache template has a button and when I try to bind click event on the button in the view, the button click doesn't invoke the callback function. I am really stuck, can someone tell me where I am not doing right?
Here is my code:
ItemView.js:
define(['jquery', 'backbone', 'underscore', 'mustache', '../../atm/model/item'], function ($, Backbone, _, Mustache, Item) {
var ItemView = Backbone.View.extend({
initialize: function() {
},
tagName: 'li',
events: {
'click .button': 'showPriceChange'
},
render: function() {
var template = $('#template-atm').html();
var itemObj = this.model.toJSON();
itemObj['cid'] = this.model.cid;
var rendering = Mustache.to_html(template, itemObj);
this.el = rendering;
return this;
},
showPriceChange: function(event) {
alert('Changing...');
$('#' + elemId).empty();
$('#' + elemId).append(document.createTextNode('Changed'));
},
});
return ItemView;
});
atm.html:
<!DOCTYPE html>
<html>
<head>
<title>Elevator</title>
<script data-main="scripts/main" src="scripts/require-jquery.js"></script>
<style type="text/css">
</style>
</head>
<body>
<h1>Vending Machine</h1>
<div id="atm-items">
</div>
<script id="template-atm" type="html/template">
<li>
<p>Item: {{name}}</p>
<label for="price-{{cid}}">Price:</label>
<input id="price-{{cid}}" type="text" value="{{price}}"/>
<button class="button">Change</button>
<p id="status-{{name}}-{{cid}}">- -</p>
</li>
</script>
</body>
</html>
You're replacing the view's el inside render:
render: function() {
//...
this.el = rendering;
//...
}
When you do that, you're losing the jQuery delegate that is attached to this.el, that delegate handler (which Backbone adds) is responsible for the event routing.
Usually, you add things to this.el rather than replacing this.el. If your template looked like this:
<script id="template-atm" type="html/template">
<p>Item: {{name}}</p>
<label for="price-{{cid}}">Price:</label>
<input id="price-{{cid}}" type="text" value="{{price}}"/>
<button class="button">Change</button>
<p id="status-{{name}}-{{cid}}">- -</p>
</script>
then you would this.$el.append(rendering) in your view's render; this would give you an <li> in this.el since you've set your view's tagName to li.
Alternatively, if you really need to keep the <li> in the template, you could use setElement to replace this.el, this.$el, and take care of the event delegation:
this.setElement(rendering);
Presumably you're wrapping all these <li>s in a <ul>, <ol>, or <menu> somewhere else; if you're not then you're producing invalid HTML and the browser might try to correct it for you, the corrections might cause you trouble elsewhere as your HTML structure might not be what your selectors think it is.
I`m newbie in jquery.
Got the following code:
`` `
How do I validate the textbox for user input required on clicking the button save and display error message next to the save button?
There appears to be a Validation Plug-In for jQuery. Have you looked at this already?
If not, it appears to do exactly what you want, and there is plenty of documentation right there to get started.
You can try something like
<html>
<head>
<script type="text/javascript">
function Test(txtCheck, lblCheck)
{
if (txtCheck.value == "")
lblCheck.innerHTML = "Textbox is empty";
else
lblCheck.innerHTML = "";
}
</script>
</head>
<body>
<input type="text" id="txtCheck" />
<input type="button" value="Save" onclick="Test(txtCheck, lblCheck);"/>
<label id="lblCheck"/>
</body>