AngularJS Basic Model Value Editing without Controller - angularjs

I am looking at this basic example of AngularJS:
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js"></script>
</head>
<body>
<div>
<label>Name:</label>
<input type="text" ng-model="yourName" placeholder="Enter a name here">
<hr>
<h1>Hello {{yourName}}!</h1>
</div>
</body>
</html>
It is the first example on the website and has no controller or anything. A textfield is attached to the model field yourName.
Now, I was wondering: since editing the text in the textfield changes the model value, how do I change the value of "yourName" programmatically? Thus far, I have found no simple way of doing this without adding a controller and whatnot.
Also, how can I attach a listener to that model to be notified whenever it changes?

In this case, you don't need a controller for this simple example. Angular behind the scenes is "creating"
$scope.yourName
In terms of changing it programmatically, you have to use a controller. In terms of watching for changes, use $scope.$watch
$scope.$watch($scope.yourName, function (newVal, oldVal) {
if (newVal !== oldVal) {
// logic here
}
});

Related

Why are my angular curly braces not working?

My curly braces are showing up as curly braces, I don't get what's wrong, I did everything...when I use the controller to manipulate the input box, it works. But when I sumbit text into the input box on my browser, it doesn't do anything..
This is where I create my module:
'use strict';
var foodApp=angular.module('foodApp', []);
This is my html:
<script src="/lib/angular/angular.js"></script>
<!DOCTYPE html>
<html lang="en-us" >
<head>
<meta charset="utf-8">
<title>Food App</title>
</head>
<body>
<div ng-controller="foodController" ng-app="foodApp">
<input type="text" ng-model="foods"/>
<input type="submit" value="Healthy Lunch?" />
<br />
<br />
<h4>{{outcome}}</h4>
{{foods}}
</div>
<script src="/js/food.js"></script>
<script src="/js/controllers/foodController.js"></script>
</body>
</html>
This is my controller page:
foodApp.controller('foodController', function($scope)
{
var foods = $scope.foods;
$scope.outcome=foods;
var foodsArray=foods.split(',');
if(foodsArray.length<=4)
$scope.outcome="Bon Appetit!";
else if(foodsArray.length<=7)
$scope.outcome="Pig!";
else
$scope.outcome="One at a time, your scale is going to shout!!";
});
The problem is in fact in your controller. The code you have in your controller is only executed once, when the controller is constructed. As such the variable foods is undefined (since $scope.foods is also undefined). A couple of lines later, you try to run foods.split(..) but since it's undefined, it throws an exception and the application fails. This is shown as an error in the developer console of your browser.
To fix it you need to completely refactor your controller code.
foodApp.controller('foodController', function($scope)
{
$scope.handleFoodChange = function() {
$scope.outcome = $scope.foods;
if($scope.foods){
var foodsArray = $scope.foods.split(',');
if(foodsArray.length<=4)
$scope.outcome = "Bon Appetit!";
else if(foodsArray.length<=7)
$scope.outcome = "Pig!";
else
$scope.outcome = "One at a time, your scale is going to shout!!";
}
}
});
and in your view you should change your input to this:
<input type="text" ng-model="foods" ng-change="handleFoodChange()" />
That should make your application work as expected. Now, whenever you make a change in the input field, the function handleFoodChange is called, and it handles the logic that sets a value for the $scope.outcome variable.
Here's a Plunker showing it working

Access form inside controller

