Change controller after form submit in AngularJS - javascript

I currently have a multistep form that lets the user filter which kind of devices he wants back. In order to store his selections, in the current controller I have injected a Factory that can store his selections:
// Factory
App.factory('DeviceSelection',function() {
var states=[{selection:{}},{selection:{}},{selection:{}},{selection:{}}];
return states;
});
So, the form that is rendered to the user looks like this:
<form class="form-horizontal">
<ul class="nav nav-tabs">
<li ng-repeat="step in steps" ng-class="{active: $index==getCurrentStepIndex()}">
{{step}}
</li>
</ul>
<div ng-switch on="selection">
<!-- First Step -->
<div ng-switch-when="How much do you talk?">
<input type="radio" name="device_hours" id="device_hours_long" value="Yes" ng-model='states[0].selection.hours'>
<input type="radio" name="device_hours" id="device_hours_short" value="No", ng-model='states[0].selection.hours'>
</div>
<!-- Second Step -->
<div ng-switch-when="Operating System">
<input type="checkbox" value="ios" id="selection_os_iOS" ng-model="states[1].selection.ios"> iOS
<input type="checkbox" value="bb" id="selection_os_bb" ng-model="states[1].selection.bb"> Black Berry
<input type="checkbox" value="dunno" id="selection_os_dunno" ng-model="states[1].selection.dunno"> I don't care
</div>
</div>
<div class="pull-right" ng-show="!hasNextStep()"><button style="margin:20px 0;" class="btn btn-success">Show me the devices!</button></div>
My question is:
Once the user clicks submit, I would like to show his results. I am assuming that I want to use another Controller that will be responsible to look for the data that the User expects (according to the selections he has done in the form). However, I fail to see how would I pass that data that I have in the current $scope to another controller (which will fetch an external API). Where/how should I place the code that will let me pass the control to another Controller and give that controller the selections of the User?

$scope is not the model, its a reference to a model, glue in between the data & the view. If $scopes in two, or more, controllers need to share data use a singleton object by registering a angular factory. That one service/factory can be injected into as many controllers as you like, and then everything can work off that one source of truth.
Here is a demo of a factory passing UI user clicks data between controllers. http://plnkr.co/edit/P2UudS?p=preview
app.factory('uiFieldState', function () {
return {uiObject: {data: null}}
});
app.controller('NavbarCtrl', ['$scope', 'uiFieldState', '$stateParams', '$state',
function($scope, uiFieldState, $stateParams, $state) {
$scope.selected = uiFieldState.uiObject;
}
]);
app.controller('LeftTabACtrl', ['$scope', 'uiFieldState', '$stateParams', '$state',
function($scope, uiFieldState, $stateParams, $state) {
$scope.selected2 = uiFieldState.uiObject;
}
]);
The factory object {uiObject: {data: null}} is injected into the controller with uiFieldState & then its simply $scope.selected = uiFieldState.uiObject; for connecting the factory to the scope ng-model="selected.data" .

Use angular to intercept the form, post the data with $http, then on success set window.location to the proper destination, which should be defined in your router

Related

Angular is not displaying data in table row. ng-repeat is not working

I'm trying to use Angularjs. The problem is, ng-repeat not working when it is in the route. It works when it is not in route.
Here's what I have so far:
See the links..
this works well...
http://plnkr.co/edit/1syQFJdMyRUYncBrREue?p=preview
but in this, it didn't work now..(navigate in persons)
http://plnkr.co/edit/gsi2mZpnU0YJUUOa20DO?p=preview
html
<html ng-app="myApp">
<head>
<title>Confirm Dialog Box</title>
<!--
in the link above i create a custom dialog box
-->
</head>
<body>
<div ng-controller="loglistController">
<table>
<tr ng-repeat="x in names">
<td onclick="showDialog()" ng-click="showInEdit(x)">{{ x.Name }}</td>
<td onclick="showDialog()" ng-click="showInEdit(x)">{{ x.Country }}</td>
</tr>
</table>
<div id="white-background">
</div>
<div id="dlgbox">
<div id="dlg-header"><h3>Information</h3></div>
<div id="dlg-body">
Name <input type="text" ng-model="selectedPerson.Name" /><br/>
Country <input type="text" ng-model="selectedPerson.Country" /><br/>
</div>
<div id="dlg-footer">
<button onclick="dlgOK()">Update</button>
<button onclick="dlgCancel()">Exit</button>
</div>
</div>
angular
var myApp = angular.module('myApp', ['ngRoute']);
myApp.controller('loglistController', ['$scope', '$http', function ($scope, $http) {
$scope.data=[];
$scope.selectedPerson={ Name:"",Country:""};
$http.get("loglistajax.php")
.then(function (response) {$scope.names = response.data.records;});
$scope.showInEdit=function(person){
$scope.selectedPerson = person;
};
}]);
Here is my version
I agree with most points that imbalind mentioned.
Just would like to add few more:
we should not instantiate same app multiple times (in original Plunker we had it in both index.html and list.html). Here are two cases:
var myApp = angular.module('myApp', ['ngRoute']); //new instance of 'myApp'
var myApp = angular.module('myApp'); //simply handle to 'myApp'
we had two 'loglistController' that belong to same angular.module, which not going to work
angular itself was loaded in both html files, not good.
for that particular Plunker, fixed route in app.js:
.when('/', {
templateUrl: '/index.html',
controller: 'mainController'
})
and please don't mixup jQuery script, it will create a lot of confusion later...
Here is a (somewhat) fixed plunker, however I found the following issues in your code:
You have double controllers: loglistController is defined twice, once inside app.js and then again inside list.html.
You wrote the following line
<tr ng-repeat="x in data">
Thinking it would read from loglistController's $scope.data but your app seems to be considering the first controller, so your controller scope has no data properties. I solved replacing the controller in app.js with the one inside list.html.
Thinking about it, I guess you are not allowed to put a route controller's code inside it's template, so you'd better avoid doing that in the future!
Your app.js' templateUrl does not exists when url is '/' and this throws an error. I solved by replacing it with 'list.html'. I'm quite sure it's not what you will want, but it was needed to make it work.
are you sure your $http request is resolving?
$http.get("loglistajax.php").then(function (response) {
$scope.names = response.data.records;
}).catch(function(response) {
console.log("I am failing to resolve a non-2xx response");
});
initially try without the $http request using dummy data n build up slowly!
FYI ...
you can do ...
$scope.names = $http.get("loglistajax.php");
Which only works if the HTTP request resolve!
PS neither of your plunkr's show anything!

