AngularJS: wait that context is loaded before write HTML - javascript

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.

Related

AngularJS how to re-initialize directive?

I have a directive in AngularJS which is first initialized by default when the parent controller of it is being loaded.
The directive takes data from remote server using AJAX and implements the data in checkboxes - each checkbox get the clue if its already checked or not.
On first load of this page (controller) and directive, the checkboxes are being checked properly.
When I move to another scope (controller) and then back again to the checkboxes scope, none of them are checked.
Even though the data is being requested again from the remote server using AJAX (I console logged it), those checkboxes are ain't get checked.
Should I re-init the directive?
This is the directive (notice the checked attribute):
<checkbox key="{{num}}" selector="{{category.key}}/{{module.key}}" changefunction="togglePermission"
checked="{{permissions[num] && permissions[num].modules.indexOf(category.key+'/'+module.key)!=-1}}"></checkbox>
and this is the checkbox directive html file:
<div class="form-group" ng-class="{'has-danger': errors[selector].length>0}">
<label class="control control-outline control--checkbox {{selector}}">
<div class="input-group">
<span ng-show="title.length>0">{{title}}</span>
<input type="checkbox"
ng-model="this[key][selector]"
ng-disabled="loading"
ng-click="this[changefunction](key, selector)"
ng-checked="checked"
/>
<span class="control__indicator"></span>
</div>
</label>
<small class="text-danger" ng-show="errors[selector].length>0">{{errors[selector]}}</small>
</div>
A quick explaination: I am sending custom key and selector names, for example:
$scope.teams = {
level: ""
}
So the teams will be the key and the level will be the selector
This is the checkbox component js file:
app.directive("checkbox", ($rootScope, parseService) => {
return {
templateUrl: "/shared/checkbox/checkbox.html",
scope: true,
link: function($scope, element, attrs) {
$scope.key = parseService.parse(attrs.key);
$scope.selector = parseService.parse(attrs.selector);
$scope.changefunction = parseService.parse(attrs.changefunction);
$scope.checked = parseService.parse(attrs.checked);
}
}
});

AngularJS share data between nested components properly

I want to embed a nested component in a page.
(A page is actually a controller that can be reached via the $routeProvider service)
And I want to bring data from the main component to its child component and vice versa - in order to make all of the components in the page and the page itself talking with each other in a full data binding.
I success to send data from parent to child with specific bindings attributes, however, I am not getting a way to bring data from child to parent.
// lobby.js - the main page.
// we can reach this page via browser by the $routeProvider service
app.config(($routeProvider) => {
$routeProvider
.when("/", {
templateUrl : "screens/lobby/lobby.html"
})
});
app.controller("lobby", ($scope, datepickerService) => {
$scope.title = "Welcome to Lobby screen!";
$scope.order = {};
$scope.send = function() {
console.log($scope.order);
};
});
Lobby.html
<!-- This is lobby.html file -->
<!-- Which is the html template of the main page (lobby.js) -->
<link rel="stylesheet" href="screens/lobby/lobby.css">
<div class="lobby" ng-controller="lobby">
<date-picker type="default" model="startDate"></date-picker>
<date-picker type="default" model="endDate"></date-picker>
<button type="button" name="button" ng-click="send()">Send</button>
</div>
Now as you can see, in the lobby.html file I have a nested component which is <date-picker></date-picker>. From parent I pass to this child component two attributes: type and model.
Now lets see this component functionality:
// datepicker.js component (actually defined as a directive)
// Initializing a datepicker plugin from jQuery UI Lib.
app.directive("datePicker", (datepickerService) => {
return {
templateUrl: "/shared/datepicker/datepicker.html",
scope: {
model: "#",
type: "#",
},
link: function(scope, elements, attrs) {
$(function() {
setTimeout(function () {
$("." + scope.model).datepicker({
onSelect: function(value) {
value = datepickerService.correct(value);
$("." + scope.model).val(value);
console.log(value);
}
});
}, 200);
});
}
}
});
datepicker.html
<!-- datepicker.html the datepicker html template -->
<!-- Successfuly getting the datepicker to be loaded and work -->
<box ng-show="type=='default'">
<input type="text" class="{{model}}" readonly>
</box>
Now the problem: notice the:
// lobby.js
$scope.send = function() {
console.log($scope.order);
};
in the lobby.js file.
I need this to send the actual startDate and endDate to a remote server. However I cannot access this data! $scope.order remains blank.
I have tried using components instead of directives I have tried ng-include I have tried more lot of things that I wont bother you with, since I have spent on it more than 3 days.
How can I work with nested components so all of the data will be shared through each of them, including the main page in AngularJS in order to create a scaleable modern app?
Thanks.
For sending data from parent to child angular provides the $broadcast() method and for sending data from child to parent it provides the $emit() method.
More info:
http://www.binaryintellect.net/articles/5d8be0b6-e294-457e-82b0-ba7cc10cae0e.aspx
I think you have to reference your startDate and endDate within your order object. Right now it seems you save those directly on your $scope.
Try this to verify:
console.log($scope.order, $scope.startDate, $scope.endDate);
add "order." in front your objects within the model attribute.
<!-- This is lobby.html file -->
<!-- Which is the html template of the main page (lobby.js) -->
<link rel="stylesheet" href="screens/lobby/lobby.css">
<div class="lobby" ng-controller="lobby">
<date-picker type="default" model="order.startDate"></date-picker>
<date-picker type="default" model="order.endDate"></date-picker>
<button type="button" name="button" ng-click="send()">Send</button>
</div>
Also, you might also need to change the attribute definition of your component to use bidirectional binding. Use "=" instead of "#". # only represents a copy of the value when getting passed to your component and not saved back to the original object.
...
scope: {
model: "=",
type: "#",
},
...
Update:
Please find my working Plunker here https://embed.plnkr.co/2TVbcplXIJ01BMJFQbgv/

