I have a basic table in which I'm displaying data, as pulled from a database, through AngularJS. I also have a search field that uses AngularJS to filter the data:
<input ng-model="search" id="search" type="text" placeholder="Search" value="">
<div ng-controller="eventsController")>
<table>
<tr ng-repeat="event in events | filter:search">
<td><span ng-bind="event.title"></span></td>
<td><span ng-bind="event.date_start"></span></td>
</tr>
</table>
</div>
<script>
function EventsController($scope, $http) {
$http.get('/api/all-events').success(function(events) {
$scope.events = events;
});
}
</script>
This is great for user-defined searches, but what if I want to run a particular filter upon page load while maintaining the search functionality? Is there a way that I can use AngularJS to automatically filter the results based on a URL parameter (i.e. example.com?search=foo)? Ideally, the value of the the input field would also be set to the URL parameter.
Like the comments said, this has nothing to do with filter. It's more about how you organize your code to customize URL path you send to the server. You can try to do it this way:
function EventsController($scope, $http) {
// this field is bound to ng-model="search" in your HTML
$scope.search = 'ALL';
$scope.fetchResults = function() {
var path;
if ($scope.search === 'ALL') {
path = '/api/all-events';
} else {
path = '/search?input=' + $scope.search;
}
// here we send different URL path
// depending on the condition of $scope.search
$http.get(path).success(function(events) {
$scope.events = events;
});
};
// this line will be called once when controller is initialized
$scope.fetchResults();
}
And your HTML code, make sure your controller is on the parent div of the input field and search button. And for the search button, you invoke fetchResults() when it's clicked:
<div ng-controller="eventsController")>
<input ng-model="search" id="search" type="text" placeholder="Search" value="">
<button ng-click="fetchResults()">Search</button>
<div>
<table>
<tr ng-repeat="event in events | filter:search">
<td><span ng-bind="event.title"></span></td>
<td><span ng-bind="event.date_start"></span></td>
</tr>
</table>
</div>
</div>
Related
I have doubts about how to share my data between two controllers.
I currently have two "div" with two controllers.
The PriceController driver calls a REST service. The service returns a price matrix that exists in a database and loads the data (objects) in a table.
In the FormController, the form information is obtained and updated in the database.
Desired functionality:
When you click on the "Edit" button, the information is loaded in the form.
The form allows you to edit the "Price" field.
By pressing the "Save" button of the form, it is updated in the database.
I made an example of what I want. The service to save the data in the database is done, so I did not put it in the example..
Questions:
Is it convenient to implement the use of ng-model? How?
Is it necessary to use ngModelOptions?
How can I send the data of my form to the database?
The connection between controllers is correct?
Can you give me an idea?
Example application
Example in JsFiddle
app.js
angular.module("app", [])
.factory("Service", function(){
var data={}
return data;
})
.controller("FormController", function($scope, Service){
$scope.service = Service;
$scope.updateData = function(data){
//TODO: Implement logic to update database
}
})
.controller("DataController", function($scope, Service){
$scope.service = Service;
// Fake database
$scope.dataPrices = [
{
code: 'AA',
price: 111
},
{
code: 'BB',
price: 222
},
{
code: 'CC',
price: 333
}
];
$scope.editData = function(index){
$scope.service.data = $scope.dataPrices[index];
}
});
index.html
<div ng-app="app">
<div ng-controller="FormController" >
<form name="mainForm" novalidate>
<div>
<label>Code</label>
<div>
<input
type="text"
name="code"
ng-required="true"
ng-disabled="true"
value="{{service.data.code}}"
/>
</div>
</div>
<div>
<label>Price</label>
<div>
<input
type="number"
name="price"
ng-required="true"
value="{{service.data.price}}"
/>
</div>
</div>
<div>
<button
type="submit"
ng-click="updateData(data)">
Save
</button>
</div>
</form>
</div>
<div ng-controller="DataController">
<table>
<tr>
<th>Code</th>
<th>Price</th>
<th>Action</th>
</tr>
<tr ng-repeat="dat in dataPrices">
<td>{{dat.code}}</td>
<td>{{dat.price}}</td>
<td>
<button ng-click="editData($index)">
Edit
</button>
</td>
</tr>
</table>
</div>
</div>
Excuse me for my English.
Thank you.
A response to your questions:
Is it convenient to implement the use of ng-model? How?
You can use ng-model like this:
<input ... ng-model="service.data.code" />
Is it necessary to use ngModelOptions?
No its not necessary but it may be useful in some circumstances - https://docs.angularjs.org/api/ng/directive/ngModel
How can I send the data of my form
to the database?
You can post it off to the backend using fetch:
fetch(endpoint, Service.data).then(response => console.log(response))
The connection between controllers is correct?
Yes, using a service is the best approach to sharing data between controllers.
I'm new to Angular. I have designed a single-page app with a series of screens with input forms, some of which need to "drill down" into another screen and auto-fill the destination input form.
Screen #1: After user fills in the form he gets results in a table. Each row in the table displays an entry with the first cell containing a link the user can click for further details about it, for instance:
<td><a ng-click="drillExtDet(e.extractRequestId)"> {{e.extractRequestId}}</a></td>
The controller for this screen has a drillExtDet function as follows, which sets the id into the scope and then activates the detail screen:
$scope.drillExtDet = function(extractRequestId) {
$scope.extrReqId = extractRequestId;
$location.path('/queryExtDet');
}
The new screen is activated, but there are two issues. The form is not getting filled in, and then I also want the form to automatically submit so the details are retrieved.
Screen #2: Has an input form with a field for the extract request id and a Query button:
<form class="navbar-form navbar-center">
<div class="form-group">
<p>Extract Request ID* </p>
<input type="number" min="1" step="1" class="form-control" ng-model="extrReqId" ng-change="resetMessage()" style="width:100px">
<button type="submit" class="form-control btn btn-primary"
ng-click="queryExtDet()" style="width:100px">
<i class="fa fa-shield"> </i>Query
</button>
</div>
</form>
Below this form is a table which displays all the details. How can I get the input field populated, and how can I then make the form auto-submit?
EDITED to add Solution, thanks to accepted answer below:
The Screen #1 drillExtDet function was modified to add the extractRequestId to the $location.path:
$scope.drillExtDet = function(extractRequestId) {
$scope.extrReqId = extractRequestId;
$location.path('/queryExtDet/' + extractRequestId);
}
I added a second entry for Screen #2 in my route provider table:
app.config(function($routeProvider) {
$routeProvider
.when('/queryExtDet', {
templateUrl : 'app/queryExtDet.htm',
controller : 'queryExtDetCtrl'
})
.when('/queryExtDet/:extrReqId', { // with route param to support drill-down
templateUrl : 'app/queryExtDet.htm',
controller : 'queryExtDetCtrl'
})
In the Screen #2 controller I added the $routeParams argument:
app.controller('queryExtDetCtrl', ['$scope', '$http', '$routeParams', function($scope, $http, $routeParams) {
Then my Screen 2 controller was modified to include code to check $routeParams and invoke the queryExtDet() function which sends the request to the server and populates the new screen (the same thing that my submit function does):
$scope.extrReqId = $routeParams.extrReqId;
if ($scope.extrReqId !== undefined) {
queryExtDet($scope.extrReqId);
}
$scope.submit = function() {
//console.log("queryExtDetCtrl.submit() invoked with extrReqId="+$scope.extrReqId);
if (!$scope.extrReqId) {
$scope.message = "'Extract Request ID' is required!";
} else {
queryExtDet($scope.extrReqId);
}
}
function queryExtDet(extractRequestId) {
$scope.extrReqId = extractRequestId;
if (!$scope.extrReqId) {
$scope.message = "'Extract Request ID' is required!";
} else {
// $http.get etc.
}
}
On the input form side, the input field had to be changed from type="number" to type="text", otherwise it would not work:
<form class="navbar-form navbar-center">
<div class="form-group">
<p>Extract Request ID* </p>
<input type="text" class="form-control" ng-model="extrReqId" placeholder="Extract Request Id" ng-change="resetMessage()" style="width:100px" required>
<button type="submit" class="form-control btn btn-primary"
ng-click="submit()" style="width:100px">
<i class="fa fa-shield"> </i>Query
</button>
<i class="fa fa fa-retweet"></i> Export
</div>
</form>
By passing the extrReqId as params, you can access the previous data using API to get those details and by using ng-change and form validation, you can auto submit the data.
i expected the forms are editable and extra fields are present in new screen , if they aren't then no need to use ng-change , after getting details from api u can redirect to submit function where u can check all the details are present or not .
$scope.submit=function(){
if ($scope.userForm.$invalid) {
alert($scope.userForm.$invalid);
}else{}}
<form name="userForm" novalidate>
<table><thead> <tr><th>Name</th> <th>Country</th></tr> </thead>
<tbody>
<tr data-ng-repeat="n in names | orderBy: 'Country'">
<td> <input type="text" ng-model="n.name" name="uName" required="">
<div ng-show="userForm.$submitted && userForm.uName.$error.required">
</td>
<td>{{n.country}}</td>
</tr>
</tbody>
</table>
</form>
I'm just starting out with Angular.
I've written some code that downloads a JSON array configuredAPIs and displays each object within it, <div ng-repeat="capi in configuredAPIs">. For each of these, there's another directive to list the items from an array of strings, <tr ng-repeat="eurl in capi.externalURLs">
Underneath there's a text box to add a new string to this array, which I've bound to a $scope variable called url.
When I click the 'add' button, everything works - the new string is added to the array, a new row appears in the table.. ..but it only works once. Subsequent clicks on the 'add' button add empty strings to the array (and thus empty text boxes).
What have I done wrong?
index.html
<div ng-app="testApp" ng-controller="testCtrl">
<div ng-repeat="capi in configuredAPIs">
<h1>Configured API</h1>
<p>
Name:
{{ capi.name }}
</p>
<h2>External URLs</h2>
<form ng-submit="addExternalURL(capi)">
<table>
<!-- A row in the table for each string in the array -->
<tr ng-repeat="eurl in capi.externalURLs">
<td>
<input type="text" ng-model="eurl" />
</td>
</tr>
<!-- Final table row to add a new string to the array -->
<tr>
<td>
<input type="text" ng-model="url" placeholder="Enter a new external URL">
<input class="btn-primary" type="submit" value="add">
</td>
</tr>
</table>
</form>
</div>
</div>
controller.js
var app = angular.module('testApp', []);
app.controller('testCtrl', function ($scope, $http) {
$scope.url = 'new url';
$http.get("/api/configuredapis?orgid=2")
.success(function (response) { $scope.configuredAPIs = response; });
$scope.addExternalURL = function ($capi) {
$capi.externalURLs.push($scope.url);
$scope.url = '';
};
});
It is because AngularJS does not watch and update primitives (e.g. strings, numbers, booleans) the way one obviously thinks it does.
So instead you bind objects with values to the scope or use a function which returns the primitive value.
See:
https://github.com/angular/angular.js/wiki/Understanding-Scopes
http://www.codelord.net/2014/05/10/understanding-angulars-magic-dont-bind-to-primitives/
Example for using an object (at controller):
$scope.primitives = {
url : 'foo://'
}
And within the template:
<input type="text" ng-model="primitives.url">
So what happens in your example is that once you set it to '' the changes to the model within the template are not recognized anymore.
I am not sure if this is entirely possible, but I am mimicking a function from a program built in flat PHP to Symfony2. I am stuck on this one function.
1) Firstly, there is a row of input fields which are dynamically populated.
<label for="noofracks"># of racks</label>
<input type="text" name="noofitems" id="numberform">
<div id="">
<form name="dataform" method="post" action="" id="dataform">
<input type="hidden" name="lastdasearch">
<div id="racks">
<div class="rack" id="rack1">
<span id="itemno1">Search
</span>
<input type="hidden" id="subid" name="subid1" value="" placeholder="subid" disabled><br/>
<input type="text" id="dano" name="dano1" value="" placeholder="dano" disabled><br/>
<input type="text" id="partno" name="partno1" value="" placeholder="partno" disabled><br/>
<input type="text" id="rackno" name="rackno1" value="" placeholder="rackno" disabled><br/>
<input type="text" id="diecode" name="diecode1" value="" placeholder="diecode" disabled><br/>
<input type="text" id="heatcode" name="heatcode1" value="" placeholder="heatcode" disabled><br/>
<hr />
</div>
</div>
<input type="date" name="shipdate"><br/>
<input type="number" name="qtyshipped" placeholder="Qty Out"><br/>
<input type="text" name="blno" placeholder="BL #"><br/>
<button type="submit" name="submit">Submit</button>
</form>
2) The input fields under the <div id="racks"> will populate based on the number that the user inserts into <input id="numberform"... So the next populated row of inputs will have incremented numbers name="dano2", dano3... etc
3) Values are to be inserted from a popup window that lists data from the database and that is where the link Search comes in. It will open a popup with the lists from the database:
<table class="tablesorter">
<thead>
<th>DA</th>
<th>Part</th>
<th>Batch</th>
<th>Rack</th>
<th>Die Code</th>
<th>Heat Code</th>
<th>Qty</th>
<th></th>
</thead>
{% for entity in entities %}
<tr>
<td>{{ entity.dano }}</td>
<td>{{ entity.partno }}</td>
<td>{{ entity.batchno }}</td>
<td>{{ entity.rackno }}</td>
<td>{{ entity.diecode }}</td>
<td>{{ entity.heatcode }}</td>
<td>{{ entity.inqty }}</td>
<td>Select</td>
</tr>
{% endfor %}
</table>
4) I have the "Select" link go to the controller that selects that row of data from Doctrine, and I want it to load it into the input fields targeting the right input number (into dano1, partno1, rackno1..). I just.. have NO idea how to go about doing it.
/**
* #Route("/searchsub/{subid}", name="log_loaddata")
* #Method("GET")
*/
public function loadDataAction($subid) {
$em = $this->getDoctrine()->getRepository('Main');
$entity = $em->allBySub($subid);
//get the exact entity that the user selected and load the data into the target input fields.
//close the popup window
}
If anyone knows a better way to do this, it would be great as well!
EDIT: The desired behavior and action I would like is:
On the contents of each appended <div class="rack" id="rack1">, there's a search link that will give a popup window with a table with rows of data listed (the rows list the data of 'dano', 'partno', 'batchno' that is to be inserted into the input fields). Next to each row of data will have a "Select" link, which I want to grab that row of data, and populated to the parent page input fields. I want it to identify which <div class="rack" id="rack.. to go into (I haven't figured out that part yet either).
So based on what I understood so far what you are trying to do is creating a click function that will get the information from the table row and create input fields with these values. So this will be purely JS stuff.
First you will need to change HTML a little to get stuff to work.
For each input field you want to populate you should replace the id field to make it a data-name since HTML id can not be duplicated and we are going to have multiple fields of these ( aka multiple "racks" )
This:
<input type="text" id="dano" name="dano1" ... />
Should be:
<input type="text" data-name="dano" name="dano1" ... />
And the same for the rest of the inputs.
in the HTML for the popup you need to add the same data-name="[inputName]" to the <td> elements so you can identify them.
for example:
<td data-name="dano">{{ entity.dano }}</td>
Finally we can add a class to the select link to attach the click event to it:
<a class"select-link" data-id="{{ entity.subid }}" ... >Select</a>
Now once you click on the link what you basically need to do is get the information you need to populate. so something like:
var parentWindow = window.opener; // get the original window to push info to
$('.select-link').on('click', function(e) {
var cells = $(this).parent().siblings(); // Get all TD elements in the same row
var subid = $('this').data('id');
cells.each(function(i, element){
var name = $(element).data('name');
var newRack = parentWindow.$('#rack1').clone() // Clone the row
// insert data into new rack
newRack.attr('id', 'rack' + subid);
$('input[data-name=' + name + ']', newRack)
.val($(element).text())
.attr('name', name + subid);
});
// Append new rack to the main window
parentWindow.$('#racks').append(newRack);
});
This is just a concept for you to start with of course. It will need some tweaking and googling along the way. Hope it helps.
When I push an item to an array, the view won't refresh the list.
table:
<tbody id="productRows">
<tr data-ng-repeat="product in products | filter: search">
<td>{{ product.Code}}</td>
<td colspan="8">{{ product.Name}}</td>
</tr>
</tbody>
form:
<form data-ng-submit="submitProduct()">
Code:
<br />
<input type="text" required data-ng-model="product.Code"/>
<br />
<br />
Naam:
<br />
<input type="text" required data-ng-model="product.Name"/>
<br />
<input type="submit" value="Opslaan" />
</form>
submitProduct in controller:
$scope.submitProduct = function () {
console.log('before: ' + $scope.products.length);
$scope.products.push({Code: $scope.product.Code, Name: $scope.product.Name});
console.log('after:' + $scope.products.length);
console.log($scope.products);
$scope.showOverlay = false;
};
As you can see, I log the total items in the array and it behaves like I would expect. The only thing that doesn't do what I expect is the content of my table, that doesn't show the new value.
What do I have to do, so the new row is displayed in the table?
I can't see the rest of your code, but make sure $scope.products is defined in your controller.
See this example.
The only addition I made to the code you provided was:
$scope.products = [];
If this doesn't help then please provide more information.
Thanks for the answer and the comments. The problem was at another place. In my routeProvider I had declared a controller. I also had a ng-controller directive in my div. So my controller gets executed twice. When I removed the ng-controller directive, everything was just working as it should be :)