Angular ng-model Over Several Views

Can I use ng-model to build up an object over several views?
For instance, say in view1 I have
<input ng-model='myObject.firstName'>
And in view2 I have
<input ng-model='myObject.lastName'>
And in view3 I have
<input ng-model='myObject.email'>
The idea being you could hit a submit button in the last view, and return the object somewhere.
My initial approach to this is to have a service which declares an empty object, then have functions in the service which allow the controllers using the service to add their view input to that object, then return the object.
However I feel like this is quite a roundabout way of doing it!
If anyone could point me in the right direction I would really appreciate it.
You can use a service for that. Here an example with 3 controllers sharing the same object using 3 directives ng-model. Each controller modify the tested.value property, but you can use differents properties of course.
angular.module('test', []).factory('tested', function() {
return {
value : '123'
};
}).controller('ctrl1', function($scope, tested) {
$scope.tested = tested;
}).controller('ctrl2', function($scope, tested) {
$scope.tested = tested;
}).controller('ctrl3', function($scope, tested) {
$scope.tested = tested;
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="ctrl1">
<input type="text" ng-model="tested.value" />
{{ tested.value }}
</div>
<div ng-controller="ctrl2">
<input type="text" ng-model="tested.value" />
{{ tested.value }}
</div>
<div ng-controller="ctrl3">
<input type="text" ng-model="tested.value" />
{{ tested.value }}
</div>
</div>
Since each view has its controller, the only way to share data is with a service of type "provider", "service" or "factory".
You could then modify your object from each controller with the methods you talk about.
In the end, to notify each view something changed, the service methods could raise an event from the service :
$rootScope.$broadcast('somethingChanged', myObject);
And each controller could listen with:
$scope.$on('somethingChanged', function(data) {
});

Search AngularJS resource using query string

I have the following resource set up in my AngularJS app:
var phonecatServices = angular.module('phonecatServices', ['ngResource']);
phonecatServices.factory('Phone', ['$resource',
function($resource){
return $resource('phones/:phoneId.json', {}, {
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
});
}]);
And by default they are listed like so via the controller:
var phonecatControllers = angular.module('phonecatControllers', []);
phonecatControllers.controller('PhoneListCtrl', ['$scope', 'Phone',
function($scope, Phone) {
$scope.phones = Phone.query();
$scope.orderProp = 'age';
}]);
As you can see I already have a filter but I want a query string based search so that I can use the History to get back to the results after choosing a record, etc.
First question I have here, is that this list actually uses phones.json for the data but I don't specify this anywhere in the code... so how does this work? Presume some magic is happening but I can't see it.
So back to the initial question, I have built the following search controller:
phonecatControllers.controller('SearchCtrl', ['$scope', '$http', '$location', 'Phone',
function ($scope, $http, $location, Phone) {
$scope.keywords = $location.search()['q'];
// The function that will be executed on button click (ng-click="search()")
$scope.search = function() {
$location.path('phones').search('q', $scope.keywords);
$scope.phones= Phone.query($scope.keywords);
}
}]);
So it should use the query string to find the results. But how do I do this? It seems very transparent how the data is pulled from the JSON file. The method should also list data if the query string is there on page load... so perhaps this should be combined into one controller for both the list and search?
The code above doesn't filter the JSON data when I do a search... so the query string isn't being used... but I presume it's because I don't understand how the app knows to look in the 'phones.json' file?
The HTML for the filtering and search:
<div class="control-group">
<label class="control-label">Filter:</label>
<div class="controls">
<input ng-model="$parent.query">
</div>
</div>
<div class="control-group">
<label class="control-label">Sort by:</label>
<div class="controls">
<select ng-model="$parent.orderProp">
<option value="name">Alphabetical</option>
<option value="age">Newest</option>
</select>
</div>
</div>
<hr/>
<div ng-controller="SearchCtrl">
<form class="form-search" ng-submit="search()">
<label>Search:</label>
<input type="text" name="q" ng-model="keywords" class="input-medium search-query" placeholder="Keywords...">
<button type="submit" class="btn" ng-click="search()">Search</button>
</form>
</div>
The HTML for the list:
<ul class="phones">
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp"
class="thumbnail phone-listing">
<img ng-src="{{phone.imageUrl}}">
{{phone.name}}
<p>{{phone.snippet}}</p>
</li>
</ul>
OK so to ensure that I understand correctly:
you already have a as-you-type search using Angular's filter filter
this search is implemented using an input bound to a variable named query
you are trying to persist the search terms when changing view and coming back
you want to persist it in the URL
You don't need the new controller or the new input. In the PhoneListCtrl controller, add $scope.query = $location.search().q. This will read the q parameter from the URL and write the value in query, which will automatically fill the input and filter your results.
To do the reverse (ie writing the value of query to the URL), add a ng-change attribute to your input (<input ng-model="query" ng-change="queryChanged()"/>), and add the corresponding function to your controller:
$scope.queryChanged = function () {
$location.search('q', $scope.query)
}
This function will be executed every time the query changes and it will update the URL accordingly. Everything should work out now. See this fiddle.
As a side note, persisting the query in the URL might not be the best idea, as it will remain visible is the user's browser after they have left the search view. You could use session storage, for example.

Calling a controller function within a Directive

I need your help about an AngularJS issue: I'm creating a Directive to manage a customer/offices situation (two select boxes, one for customers and one for offices related to the customer selected). When I load the html page containing the directive I must check if an officeID "is present" and, in that case, fill the html selects with the right values based on that officeID. To do so I must call a function in the controller of the directive. This is my directive:
angular.module("app").directive('myCustomersOffices', ["ConstantsService",
function(ConstantsService){
return {
restrict: 'E',
scope : {
office : '='
},
controller : 'customersOfficesController',
templateUrl: ConstantsService.URL.BASE_APP+'/bundles/customers/views/customersOffices.html',
link : function (scope, elem, attrs, controller) {
//!!!!!!!!!!!!
//Here I would like to call getOfficesByOfficeID(officeID), a function
//contained in the controller
//!!!!!!!!!!!!!!!!!!!!
}
};
}]);
This is my html directive template:
<div id="customer-and-offices">
<select class="form-control" id="activity-customers"
data-ng-change="getOffices(selectedCustomer)" data-ng-options="customer.ID as customer.name for customer in customers" data-ng-model="selectedCustomer">
<option value="">Choose customer...</option>
</select>
<select class="form-control" id="activity-offices"
data-ng-options="office.ID as office.name for office in customerOffices" data-ng-model="office">
<option value="">Choose office...</option>
</select>
</div>
and this is the way I call the directive in the main html page:
<my-customers-offices office="activity.get().officeID"></my-customers-offices>
All the stuff that you can read above works properly to retrieve the office starting from the customer selection (the "normal" case). But, as I said, I would like to call a function getOfficeByOffice if an officeID "is present" when the html main page "is ready". How can I pass the possible officeID to the link directive function? Thank you in advance.
Usually, it's a bad idea to have methods in your controllers. I'd advise that you create service modules that contain functions that can be shared across directives and controllers. Then its only a matter of injecting the the module (and the service) anywhere in your main application.
Here's a working fiddle that is somewhat close to what you want.
The code:
'use strict';
var module = angular.module("myApp", [])
.controller('MyCtrl', function ($scope) {
$scope.variable = "this";
})
.directive('myDirective', function () {
return {
template: '{{variable}} is accessible here, so are functions.'
};
});
And the HTML;
<div ng-app="myApp" ng-controller="MyCtrl">
<div my-directive></div>
</div>

AngularJS pass model to controller in live update

The two way binding in AngularJs is great in updating the view anytime the model changes. I was wondering if there was some way to pass the model to a function defined in the controller before being displayed. And not with a button click but live.
So for example, the p element would be updated automatically
<input data-ng-model='myModel'>
<p>{{myModel}}</p>
Is there any way to do the following?
<div data-ng-controller='myController'>
<input data-ng-model='myModel'>
<p>{{increment(myModel)}}</p>
</div>
where increment is a function defined in myController
Most definitely you can. Just define the function in the same controller. For instance
Controller:
app.controller('myCtrl', function($scope) {
$scope.increment = function() {
return $scope.myModel;
}
});
HTML
<div data-ng-controller='myController'>
<input data-ng-model='myModel'>
<p>{{increment()}}</p>
</div>
That returns the exact same thing as {{myModel}}

Categories

Resources