AngularJS- Autofill Dropdown with Information from Previous Page

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.

Use http cookie value in an Angular template

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).
});

Populate Ember View.Select object on each transition to page

New Ember user here,
I am having an issue trying to get a dropdown view to be populated with initial values from a model on transition to any one of multiple edit routes. I am currently using fixture data...
My router is setup as such:
StoryTime.Router.map(function () {
this.resource('projects', function(){
this.resource('project', { path: '/:project_id' }, function(){
this.resource('stories', function(){
this.resource('story', { path: '/:story_id' }, function(){
this.route('edit');
});
});
this.route('edit');
this.route('report');
this.route('export');
});
this.route('new');
});
});
with a route for editing a story as:
StoryTime.StoryEditRoute = Ember.Route.extend({
setupController: function (controller, model) {
controller.set('model', this.controllerFor('story').get('model'));
}
});
and my controller setup as:
StoryTime.StoryEditController = Ember.ObjectController.extend({
needs:['story', 'project'],
actors: Ember.computed.alias('controllers.project.actors'),
selectedActor: null,
updateActor: function(){
var actor = this.get('selectedActor'),
model = this.get('model');
model.set('actor', actor);
}.observes('selectedActor'),
actions: {
//actions...
}
});
and my template has this piece in question in it:
<div class="form-group">
<label class="control-label col-sm-2 text-left no-padding">Actor:</label>
<div class="controls col-sm-10">
{{model.actor.name}}: {{model.actor.id}}
{{view Ember.Select
name = "actorSelect"
content = actors
optionLabelPath = "content.name"
optionValuePath = "content.id"
selectionBinding = "selectedActor"
class = "form-control"
}}
</div>
</div>
My preferable setup would have:
The model being given to the controller (set by the story edit route to be the model given to the stories route) populate the dropdown box's initial value, but then binds the subsequent selection to the controllers attribute for processing, and
The controller changes the "selectedActor" attribute to null again on transition to another edit route.
Right now, this is not happening. Here's what is happening:
When I navigate to .../stories/1/edit for example, the dropdown is not populated to the model's actor value, but rather the first thing in the list
If I change the value it changes the controller's model's actor, as expected.
However, on subsequent transitions to ../stories/2/edit the dropdown is still populated with the old selected value due to "selectedActor" being set to it.
Can anyone illuminate as to what I am missing here? I feel like there has to be way to both work with the Ember Select view and a way to reset attributes of a particular route or controller on transition. Am I incorrect on this thinking?
Thanks for any insight!
Yeah, fanta is right. You just need to remove a whole bunch of code from your controller and modify your template slightly. Replace your controller and template like so:
StoryTime.StoryEditController = Ember.ObjectController.extend({
needs:['story', 'project'],
actors: Ember.computed.alias('controllers.project.actors'),
actions: {
//actions...
}
});
<div class="form-group">
<label class="control-label col-sm-2 text-left no-padding">Actor:</label>
<div class="controls col-sm-10">
{{actor.name}}: {{actor.id}}
{{view Ember.Select
name = "actorSelect"
content = actors
optionLabelPath = "content.name"
optionValuePath = "content.id"
selectionBinding = actor
class = "form-control"
}}
</div>
</div>
Note you don't need model references in your template. Ember will automatically pass the references back to the underlying model. The primarily job of the controller is to decorate and provide the model (and handle actions & events) to the template / views.

Categories

Resources