I have searched quite throughly on StackOverFlow, but did not find my answer, so I will ask below.
I have a form with a dropdown menu. And if the user clicks a button, it will take it to a new html page with more information to fill out. But how do I pass my data from the dropdown menu to the next page and autofill the same dropdown menu with the option selected if I am using the same controller for the form for both pages?
var app = angular.module('myApp', []);
var currentEquipmentType = "";
app.controller('myController', ['$scope', function($scope) {
$scope.addEquipment.type = "";
$scope.addEquipment.name = "";
$scope.typeList = ['A', 'B', 'C', 'D'];
//trying to get this info passed onto the next page.
if (sessionStorage.type) {
currentEquipmentType = sessionStorage.type;
}
$scope.getEquipmentInfo = function() {
if ($scope.addEquipment.name !== undefined) {
sessionStorage.name = $scope.addEquipment.name;
sessionStorage.type = $scope.addEquipment.type;
} else {
// warning message
}
}
}]);
<input type="text" ng-model="addEquiment.name">
<select ng-model="addEquipment.type" ng-options="type for type in typeList">
<option value="" selected="selected">Please select a type.</option>
</select>
<!-- on the next page (different html file, but uses the same controller as the previous page) -->
<!-- more form inputs here -->
<select ng-model="addEquipment.type" ng-options="type for type in typeList">
<option>NEED THE OPTION SELECTED FROM PREVIOUS PAGE</option>
</select>
You can good use directive and set the same template for both dropdowns like in example, every element has attribute select-box will filled this template:
var app=angular.module('myApp', []);
app.directive("selectBox", function() {
return {
restrict: 'A',
template :'<select ng-model="addEquipment" ng-options="type for type in typeList"><option value="">Please select a type.</option></select>',
link: function(scope, elem, attrs) {
scope.typeList = ['A','B','C','D'];
}
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div ng-app="myApp" >
<div select-box></div>
</div>
SERVICES SOLUTION:
One way to persist data in AngularJS apps would be in Services (or Factories). Services are singletons so they will persist data as long as you don't refresh the page.
app.service('YourService', function() {
let yourDataFromPreviousPage = {
page: 1,
// currentEquipmentType: '',
// properties can be added later from controllers when you have them resolved.
};
function yourData() {
return ['A','B','C','D'];
}
angular.extend(this,{
yourData,
yourDataFromPreviousPage
});
});
Then just inject the service in your directive, and you can get/set variables on service from there.
More info about services:
https://docs.angularjs.org/guide/services
Really good comparison between services and factories:
https://toddmotto.com/factory-versus-service
UI-ROUTER SOLUTION
Another way would be to use ui-router to keep track of your routes through states - I would recommend this approach since it's de-facto standard nowdays for SPA applications. You can set up routes for different pages and when you switch to different page-state you load different parameters.
You can pass objects between states using:
$state.go('yourstate', {
// your object properties
});
For more info about ui-router:
https://github.com/angular-ui/ui-router/wiki/Quick-Reference
LOCAL STORAGE
You got that covered in previous comments.
Related
I know there are several similar topics already but I found none that really matches my problem.
When opening my AngularJS app/website I am loading two arrays of objects (both are of the same type). One list contains all possible values (called sources here) and the other list is a selection of elements from the first.
My goal is display all sources as checkboxes. The ones from the second list have to be preselected, the rest not. Now the user can select/deselect checkboxes. If he does so I need to inform the server (with the source.id).
What I got so far:
exampleApp.controller('testController', [ '$scope', '$http', function($scope, $http) {
$scope.sources = [];
$scope.selectedSources = [];
$scope.changeSource = function(source) {...};
})
and
<div ng-repeat="source in sources">
<input
type="checkbox"
name="source.name"
value="{{source.id}}"
ng-model="??"
ng-change="changeSource(source.id)"
> {{source.name}}
</div>
What I can't figure out is how I can get ng-model to preselect the right checkboxes and how to get the new (and old) values to changeSource(). Is there an elegant way of doing that?
Example (Pseudo code only):
Sources = [{id=1, name=test1},{id=2, name=test2}, ...]
SelectedSources = [{id=2, name=test2}]
Now what I need are checkboxes like this:
[ ] test1 [x] test2
where all elements from sources are checkboxes and the ones from selectedsources are preselected. Changes of the selection can be stored in selected sources (as objects) and have to trigger my changeSource() function so that I can inform my server.
Set the selected/unselected state in a property inside each of the objects in Sources array(initialize it based on whats present in selectedArray)
$scope.sources.forEach(function(source) {
source.selected = isSelected(source);
})
function isSelected(selectedSource) {
return !!$scope.selectedSources.find(function(s) {
return s === selectedSource || s.id == selectedSource.id;
})
}
Here's a working plunker link
I didn't understood your question very well, but if i'm not mistaken, you want to fill the second collection only with the selected items from the first one, right? If it's the case, you could turn your second collection into a returning function with a filter of the first inside, as follows:
In your controller:
exampleApp.controller('testController', [ '$scope', '$http', function ($scope, $http) {
$scope.sources = [];
/* ... */
$scope.getSelectedSources = function () {
return $scope.sources.filter(function (val) {
return val.selected;
});
};
})
In your template:
<div ng-repeat="source in sources">
<input
type="checkbox"
name="source.name"
value="{{source.id}}"
ng-model="source.selected"
ng-change="changeSource(source.id)"
> {{source.name}}
</div>
<div ng-repeat="source in getSelectedSources()">
<input
type="checkbox"
name="source.name"
value="{{source.id}}"
ng-model="source.selected"
ng-change="changeSource(source.id)"
> {{source.name}}
</div>
Hi this may be help you to get new & old value.
$scope.$watch('sources', function (oldval, newval) {
console.log(oldval + newval);
});
When the input select is loaded in an HTML form, sometimes the data get from the back-end is not ready and the select is displayed without any option selected.
Could be possible to wait that the data is loaded before write the input select in the page?
or there are any other way to select the right option depending on the angular value.
PS. i can't change the data that i get from the back-end and that are una array for the all value and another variable with the selected option. The first one is always loaded correctly but sometimes the second one is empty when i want to select an option.
thanks
I assume you're using asynchronous methods to load the data. In such case, the following should work.
First, have such markup:
<div ng-show="loading">
Loading, please wait...
<!-- can also put gif animation instead -->
</div>
<select ng-hide="loading">...</select>
And in the controller:
$scope.loading = true;
GetData().then(function() {
$scope.loading = false;
}, function() {
$scope.loading = false;
alert('error');
});
This assumes you load the data in a function that returns a Promise, you can of course just put the $scope.loading = false; line in the proper location in your code, after the data is actually loaded.
The effect will be that while $scope.loading is set to true, the user will see the "Loading" message while the drop down is hidden, and when you set it to false, the drop down will become visible while the "Loading" message will become hidden.
Try to get access after event stateChangeSuccess
$scope.$on('$stateChangeSuccess', function() {
(function() {
})();
});
That is how I fix this problem using AngularJS, Angular Resource & Ui-router to display selected object in an entity with Relationship:
Given that we have to entity in a simple relationship:
Class: name(String), level(String). ----> A class in school.
Child: name(String), pseudo(String). ----> A Child.
A child can be in one class at a time and there is many classes in school.
So We can have something like this(a One-To-One):
Class: name(String), level(String). ----> A class in school.
Child: name(String), pseudo(String), class(Class). ----> A Child.
In my Ui-router state I do something like this when editing a Child:
That is the state of the child to edit, when click on a link corresponding to it we query him and use a controller to resolve the entity related to him.
.state('child-edit', {
parent: 'entity',
url: '/child/{id:int}',
views: {
'content#': {
templateUrl: 'path/to/chil/view/child-edit.html',
controller: 'ChildEditController'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('child');
return $translate.refresh();
}],
entity: ['$stateParams', 'ChildService', function($stateParams, ChildService) {
// We return the child to edit using a service.
return ChildService.get({id : $stateParams.id});
}]
}
})
That is the controller I use to make this run normally:
angular.module('myApp').controller('ChildEditController',
['$scope', '$stateParams', '$q', 'entity', 'ClassService',
function($scope, $stateParams, $q, entity, ClassService) {
// We get all classes of school here.
$scope.classes = ClassService.query();
// That is the promise of child to edit get from resolve in state.
$scope.childToEdit = entity;
$q.all([$scope.classes.$promise, $scope.childToEdit.$promise]).then(function() {
// When all data are resolved
// In Js two objects with same properties and valyes but different memory allocation are different.
// So I test value of Id before setting the right class of this child and angular will make able to edit
// him in the UI with the ng-model
var classOfChild = $scope.childToEdit.class;
for (var k in $scope.classes) {
if ($scope.classes[k].id === classOfChild.id) {
// We put the same reference of this class: then it will be selected in the UI of select box
$scope.childToEdit.class = $scope.classes[k];
}
}
});
}]);
And the associated UI in HTML:
<!-- The name of Child -->
<div class="form-group">
<div class="col-md-4">
<label for="field_child_name">Name of Child</label>
<input type="text" class="form-control" name="name" id="field_child_name"
ng-model="childToEdit.name"
required />
</div>
</div>
<!-- Selected class of child will be display here with all other classes available -->
<div class="form-group">
<div class="col-md-4">
<label for="field_child_class">Class of Child</label>
<select class="form-control" id="field_child_class" name="class" ng-model="childToEdit.class" ng-options="class as class.name + ' : ' + class.level for class in classes">
<option value=""></option>
</select>
</div>
</div>
Note: Hope it is the same situation where the selected data is not displaying because the references of querying class and property class in child object are different.
I've created a small sample of what is happening.
http://plnkr.co/edit/py9T0g2aGhTXFnjvlCLF
Basically, the HTML is:
<div data-ng-app="app" data-ng-controller="main">
<select class="ui dropdown" id="ddlState" data-ng-options="s.name for s in states track by s.id" data-ng-model="selectedState"></select>
<select class="ui dropdown" id="ddlCity" data-ng-options="c.name for c in cities track by c.id" data-ng-model="selectedCity"></select>
</div>
And the javascript is:
angular.module("app", [])
.controller("main", function($scope, $timeout) {
$scope.selectedState = {id:1,name:"A"};
$scope.selectedCity = {id:1,name:"A.1",stateId:1};
$scope.states = [{id:1,name:"A"},{id:2,name:"B"},{id:3,name:"C"}];
var fakeDataSource = [
{id:1,name:"A.1",stateId:1},
{id:2,name:"A.2",stateId:1},
{id:3,name:"A.3",stateId:1},
{id:4,name:"B.1",stateId:2},
{id:5,name:"B.2",stateId:2},
{id:6,name:"B.3",stateId:2},
{id:7,name:"C.1",stateId:3},
{id:8,name:"C.2",stateId:3},
{id:9,name:"C.3",stateId:3}
];
$scope.$watch("selectedState", function(n,o){
if (n !== o)
$scope.selectedCity = null;
$scope.cities = fakeDataSource.filter(function(x){
return n.id === x.stateId;
});
$timeout(function(){
$(".ui.dropdown").dropdown().dropdown("refresh");
});
})
$timeout(function(){
$(".ui.dropdown").dropdown();
})
})
The problem is when I change the first dropdown to value 'B' or 'C', the value of second dropdown does not change, even it is changed in angular model.
You guys can notice that I've the line $(".ui.dropdown").dropdown().dropdown("refresh") to refresh the values but does not work.
I tried destroy and recreate using $(".ui.dropdown").dropdown("destroy").dropdown() but still does not work.
Any help?
Simply using ngModel won't make the values change dynamically. Take a look at the documentation here: https://docs.angularjs.org/api/ng/directive/ngModel
You can bind the values using ngBind or what I have done is do an onChange to then check the value and change your second drop down accordingly. Something like:
$("#ddlState").on("change", function(e) {
//check $scope.selectedState for it's value, and change #ddlCity/$scope.selectedCity accordingly
});
Here is the plunkr i have created.
Basically i am facing 2 issues with this piece of code -
I need help loading months in the drop down and
When month is changed in the dropdown from headerController, the sales for that month is displayed in detailController. I am trying to create a dependency between multiple controllers using a service.
I will appreciate any help fixing these 2 issues.
You can use $broadcast service for event purposes. Following is the link which explains the use of $broadcast and communicating between two controllers.
enter code herehttp://plnkr.co/edit/d98mOuVvFMr1YtgRr9hq?p=preview
You could simply achieve this by using $broadcast from one controller in $rootScope and listen that event in $scope using $on. Though I would suggest you to use service that will share data among to controller. Using dot rule will reduce your code. Take a look at below optimized code. Also you could replace your select with ng-repeat with ng-option to save object on select.
Markup
<div data-ng-controller="headerController">
<h3>Select Month</h3>
<select id="month" ng-model="sales.selectedMonth"
ng-options="month.monthId for month in sales.monthlySales">
</select>
</div>
<div data-ng-controller="detailsController">
<h3>Sales for Month</h3>
<div ng-bind="sales.selectedMonth"></div>
</div>
Code
app.service('valuesService', [function() {
var factory = {};
factory.sales = {}
factory.sales.salesForMonthId = 10;
factory.sales.months = [1, 2];
factory.sales.monthlySales = [{monthId: 1,sales: 10}, {monthId: 2,sales: 20}];
factory.sales.selectedMonth = factory.sales.monthlySales[0];
return factory;
}]);
app.controller('headerController', ['$scope', 'valuesService',
function($scope, valuesService) {
$scope.sales = {};
getData();
function getData() {
$scope.sales = valuesService.sales;
}
}
]);
app.controller('detailsController', ['$scope', 'valuesService',
function($scope, valuesService) {
$scope.sales = {};
getData();
function getData() {
$scope.sales = valuesService.sales;
}
}
]);
Demo Plunkr
I can see the months are already loading fine.
For proper data binding to work across service and controller, you would need to bind one level above the actual data, resulting a dot in your expression. This is because javascript doesn't pass by reference for primitive type.
In service:
factory.data = {
salesForMonthId: 0
}
In controller:
app.controller('detailsController', ['$scope', 'valuesService',
function ($scope, valuesService) {
$scope.values = valuesService.data;
}
]);
In template:
<div>{{values.salesForMonthId}}</div>
Plunker
I have angular working in one of my ASP.NET MVC applications. I am using two html templates with Angular Routing. One is a list of current Favorites that comes from the database and is serialized into json from my Web API and used by angular to list those items from the database.
The second html template is a form that will be used to add new favorites. When the overall page that includes my angular code loads, it has a cookie named currentSearch which is holding the value of whatever the last search parameters executed by the user.
I would like to inject this value into my angular html template (newFavoriteView.html) for the value of a hidden input named and id'd searchString.
I have tried using jQuery, but had problems, plus I would much rather do this inside of angular and somehow pass the value along to my template or do the work inside the view(template). However, I know the latter would be bad form. Below is the code I think is important for one to see in order to understand what I am doing.
Index.cshtml (My ASP.NET VIEW)
#{
ViewBag.Title = "Render Search";
ViewBag.InitModule = "renderIndex";
}
<div class="medium-12 column">
<div data-ng-view=""></div>
</div>
#section ngScripts {
<script src="~/ng-modules/render-index.js"></script>
}
Setting the cookie in the MVC Controller
private void LastSearch()
{
string lastSearch = null;
if (Request.Url != null)
{
var currentSearch = Request.Url.LocalPath + "?" +
Request.QueryString;
if (Request.Cookies["currentSearch"] != null)
{
lastSearch = Request.Cookies["currentSearch"].Value;
ViewBag.LastSearch = lastSearch;
}
if (lastSearch != currentSearch)
{
var current = new HttpCookie("currentSearch", currentSearch){
Expires = DateTime.Now.AddDays(1) };
Response.Cookies.Set(current);
var previous = new HttpCookie("lastSearch", lastSearch) {
Expires = DateTime.Now.AddDays(1) };
Response.Cookies.Set(previous);
}
}
}
render-index.js
angular
.module("renderIndex", ["ngRoute"])
.config(config)
.controller("favoritesController", favoritesController)
.controller("newFavoriteController", newFavoriteController);
function config($routeProvider) {
$routeProvider
.when("/", {
templateUrl: "/ng-templates/favoritesView.html",
controller: "favoritesController",
controllerAs: "vm"
})
.when("/newsearch", {
templateUrl: "/ng-templates/newFavoriteView.html",
controller: "newFavoriteController",
controllerAs: "vm"
})
.otherwise({ redirectTo: "/" });
};
function favoritesController($http) {
var vm = this;
vm.searches = [];
vm.isBusy = true;
$http.get("/api/favorites")
.success(function (result) {
vm.searches = result;
})
.error(function () {
alert('error/failed');
})
.then(function () {
vm.isBusy = false;
});
};
function newFavoriteController($http, $window) {
var vm = this;
vm.newFavorite = {};
vm.save = function () {
$http.post("/api/favorites", vm.newFavorite)
.success(function (result) {
var newFavorite = result.data;
//TODO: merge with existing topics
alert("Thanks for your post");
})
.error(function () {
alert("Your broken, go fix yourself!");
})
.then(function () {
$window.location = "#/";
});
};
};
favoritesView.html
<div class="container">
<h3>New Favorite</h3>
<form name="newFavoriteForm" ng-submit="vm.save()">
<fieldset>
<div class="row">
<div class="medium-12 column">
<input name="searchString" id="searchString" type="hidden"
ng-model="vm.newFavorite.searchString"/>
<label for="title">Name</label><br />
<input name="title" type="text"
ng-model="vm.newFavorite.name"/>
<label for="title">Description</label><br />
<textarea name="body" rows="5" cols="30"
ng-model="vm.newTopic.description"></textarea>
</div>
<div class="medium-12 column">
<input type="submit" class="tiny button radius" value="Save"/> |
Cancel
</div>
</div>
</fieldset>
</form>
</div>
My current attepts have been using jQuery at the end of the page after Angular has loaded and grab the cookie and stuff it in the hidden value. But I was not able to get that to work. I also thought about setting the value as a javascript variable (in my c# page) and then using that variable in angular some how. AM I going about this the right way?
Or should it be handled in the angular controller?...
I'm new to angular and the Angular Scope and a bit of ignorance are getting in the way. If any other info is needed I can make it available, thanks if you can help or guide me in the right direction.
You can do it by reading the cookie value using JavaScript, set it as a property of the $scope object and access it on the template.
//Inside your controllers
function favoritesController($http, $scope) {
//Get the cookie value using Js
var cookie = document.cookie; //the value is returned as a semi-colon separated key-value string, so split the string and get the important value
//Say the cookie string returned is 'currentSearch=AngularJS'
//Split the string and extract the cookie value
cookie = cookie.split("="); //I am assuming there's only one cookie set
//make the cookie available on $scope, can be accessed in templates now
$scope.searchString = cookie[1];
}
EXTRA NOTE
In AngularJS, the scope is the glue between your application's controllers and your view. The controller and the view share this scope object. The scope is like the model of your application. Since both the controller and the view share the same scope object, it can be used to communicate between the two. The scope can contain the data and the functions that will run in the view. Take note that every controller has its own scope. The $scope object must be injected into the controller if you want to access it.
For example:
//inject $http and $scope so you can use them in the controller
function favoritesController($http, $scope) {
Whatever is stored on the scope can be accessed on the view and the value of a scope property can also be set from the view. The scope object is important for Angular's two-way data binding.
Sorry if I'm misunderstanding or over-simplifying, but...assuming JavaScript can read this cookie-value, you could just have your controller read it and assign it to a $scope variable?
If JavaScript can't read the value, then you could have your ASP write the value to a JavaScript inline script tag. This feels yuckier though.
Update to show controller-as example.
Assuming your HTML looked something vaguely like this:
<div ng-controller="MyController as controller">
<!-- other HTML goes here -->
<input name="searchString" id="searchString" type="hidden" ng-model="controller.data.currentSearch"/>
Then your controller may look something like this:
app.controller('MyController', function ($scope, $cookies) {
$scope.data = {
currentSearch: $cookies.currentSearch
};
// Note that the model is nested in a 'data' object to ensure that
// any ngIf (or similar) directives in your HTML pass by reference
// instead of value (so 2-way binding works).
});