First off I'd like to say that I'm just starting with AngularJS, so please excuse me if this is a stupid question.
I have a controller that performs an AJAX request and it returns a JSON object. This JSON is then saved as $scope.person. It look's like this:
function PersonController($scope, $http) {
$http({
method: 'GET',
url: constants.adminUrl + '/getJSON.php?data=person'
}).success(function(data, status, headers, config) {
$scope.person = data;
}).error(function(data, status, headers, config) {
throw new Error('I\'m truly sorry, but I couldn\'t fetch your data');
});
}
The file getJSON.php successfully returns what I expect, a JSON object which looks like this:
[{
"id": 1,
"firstName": "Silvestre",
"lastName": "Herrera",
"headline": "DiseƱador y front-end engineer",
"location": "Argentina",
"summary": "Summary summary summary"
}]
And then, in my HTML I have the following:
<ol ng-controller="PersonController">
<li ng-repeat="person in person | filter: {id:1}">
<input data-autoGrow name="firstName" type="text" value="{{ person.firstName }}" placeholder="<?= __("What's your first name?"); ?>"><input data-autoGrow name="lastName" type="text" value="{{ person.lastName }}" placeholder="<?= __("And your last name?"); ?>">
</li>
<li ng-repeat="person in person | filter: {id:1}"><input type="text" value="{{ person.headline }}" placeholder="<?= __("Headline"); ?>"></li>
<li ng-repeat="person in person | filter: {id:1}"><input type="text" value="{{ person.location }}" placeholder="<?= __("Where do you live?"); ?>"></li>
<li ng-repeat="person in person | filter: {id:1}"><textarea placeholder="<?= __("Write something about you..."); ?>">{{ person.summary }}</textarea></li>
</ol>
All the PHP function __() does is translate the given string. Anyway, as you can see I'm using the ng-repeat directive which I'd like to avoid as there's only one person and there will always be only one.
What I tried was using ng-model="person" in the <ul> element instead of ng-repeat="person in person" in every <li> and then try printing {{ person.firstName }} but nothing gets printed. But if I print {{ person }} I do get the whole object.
Well, I guess that pretty much sums up my problem. Thanks in advance for any input!
Can't you do like this?:
$scope.person = data[0];
You don't use the ng-model directive when you're setting the content of an HTML tag. ng-model is the Angular way to handle the value attribute in form fields. For your purposes, you should be able to just write:
<ol ng-controller="PersonController">
<li>
<input data-autogrow name='firstName' ng-model='person[0].firstName'/>
<input data-autogrow name='lastName' ng-model='person[0].lastName'/>
</li>
<li><input ng-model='person[0].headline'/></li>
<li><input ng-model='person[0].location'/></li>
<li><textarea ng-model='person[0].summary'/></li>
</ol>
Note, the textarea tag is not actually vanilla HTML, it's an AngularJS directive implementing the same functionality, so it doesn't work in quite the same way and you can still bind the ng-model attribute instead of plugging it into the content as you would in vanilla HTML.
You can not use same variable name 'person' for array and items in array. Your code should be
$scope.people = data;
Then in html
<li ng-repeat="person in people | filter: {id:1}">
Related
I have an array of string that I want to bind to an array of inputs:
HTML:
<ul>
<li class="form-group" ng-repeat="remark in trip.remarks track by $index">
<label>Remarque {{$index + 1}}:</label>
<textarea class="form-control" type="text" ng-model="remark"></textarea>
</li>
<hr>
</ul>
<button ng-click="addRemark()" class="btn btn-success" style="width: 100%">Ajouter</button>`
In my controller I have already initialized the trip object, and the binding does happen, but when I edit the data through the textarea nothing changes in the trip object.
The is how my controller is coded:
$scope.trip = productService.getCurrentTrip();
$scope.addRemark = function () {
$scope.trip.remarks.push("");
}
It seem the binding is happening only from the controller to the view, also when I click addRemark button a new textarea does appear. So can anyone tell me how can I bind back to the controller?
NB:
1)I also tried to bind to this controller using ng-model="trip.remarks[$index]" but no use.
2) I have other fields in trip object that are binded and working two-ways
This is an example of prototypal inheritance which you can further explore here.
In your code snippet here:
ng-repeat="remark in trip.remarks track by $index"
Ng-repeat is creating a child scope for every remark in your trip object, and since remark is a primitive type, any modifications to it are getting hidden/shadowed and only modifying the child scope. The solution is therefore to utilize the "." notation as lzagkaretos mentioned, or bind your ng-model to an object reference as opposed to the primitive (string).
Option 1 should work. See working plunker.
ng-model="trip.remarks[$index]"
If it does not work for you, please check the trip object structure, maybe it is not as clear as something like {"remarks":["remark 1","remark 2d"]}.
You need to make sure the textarea NgModel is pointing to the same reference. To do so, create an array of objects where you only edit one value of that object.
Here's an example:
angular.module("app",[]).controller("myCtrl", function($scope){
$scope.trip = {};
$scope.trip.remarks = [
{"name": "1", "value": "hello world 1"},
{"name": "2", "value": "hello world 2"},
{"name": "3", "value": "hello world 3"},
];
$scope.test = function(){
alert($scope.trip.remarks[0].value + " | " + $scope.trip.remarks[1].value + " | " + $scope.trip.remarks[2].value);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"></script>
<div ng-app="app" ng-controller="myCtrl">
ul>
<li class="form-group" ng-repeat="remark in trip.remarks track by $index">
<label>Remarque {{$index + 1}}:</label>
<textarea class="form-control" type="text" ng-model="remark.value"></textarea>
</li>
<hr>
</ul>
<button ng-click="test()" class="btn btn-success" style="width: 100%">Ajouter</button>
</div>
I have an ng-repeat with a comment input inside the loop. With a ng-model="comss.comment", however when I start to type on the first input, I can see the typing on the 2nd and all of the other inputs. How can I stop this? I've tried adding a name with a unique ID however that did not work.
Here is my code:
<li class="item" style="margin-top:20px;" ng-repeat="schedule in discoverloaded | filter:scheduleSearch | limitTo:numberOfItemsToDisplay">
<input type="text" ng-model="comss.comment" required='required' placeholder="Write a comment..">
</li>
Since you're in a loop, accessing comss.comment for each loop is going to be the same model, you need to modify your template and the model slightly:
<li class="item" style="margin-top:20px;"
ng-repeat="schedule in discoverloaded | filter:scheduleSearch | limitTo:numberOfItemsToDisplay track by $index">
<input type="text" ng-model="comss[$index].comment"
required='required' placeholder="Write a comment..">
</li>
In the controller it would be a larger object, so for a loop of two items in discoverloaded, you would have this in comss:
comss = {
0: {
comment: ''
},
1: {
comment: ''
}
};
In the template you can't access it via comss.0.comment, which is why you use comss[$index].comment as you're inside the loop when you assign the model.
I'm building a dynamically generated form from a database using ng-repeat in Node.js and Angular. All of the text boxes are replicating the text entered on any one of the text boxes. So, if I type "xyz" in one text box, all of them have "xyz". But, if I submit the results, it only updates that one form reference.
This is the HTML:
<div class="container">
<div class="todo-form">
<form class="form-inline" ng-repeat="todo in todoData">
<li>
<h4>Country Code: {{ todo.country_code }} <input id="{{ todo.country_code }}" type="text" class="form-control input-sm" placeholder="{{ todo.country_name }}" ng-model="formData.text">
<button type="submit" class="btn btn-default" ng-click="updateTodo(todo.country_code)">Update</button></h4><br>
</li>
</form>
</div>
This is the JS it refers to:
angular.module('editTodo', [])
.controller('editController', ($scope, $http) => {
$scope.formData = {};
$scope.todoData = {};
// Get Org Details
$http.get('ref_country_code_get')
.success((data) => {
$scope.todoData = data;
console.log(data);
})
.error((error) => {
console.log('Error: ' + error);
});
Clearly, I need to disable this. I've tried to add a name= or ID={{ todo.country_code }} into the form to make it unique, but that doesn't work. Why are the all acting like they are the same text box? I'm new to Node.js and very rusty with my HTML, but I can't find any reference to this phenomena. Maybe it is too basic that nobody makes this mistake? %)
Andy F solved the issue. Simply replace the ng-model="formdata.txt" to ng-model="todo.txt". Now, none of the text boxes replicate what is typed in any other text box.
I'm learning AngularJS and I making simple task management application.
Application is connecting to external rest service. I try to render all my tasks in activities (task's container) with add task button but when form is in loop then it's not sending anything.
Here's my html code:
<div id="task-group" ng-repeat="activity in activities | filter:query">
<p>
<h2>{{activity.activityName}}</h2></p>
{{activity.activityId}}
<form name="form" ng-submit="addTask()">
<input type="text" ng-model="sendTaskName">
<input type="hidden" name="{{activity.activityId}}" >
<input type="submit" class="btn-primary" value="Add task">
</form>
<ul class="task-list">
<li ng-repeat="task in activity.tasks">
<p>{{task.taskName}}</p>
</li>
</ul>
</div>
Add task function in my controller:
$scope.addTask = function(){
$http({
method: 'POST',
url: 'http://localhost:8080/add-task',
data: {taskName: $scope.sendTaskName, activityId: $scope.sendActivityId}
}).success(function(){
getActivities()
$scope.sendTaskName = ''
})
}
EDIT: #Mickael helps me to solve my problem. The solution is in his answer. Thank you very much once again ! ;)
You are using ng-repeat and inside each iteration, you bind your inputs with the ng-model directive.
What happens here is that on each iteration, a new scope is created. When you start typing inside your input, the sendTaskName variable is created inside the scope of the iteration, not the scope of your controller.
When your form is submitted, you submit the variable of the scope of your controller, that's why you do not send anything.
To solve your problem, I suggest that you give parameters to your addTask method:
<div id="task-group" ng-repeat="activity in activities | filter:query">
<p>
<h2>{{activity.activityName}}</h2></p>
{{activity.activityId}}
<form name="form" ng-submit="addTask(activity.activityId, sendTaskName); sendTaskName = '';">
<input type="text" ng-model="sendTaskName">
<input type="submit" class="btn-primary" value="Add task">
</form>
<ul class="task-list">
<li ng-repeat="task in activity.tasks">
<p>{{task.taskName}}</p>
</li>
</ul>
</div>
And use these parameters in your controller:
$scope.addTask = function(activityId, taskName) {
$http({
method: 'POST',
url: 'http://localhost:8080/add-task',
data: {taskName: taskName, activityId: activityId}
}).success(function(){
getActivities();
})
}
Next time, with a plunkr, I will be able to fix your code ;)
I am pretty new to angular. I am trying to add a filter parameter to my controller that will filter a search by every letter in a word, in order of letter. So imagine my data contained the following words: horses, oranges, terminator, Motorola, floral, orthopedic. When I searched for "or" in my search engine, all words would appear in the result because they all contain "or." Instead, I'd want a filter that filters my data in strict order of letter so I would only get "oranges" and "orthopedic"
This is my current filter in my controller:
$scope.activateFilter = function(textBoxValue) {
$scope.$apply(function() {
if ( textBoxValue=="") {
$scope.pages = FacebookPage.userPages;
} else {
$scope.pages=$filter('filter')($scope.pages,{name:textBoxValue});
}
});
};
This is the HTML for the filtering in case it helps:
<div id="searchEngineController" ng-controller="SearchEngineController">
<ul class="rig">
<li ng-repeat="page in pages">
<a ng-click="pageClick($index)">
<img src= "{{page.image}}" class="img-responsive img-circle" height="140" width="240">
<p style="font-size:25px; text-align:center">{{page.name}}<p>
</a>
</li>
</ul>
</div>
And my JS function:
pagesSearchBar.pageValueDidChange = function(value) {
angular.element(document.getElementById('searchEngineController')).scope().activateFilter(value);
};
Thanks!
You are re-inventing the wheel. Checkout angular build-in filter for ng-repeat
https://docs.angularjs.org/api/ng/directive/ngRepeat
<li ng-repeat="page in pages | filter:x">
Reading your code more thoroughly I think you have some misunderstanding of angular logic there as well. For example, you don't need extra function to detect search change and then getting the scope from dom. You could simply do:
<input type="text" name="search" ng-change="activateFilter(this.value)">
Something like that
UPDATE
So If I understand correctly now, you will want to display only one result for a filter. You could achieve that by somehow combining the filter with limitTo (no time to fiddle around now)
Or even as a temporary solution use css:
.list.filtered .class-of-item {display:none;}
.list.filtered .class-of-item:first-child {display:block;}
UPDATE 2
Ok, now I understand, so you want to use strict filtering in the way that only items starting with the search phrase show up. Angular is ready for that too, check below snippet
angular.module('filterApp',[]);
angular.module('filterApp').controller('MainCtrl',['$scope',function($scope){
$scope.items = ['Orange','Orangutan','Words','Orchid'];
$scope.customComparator = function(actual, expected){
return (actual.toLowerCase().indexOf(expected.toLowerCase()) === 0);
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="filterApp">
<div ng-controller="MainCtrl">
<ul>
<li ng-repeat="item in items | filter:'or':customComparator">{{item}}</li>
</ul>
</div>
</div>
Comparator reference: https://docs.angularjs.org/api/ng/filter/filter
Use something like below :
<input type="search" data-ng-model="searchName">
<li ng-repeat="list in lists | filter:searchName">{{list}}</li>
This filter is already provided by angular.js which itself is very powerful and quite robust.
I have made this plnkr which has lot more features. You might find this helpful.
There a filter in AngularJS named 'filter' that can make your life a lot easier in lesser code.
You can fine tune it to perform search on all fields in the object or you can restrict the search on some specific fields.
Copy pasting example from the link mentioned above:-
<div ng-init="friends = [{name:'John', phone:'555-1276'},
{name:'Mary', phone:'800-BIG-MARY'},
{name:'Mike', phone:'555-4321'},
{name:'Adam', phone:'555-5678'},
{name:'Julie', phone:'555-8765'},
{name:'Juliette', phone:'555-5678'}]"></div>
<label>Search: <input ng-model="searchText"></label>
<table id="searchTextResults">
<tr><th>Name</th><th>Phone</th></tr>
<tr ng-repeat="friend in friends | filter:searchText">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
</tr>
</table>
<hr>
<label>Any: <input ng-model="search.$"></label> <br>
<label>Name only <input ng-model="search.name"></label><br>
<label>Phone only <input ng-model="search.phone"></label><br>
<label>Equality <input type="checkbox" ng-model="strict"></label><br>
<table id="searchObjResults">
<tr><th>Name</th><th>Phone</th></tr>
<tr ng-repeat="friendObj in friends | filter:search:strict">
<td>{{friendObj.name}}</td>
<td>{{friendObj.phone}}</td>
</tr>
</table>
$scope.pages=$filter('filter')($scope.pages,{name:textBoxValue},true);
This above line will change it
$scope.pages = $filter('filter')($scope.pages,function(index, value){
return value === textBoxValue;
},true);