I'm building an Angular app connected to an REST service which does server-side input validation.
e.g. if I send a object to the server with JSON like:
entry: { id: 5, name: "Test", locales: ["de","en"] }
I will get an response like:
{ id: 5, name: "Test", countries: ["de","en"],
__errors__: [
{ field: "entry.name", message: "Test already in use" },
{ field: "entry.countries[1]", message: "'en' is not a country" }
]
}
(quotation marks omitted for better reading)
The field value is the "path" in javascriptish notation to the original value which caused the problem.
I'm somewhat free in what notation I will choose but I like this one because it's easy to read and integrates with the rest of the system. But I'm open to better suggestions.
The Question:
Now I want Angular to show which field failed with which message. What's the best way of doing this?
I tried things like $scope.EditForm.$setValidity( field, message ) but it has no effect.
(nb: I'm using Angular with Bootstrap)
Why not return an errors object that you bind to in your markup?
Here's a fiddle: http://jsfiddle.net/edeustace/UMRU9/2/
Here's a snippet:
<div ng-app="App" ng-controller="Ctrl">
<input id="name" type="text" ng-model="name"></input>
<span ng-show="errors.name" style="color: red">{{errors.name}}</span>
<br/>
<input id="lastName" type="text" ng-model="lastName"></input>
<span ng-show="errors.lastName" style="color: red">{{errors.lastName}}</span>
<div ng-repeat="c in countries">
<input ng-model="c" type="text"></input>
<span style="color: red" ng-show="errors.countries[$index]">{{errors.countries[$index]}} </span>
</div>
<button ng-click="submit()">Submit</button>
</div>
Js:
var app = angular.module('App', []);
app.controller('Ctrl', function($scope){
$scope.name = "Ed";
$scope.lastName = "Eustace";
$scope.countries = ["Ireland", "England"];
$scope.submit = function(){
$scope.errors = {name: "Name in use", lastName: "", countries: ["Ireland is not available"] };
}
});
That way you only need to wire up the ui and the update will happen for free due to data binding.
Related
I am developing a web application with technologies html,angularjs, java, Spring.
My requirement is to create a search page for my application. Please find the demo here
I need to enter the details and search the information.Please enter Atlanta in From text field and Chicago in To text field and click on SearchLocations button to view the information.
Few questions which i have:
1) Can i write the business logic in my angularjs controllers which are written in javascript file as the sample code shown below:
angular.module('myApp').controller('searchController', ['$scope', function($scope) {
$scope.places = ['', ''];
$scope.searchValue = '';
$scope.submit = function() {
$scope.showGrid = $scope.Passport;
if ($scope.places[0].length > 0 && $scope.places[1].length > 0) {
$scope.searchValue = $scope.places[0] + $scope.places[1];
}
};
2) Can i write backend logic in my controllers. If yes can you please suggest any links so that i can proceed or gain knowledge from their.
The javascript code which i have written, can i say it as a backend code/business logic. Sorry about this kind of questions but i thought this is the great platform to put my questions to clarify..
If someone ask me to write backend code for this kind of search functionality, how would anyone proceed. Any suggestions would be very very helpful.The code which i wrote is as shown in the demo plunker above.Thanks.
js code:
angular.module('myApp', ['ngAnimate', 'ui.bootstrap', 'ngTouch', 'ui.grid', 'ui.grid.selection', 'ui.grid.edit', 'ui.grid.cellNav']);
angular.module('myApp').controller('citiesCtrl', function($scope) {
// $scope. places = undefined;
$scope.items = ["Atlanta", "Chicago", "NewYork"];
$scope.selectAction = function() {
console.log($scope.places1);
};
});
/*Controller for searchLocations button*/
angular.module('myApp').controller('searchController', ['$scope', function($scope) {
$scope.places = ['', ''];
$scope.searchValue = '';
$scope.submit = function() {
$scope.showGrid = $scope.Passport;
if ($scope.places[0].length > 0 && $scope.places[1].length > 0) {
$scope.searchValue = $scope.places[0] + $scope.places[1];
}
};
$scope.users = [{
'name': 'AtlantaChicago',
'show': true,
'details': [{
"Travel Date": "10/10/2014",
commute: "Bus"
}, {
"Travel Date": "10/11/2014",
commute: "flight"
}]
}, {
'name': 'NewYorkChicago',
'show': true,
'details': [{
"Travel Date": "3/15/2016",
commute: "flight"
}, {
"Travel Date": "10/12/2016",
commute: "flight"
}, ]
}];
$scope.gridOptions = {
enableFiltering: true,
columnDefs: [{
name: 'Travel Date',
width: '5%'
}, {
name: 'Departurecommute',
enableFiltering: false,
width: '12%'
}],
rowHeight: 20,
enableHorizontalScrollbar: 2
};
}]);
html code:
<div>
<label>
Show one Grid
<input type="radio" name="Passport" ng-model="Passport" value=1 ng-click="ShowPassport('Y')" />
</label>
<label>Show two Grids
<input type="radio" name="Passport" ng-model="Passport" value=2 ng-click="ShowPassport('N')" />
</label>
</div>
<div class="row">
<div class="form-group" ng-controller="citiesCtrl">
<label>From</label>
<input type="text" ng-model="places[0]" placeholder="Type Departure City" typeahead="item for item in items | filter:$viewValue | limitTo:8">
</div>
<div class="form-group" ng-controller="citiesCtrl">
<label>To</label>
<input type="text" ng-model="places[1]" placeholder="Type Destination City" typeahead="item for item in items | filter:$viewValue | limitTo:8">
</div>
</div>
<input type="button" value="SearchLocations" ng-click="submit()">
<div ng-show="showGrid==='1'||showGrid==='2'">
<div ng-repeat="user in users | filter: {name: searchValue} : true ">
<h3>First Grid</h3>
<div ui-grid="{ data: user.details }" ng-show="user.show" class="myGrid"></div>
</div>
</div>
<div ng-show="showGrid==='2'">
<div ng-repeat="user in users | filter: {name: searchValue} : true ">
<h3>Second Grid</h3>
<div ui-grid="{ data: user.details }" ng-show="user.show" class="myGrid"></div>
</div>
</div>
Angular is MVW framework, the main component is view and model. So Basically its used for segregation of page rendering from Server Code. Now business logic related to client request handling and page rendering could be write in angular controller but the logic which is related to our actual data/database can not be write in angular controller.So you better know what kind of code you have in back-end and decide based on that.
I'm trying to learn AngularJs and writing some throw away code. I'm trying to create an object Bookmark and push it into an array.
HTML:
<h2>Create a new bookmark </h2>
<form class="form-group" ng-submit="createBookmark(newBookmark)" novalidate>
<!--Title-->
<h4>Bookmark Title</h4>
<input type="text" ng-model="newBookmark.title">
<!--Url-->
<h4>Bookmark Url</h4>
<input type="text" ng-model="newBookmark.url">
<!--Submit-->
<button type="submit" href="#" class="btn btn-primary" id="crForm" ng-click="stopCreating()">Save</button>
</form>
JS:
function resetCreateForm(){
$scope.newBookmark = {
title : '',
url : '',
category : $scope.currentCategory.name
};
}
function createBookmark(bookmark) {
bookmark.id = $scope.bookmarks.length;
bookmark.category = $scope.currentCategory.name;
$scope.bookmarks.push(bookmark);
resetCreateForm();
}
$scope.createBookmark = createBookmark;
$scope.resetCreateForm = resetCreateForm;
Object:
$scope.bookmarks = [
{id: 0, title: "Title1", url: "www.Title1.com", category: "Development"},
{id: 1, title: "Title2", url: "www.Title2.com", category: "Development"}
];
Module and Controller:
var app = angular.module('list',[]);
app.controller('listController', function($scope){
For some reason it does not work, so far I think it's from changes in the Angular version but I could not find a way to make it work.
You have to bind resetCreateForm & createBookmark in $scope of controller so that you can access them from view.
//place this inside your controller
$scope.resetCreateForm = resetCreateForm;
$scope.createBookmark= createBookmark;
Also you don't need to call function on ng-click, on click of button ng-submit will get called. Remove ng-click="stopCreating()"
I have these objects right here that I will use to save data from a form, and later send it to an api as JSON :
$scope.price = {}
$scope.item = {"price":$scope.price, };
I also have these field which will be used to dynamically generate inputs on a html page:
$scope.fields = [
{
name: $scope.item.title,
title: 'Title',
type: {
view: 'input'
}
},
{
name: $scope.price.regular,
title: 'Regualar Price',
type: {
view: 'input'
}
}
];
Now in order to generate the form I use this code:
<div class="form-group" ng-repeat="field in fields">
<label>{{ field.title }}:</label>
<span ng-switch on="field.type.view">
<span ng-switch-when="input">
<input
ng-model=field.name
type="text"
/>
</span>
</span>
</div>
And with this code, it is not assigning the values in the input to the objects. Is there a way to do it? I know I can do it this way:
ng-model="item[field.name]"
But that limits me to only one level of the object. I want to be able to bind nested objects. And I just can't seem to figure it out. Thank You!
I have a model, which will be related to a number of other models. Think of a stack overflow question, for example, where it is a question related to tags. The final Object might look as follows before a POST or a PUT:
{
id: 28329332,
title: "checkboxes that append to a model in Angular.js",
tags: [{
id: 5678,
name: "angularjs"
}, {
id: 890,
name: "JavaScript"
}]
}
So far, I have the following controller:
.controller('CreateQuestionCtrl',
function($scope, $location, Question, Tag) {
$scope.question = new Question();
$scope.page = 1;
$scope.getTags = function() {
Tag.query({ page: $scope.page }, function(data) {
$scope.tags = data;
}, function(err) {
// to do, error when they try to use a page that doesn't exist
})
};
$scope.create = function() {
$scope.question.$save(function(data) {
$location.path("/question/" + data.id);
});
};
$scope.$watch($scope.page, $scope.getTags);
}
)
So I display all of the tags, paginated, on the page. I want them to be able to select the given tags and append it to my model so that it can be saved.
How can I create a checkbox interface where it updates the $scope.question with the selected other models?
EDIT: think I might be part of the way there
<div class="checkbox" ng-repeat="tag in tags.objects">
<label><input
type="checkbox"
ng-change="setTag(tag.id)"
ng-model="tag"
> {{ tag.name }}
</div>
Then on the controller
$scope.setTag = function(id) {
Tag.get({id: id}, function(data) {
// don't know what now
})
}
Basically, it takes a directive to approach your goal Take a look at the plunker I wrote for you. As you can see, in the list of selected tags the text property of each tag is displayed, it means that the object structure is kept. In your case, you would bind the $scope.question.tags array as the collection attribute and each tag from the $scope.tags as the element attribute.
Here a codepen for multiple check-boxes bound to the same model.
HTML
<html ng-app="codePen" >
<head>
<meta charset="utf-8">
<title>AngularJS Multiple Checkboxes</title>
</head>
<body>
<div ng:controller="MainCtrl">
<label ng-repeat="tag in model.tags">
<input type="checkbox" ng-model="tag.enabled" ng-change="onChecked()"> {{tag.name}}
</label>
<p>tags: {{model.tags}}</p>
<p> checkCount: {{counter}} </p>
</body>
</html>
JS
var app = angular.module('codePen', []);
app.controller('MainCtrl', function($scope){
$scope.model = { id: 28329332,
title: "checkboxes that append to a model in Angular.js",
tags: [{
id: 5678,
name: "angularjs",
enabled: false
}, {
id: 890,
name: "JavaScript",
enabled: true
}]
};
$scope.counter = 0;
$scope.onChecked = function (){
$scope.counter++;
};
});
I found a great library called checklist-model worth mentioning if anyone is looking up this question. All I had to do was this, more or less:
<div class="checkbox" ng-repeat="tag in tags">
<label>
<input type="checkbox" checklist-model="question.tags" checklist-value="tags"> {{ tag.name }}
</label>
</div>
Found this on googling "directives for angular checkbox".
I've tried to create a very simple example of the issue I'm having with AngularJS. I've got a simple scope called testScope. I also have 2 other scopes (grouped1 and grouped2) that are derived from testScope that have been altered using a grouping function found in UnderscoreJs.
script.js
var app = angular.module('testScope', []);
app.controller('mainCtrl', function($scope) {
$scope.testScope = {
test1: {
data: [
{
field1: 'blah',
field2: 'blah blah'
},
{
field1: 'test',
field2: 'test test'
}
]
}
};
$scope.createEntry = function(newEntry) {
$scope.test1.data.push({field1: newEntry.field1, field2: newEntry.field2});
};
$scope.test1 = $scope.testScope['test1'];
$scope.grouped1 = _.groupBy($scope.test1, 'field1');
$scope.grouped2 = _.groupBy($scope.test1.data, 'field1');
});
index.html
<body ng-app="testScope" ng-controller="mainCtrl">
<form ng-submit="createEntry(newEntry)">
Field1: <input type="text" ng-model="newEntry.field1" />
Field2: <input type="text" ng-model="newEntry.field2" />
<input type="submit" />
</form>
data
<div> {{ test1 }} </div><br>
grouped1
<div>{{ grouped1 }}</div><br>
grouped2
<div>{{ grouped2 }}</div>
</body>
The problem is that when I modify my scope (using the form), test1 and grouped1 will update but grouped2 will not. Why doesn't grouped2 update and how do I get grouped2 to update when the scope changes?
Please see my example:
http://plnkr.co/edit/IN8lADekDBxDp1CNf8VG?p=preview
the reference that .groupBy($scope.test1.data, 'field1') creates changes each time $scope.test1.data changes1. Since $scope works based off of the reference, changing that allows the data to become stale or outdated.
To fix this, you can simply wrap the scope in a function. Such as this:
$scope.grouped2 = function() {return _.groupBy($scope.test1.data, 'field1');};
And then just change your reference in your html like so:
grouped2
<div>{{ grouped2() }}</div>
plunkr: here