Here is my full code:
<html ng-app="myApp">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js"></script>
</head>
<body >
<div ng-controller="myController">
<form name="myForm">
<input ng-model="option" name="test">
{{myForm.$dirty}}
<button>Save</button>
</form>
</div>
<script>
angular.module('myApp',[]).controller('myController',function($scope){
$scope.option=2;
console.log($scope.myForm);
});
</script>
</body>
</html>
While {{myForm.$dirty}} works, console.log($scope.myForm) returns undefined!!! At the same time if I console.log($scope) I can see myForm as one of its properties!!!! As not to go mad, could someone explain this paradox?
I don't like this solution, but it works. Put a $timeout around the code that you would like to access the form. The $timeout kind of forces the code to wait until the form is fully rendered. (Don't forget to inject $timeout.)
angular.module('myApp',[]).controller('myController',function($scope, $timeout){
$scope.option=2;
$timeout(function() {
console.log($scope.myForm);
}); //Note that you don't need it to actually wait for any amount of time
The reason why you see the form if you console.log($scope) but not when you console.log($scope.myForm), is because the console will evaluate $scope when you expand it. By then, the form has rendered and attached to the scope.
Have you tried to put a watcher on the form ? I'm guessing that the form is simply not yet defined when the controller code is defined.
$scope.$watch('myForm', function(form) {
if(form) {
//check if form is defined
}
});

Simple practical example for two way data binding in AngularJS

Can any one help me out to understand what exactly two way data binding in AngularJS means with a help of simple code.
One way data binding -
The model values are automatically assigned to the HTML placeholder elements specified through the data binding notation, but the HTML elements don't change the values in the model(one way).
Example :
Controller :
app.controller('MainCtrl', function($scope) {
$scope.firstName = 'John';
});
HTML :
<span>First name:</span> {{firstName}}<br />
Two Way Data Binding -
The model values are automatically assigned to the HTML placeholder elements specified through the data binding notation, where HTML elements can change the value in the model(two way).
Example :
Controller :
app.controller('MainCtrl', function($scope) {
$scope.firstName = 'John';
});
HTML
<span>First name:</span> {{firstName}}<br />
<span>Set the first name: <input type="text" ng-model="firstName"/></span><br />
In above example we can change firstName model value with the help of HTML Input element.
Working example : http://plnkr.co/edit/GxqBiOoNFuECn55R4uJZ?p=preview
Retrieved from the AngularJS homepage (2015.06.02):
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
</head>
<body>
<div>
<label>Name:</label>
<input type="text" ng-model="yourName" placeholder="Enter a name here">
<hr>
<h1>Hello {{yourName}}!</h1>
</div>
</body>
</html>
This is possibly the simplest example of two-way data binding in Angular.
The <input> is associated to a yourName model, and the same model is used to fill the content of the <h1> tag. Modifying one will automatically update the other.
Although the data binding in the example can be seen as one-way, because you can't modify the <h1> directly, this should get you started. The AngularJS docs and tutorials contain a lot of great resources.

Get selected buttons of btn-group (buttons-checkbox) with angularjs

I'm using bootstrap and angular to build an application. In a HTML page, i'm using this:
<div class="btn-group-justified" data-toggle="buttons-checkbox">
<button type="button" class="btn fzbtn-consServices" ng-repeat="service in availability.services">{{service.name}}</button>
</div>
It's building a button group with dynamic values. Is there a practical way to obtain the selected buttons inside this button group?
I already tried some solutions, some of them are working but I don't know if it's the best way...
1: On "ng-click" method I would change a attribute value (eg. "checked" attribute) of each service to true or false;
2: I searched about any html attribute for btn-group which could offer me all the selected buttons inside this group, but i had no success;
3: I heard that i could beat this problem using Angular Filter, but i didn't find any similar example;
Anyone with a better idea? Thanks so much :)
This is the best solution I found until now:
<div class="btn-group-justified" data-toggle="buttons-checkbox">
<button type="button" class="btn fzbtn-consServices" ng-repeat="service in availability.services" ng-click="onClickService(service)" ng->{{service.name}}</button>
</div>
Controller:
$scope.onClickService = function (service) {
if (service.checked) {
service.checked = "false"
} else {
service.checked = "true";
}
}
Answer: Bootstrap UI
I feel your pain. Bootstrap is not always angular-friendly. But there is a good solution:
The easiest (and by far the cleanest) approach is to use Bootstrap UI. Built by the Angular Team, it is a rewrite of the javascript-portion of Bootstrap but for an Angular-friendly usage. Here's the section about buttons: http://angular-ui.github.io/bootstrap/#/buttons
Example solution: Checkbox button behavior
In this solution, the initial services array is used to store a boolean field 'selected' to know if any particular service is selected or not. (Similar to the "checked" in the question). This field is 2-way bounded to the checkbox state. Clicking the checkbox changes the field and changing the field changes the checkbox state.
var app = angular.module('plunker', ['ui.bootstrap']);
app.controller('MainCtrl', function($scope) {
var services = [
{ name:'Service A' },
{ name:'Service B', selected:true },
{ name:'Service C' },
{ name:'Service D', selected:true }
];
$scope.availability = { services:services };
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS BootStrap UI radios</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.2.js"></script>
<script src="app.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body ng-controller="MainCtrl">
<div class="btn-group-justified">
<label ng-repeat="service in availability.services" class="btn btn-primary" ng-model="service.selected" btn-checkbox>{{service.name}}</label>
</div>
<div ng-repeat="service in availability.services">{{service.name}} <span ng-show="service.selected">- selected</span></div>
</body>
</html>
Radio button behavior
I've included a solution for a "single selection" checkbox also known as a "radio-button". The "current selection" is bound to a variable on the scope. It will get updated automatically when the user picks an element. Setting it will, in turn, change the current selection.
var app = angular.module('plunker', ['ui.bootstrap']);
app.controller('MainCtrl', function($scope) {
var services = [
{ name:'Service A' },
{ name:'Service B' },
{ name:'Service C' },
{ name:'Service D' }
];
$scope.availability = { services:services };
$scope.model = {};
// Here we are using the service object instance as the "selection value".
// Depending on what you need, you could also use some sort of identifier or
// even the $index if that's more useful.
// Immediately select the second one.
$scope.model.selectedService = services[1];
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS BootStrap UI radios</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.2.js"></script>
<script src="app.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body ng-controller="MainCtrl">
<div class="btn-group-justified">
<label ng-repeat="service in availability.services" class="btn btn-primary" ng-model="model.selectedService" btn-radio="service">{{service.name}}</label>
</div>
<div>Current Selection is: {{model.selectedService.name}}</div>
</body>
</html>
NOTE: I used <label> instead of <button>, but I did not have your special CSS styles so it wasn't behaving on the screen, functionality-wise it works equally well with <button> elements.
You don't really specify why you are wanting to "get" the buttons. In general, getting a reference to DOM elements like this is not the "angular way." It is generally better to find a way to use angular's data-binding to manipulate UI elements.
For instance, if you want to show or hide buttons based on data or on another UI event, then use ng-show or ng-hide bound to a property of the service object you are binding these buttons to. Then you can update the object's property to change the UI. You should be able to find a similar way to make other changes (like setting classes, attributes, etc.) with angular's data-binding rather than doing it manually.

Backbone.stickit works on forms but not contenteditable

I'm using Backbone.stickit for two way binding between a View and a Model. I would like to use a contenteditable span as the bound DOM element, but that's not working.
If I use this template:
<script type="text/template" id="textbox-template">
<!--<span id="content" contenteditable/>-->
<input type='text' id='content'/>
</script>
two way binding works as anticipated. If I use the span rather than the input element, changes do not push back to the model. Note that onGet works, so the original model attribute value is visible. However, I've confirmed that onSet is not called, so an event isn't firing properly.
The Backbone.stickit looks for the contenteditable attribute where its value is true so to make it work you need to write:
<script type="text/template" id="textbox-template">
<span id="content" contenteditable="true"/>
<input type='text' id='content'/>
</script>

Resources