I have a single-page app that has several views. On one view, I list all the orders, on another page, I modify those orders. I want to be able to go between the two pages without needing to save any data to a database.
On my development box, this works well. I just save the orders on $window and when I load the details page, I look through window.orders for it. Great. However, when deployed, all my links act like they link to a new page... so the page reloads. This means that when I try to link from my main list page to my details page, the details page doesn't get access to anything on window. Isn't the whole point of a single page app that it doesn't reload the page when changing views? How do I get my links to not load a new page? they're all in the format of text.
So here's part of my orders-list.template.html:
<table class="table-bordered" id="TheTable">
<tr>
<th>Part #</th>
<th>Customer</th>
<th>Job #</th>
<th>Due Date</th>
<th ng-repeat="day in days" class="td-nowrap">{{day}}</th>
</tr>
<tr ng-repeat="order in orders | orderBy:'DueDate'">
<th>{{order.PartNumber}}</th>
<td>{{order.Customer}}</td>
<td class="td-nowrap">{{order.JobNumber}}</td>
<td>{{order.DueDate|date:'shortDate'}}</td>
<td ng-repeat="day in days">
<input type="number" style="width:60px;" ng-model="order.Weeks[day].Pieces" ng-change="calculateHoursOfWork();"/>
{{(order | runTimePlusSetupPlusEfficiency:options:order.Weeks[day].Pieces) | number:3}}
</td>
</tr>
</table>
Note that the part number is a link to #!/orders/{{order.ID}}. I was under the impression this link wouldn't reload the web page.
This is in my main script:
$routeProvider.
when("/orders", {
template: "<orders-list></orders-list>"
}).
when("/orders/:orderId", {
template: "<order-detail></order-detail>"
}).
when("/options", {
template: "<options></options>"
}).
when("/printable", {
template: "<orders-printable></orders-printable>"
}).
otherwise("/orders");
So it should be clear that /orders refers to my ordersDetail component. Here's part of it:
angular.module("orderDetail").component("orderDetail", {
templateUrl: "AngularModules/order-detail/order-detail.template.html",
controller: ['$routeParams', "$http", "$window", function OrderDetailController($routeParams, $http, $window) {
var self = this;
self.TotalNonSetupRunTime = 0.0;
self.options = $window.options;
self.order = null;
$.each($window.orders, function (idx, elem) {
if (elem.ID == $routeParams.orderId) {
self.order = elem;
}
});
if (self.order === null) {
var orderID = null;
if ($routeParams.hasOwnProperty('orderId')) {
orderID = $routeParams.orderId;
}
var windowOrders = "undefined";
if ($window.hasOwnProperty('orders')) {
windowOrders = $window.orders.length;
}
alert("couldn't find order " + orderID + " among " + windowOrders + " orders! try going back and selecting again.");
}
}]
});
because the page is getting reloaded, I'm seeing windowOrders as undefined. Is there some setting in IIS I could change to make links to #<something> not reload the page (as expected)? or do I have to do something on the angularJS side?
Confounding it further... I did find this: hash link reloads page and apparently having a <base> directive screws with this? is there an angular-safe way to do this? I don't think it would make sense to get rid of that directive. I'm actually writing this on an asp.net-MVC page, so I have
<base ng-href="#Url.HttpRouteUrl("Default", new { }).Replace("?httproute=True", "")" />
in my _Layout.cshtml page. This way when it gets deployed (to http://<server>/<app>/) it works as well as when I'm debugging (on locahost:<some port>/).
Related
I have an api that returns a list of values, i want to be able to click the name of the driver in the api to route to driver details but i cant figure out how to do that.
This is how it looks now
Alternative Look
This is how it should look when i click on the name from ng-repeat from api
i am new to angular and its very confusing atm.
this is my controller.
import { app } from '../index';
app.controller("api", function ($scope, $http) {
$scope.Naziv = "Driver Championship Standings - 2013";
$scope.NazivH1 = "Driver Championship";
$scope.TopDrivers = function () {
console.log("i been pressed");
$http.get("https://ergast.com/api/f1/2013/driverStandings.json")
.then(function successCallback(response) {
$scope.drivers = response.data.MRData.StandingsTable.StandingsLists[0].DriverStandings;
console.log("response.data.MRData.StandingsTable.StandingsLists.0.DriverStandings");
console.log(response.data.MRData.StandingsTable.StandingsLists[0].DriverStandings);
}, function errorCallback(response) {
console.log("Unable to perform get request");
});
}
my routes:
app.config(function($routeProvider, $locationProvider) {
$locationProvider.hashPrefix("!");
$routeProvider
.when("/drivers", {
templateUrl: "./src/pageDetails/drivers.html",
})
.when("/teams", {
templateUrl: "./src/pageDetails/teams.html",
})
.when("/races", {
templateUrl: "./src/pageDetails/races.html",
})
.otherwise({
redirect: "./src/pageDetails/default.html",
});
});
this is the how my template looks.
<div ng-controller="api" ng-init="TopDrivers()">
<h1>{{NazivH1}}</h1>
<div id="modifyDiv">{{Naziv}}</div>
<!-- <button ng-click="TopDrivers()">Test Rest</button> -->
<div ng-repeat="x in drivers | orderBy: '+Points'">
<div id="divRow">
<table>
<tr id="tableRow"><td id="td1">Nb: {{x.position}}</td><td id="td2">{{x.Constructors[0].nationality}} {{x.Driver.givenName}} {{x.Driver.familyName}}</td><td id="td3">{{x.Constructors[0].name}}</td> <td id="td4">Points{{x.points}}</td> </tr>
</table>
</div>
</div>
</div>
firstly why do you have ng-repeat on div and not on <tr>? Do you want a separate div for each driver or do you want a separate row? I suggest you use ng-repeat on <tr> and also add a ng-click directive on your <tr> so whenever someone clicks a driver row, function gets executed on your controller and in that function you can route to the driver details like below:-
<tr ng-repeat="x in drivers | orderBy: '+Points'" ng-click="driverDetails(x.DriverId)" id="tableRow">
<td id="td1">Nb: {{x.position}}</td>
<td id="td2">{{x.Constructors[0].nationality}} {{x.Driver.givenName}} {{x.Driver.familyName}}</td>
<td id="td3">{{x.Constructors[0].name}}</td>
<td id="td4">Points{{x.points}}</td>
</tr>
now in your controller write the driverDetails function like below:-
$scope.driverDetails = function (driverId) {
$location.path('/driverDetails').search({ driverId: driverId });
};
in this function you are changing the url and appending the driverId as query string. Now in the routes add another route
.when("/driverDetails", {
templateUrl: "./src/pageDetails/driverdetails.html",
})
so when the driverDetails function is run in the controller, the driverdetails.html is loaded and you have the driverId in the query srting. Now in the driverdetails controller just retrieve the driverId and make call to your api for that specific driver
You need to add a driver details route with an optional parameter (something like /driver-detail/:id in the route definition). Pass it's own controller, a way to fetch the detail data (you might want to keep it in a service) and then add a ng-click event handler for when the user is in the drivers list ng-repeat and you pass the driver id in there.
Something like :
<tr id="tableRow" ng-click="goToDetail(x.id)">...</tr>
And in the controller :
$scope.goToDetail() = function(id) {
$location.path('/driver-detail/' + id)
}
Your route definition could look something like this :
.when('/driver-detail/:id', {
templateUrl: '...',
controller: function($scope, $stateParams) {
$scope.id = $stateParams.id;
...
}
}
But I strongly recommend you to upgrade your stack, even for AngularJS this is an old way to code - they introduced the component API and particularly for routing, the ui-router is a much more powerful alternative to the ngRouter. If you're starting a project, start with Angular CLI (currently Angular 7) and you'll collect a lot more feedback and find more online resources to guide you.
Working on an AngularJS + UI-Router project. Got a task with these (simplified here) requirements:
display a list of items in a table with Edit button at the end of the table row
clicking Edit button should turn a table row into item edit form (inline edit)
Item list and item edit views should be accessible via url.
So I have defined my states:
// app.js
$stateProvider
.state("list", {
url: "/",
component: "listComponent"
})
.state("list.edit", {
url: "/{id}/edit",
component: "editComponent"
});
}
ListComponent template looks like this:
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th> </th>
</tr>
<!-- Hide this row when NOT in edit mode -->
<tr ng-repeat-start="item in $ctrl.items" ng-if="$ctrl.editIndex !== $index">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>
<button type="button" ng-click="$ctrl.onEditClick($index, item.id)">Edit</button>
</td>
</tr>
<!-- Show this row when in edit mode -->
<tr ng-repeat-end ng-if="$ctrl.editIndex === $index">
<td colspan="3">
<ui-view></ui-view>
</td>
</tr>
</table>
And main parts from ListComponent itself:
function ListController($state) {
this.editIndex = null;
this.items = [
{ id: 1, name: "Y-Solowarm" },
// ...
{ id: 10, name: "Keylex" }
];
this.onEditClick = function(index, id) {
this.editIndex = index;
$state.go("list.edit", { id: id });
};
}
Problem:
When I was working on EditComponent I noticed that it initiates http requests twice. After a couple of hours later I came up with such EditComponent that showed what actually was happening:
function EditController() {
// random number per component instance
this.controllerId = Math.floor(Math.random() * 100);
this.$onInit = function() {
console.log("INIT:", this.controllerId);
};
this.$onDestroy = function() {
console.log("DESTROY:", this.controllerId);
};
}
Console showed this output:
DESTROY: 98
INIT: 80
DESTROY: 80
INIT: 9
When clicking Edit for a second time this output shows that
EditComponent#98 is destroyed as we navigate away from it (expected)
EditComponent#80 is created and immediately destroyed (unexpected)
EditComponent#9 is created as we are now 'editing' new item (expected)
This just shows me that many <ui-view>s together with ng-ifs does not play very nice but I have no idea how to fix that.
One thing that I have tried was I created one <ui-view> in ListComponent and was moving it around on ui-router state change by means of pure javascript. But that did not work as I soon started getting errors from ui-router's framework that were related to missing HTML node.
Question:
What am I doing wrong here? I think that angular's digest cycle (and related DOM changes) end later than ui-router starts transitions and related component creation and destruction and that might be a reason of EditComponent#80 being created and quickly destroyed. But I have no idea how to fix that.
Here is a codepen showing what is happening:
https://codepen.io/ramunsk/project/editor/AYyYqd
(don't forget to open developer console to see what's happening)
Thanks
Let's say you're switching from index 2 to index 3. I think this might be what is happening:
The ui-view at index 2 is currently active. In the click handler you call state.go and the ui-view at index 2 briefly receives the updated state parameter id: 3. Then it is destroyed when the ng-if takes effect and the ui-view at index 3 is created.
Change your code so it destroys the ui-view at index 2 first. Add a timeout so it calls state.go shows the second ui-view in the next digest cycle.
this.onEditClick = function(index, id) {
this.editIndex = null;
$timeout(() => {
this.editIndex = index;
$state.go("list.edit", { id: id });
});
};
https://codepen.io/christopherthielen/project/editor/ZyNpmv
I am new to angular JS. I have created a simple page using ngRoute.
In the first view I have a table, and on clicking it redirects to second view.
But I have called two functions using ng-click the changeView functions is running fine. But the second function fails to execute on the second view.
But Its running fine if using on the first view itself.
Heres the code for first view
Angular.php
<div ng-app="myApp" ng-controller="dataCtr">
<table class='table table-striped'>
<thead>
<tr>
<td>Title</td>
<td>Stream</td>
<td>Price</td>
</tr>
</thead>
<tbody>
<tr ng-repeat = "x in names | filter:filtr | filter:search" ng-click=" disp(x.id);changeView()">
<td >{{ x.Title }}</td>
<td>{{ x.Stream }}</td>
<td>{{ x.Price }}</td>
</tr>
</tbody>
</table>
</div>
heres the second View
details.php:
<div ng-app="myApp" ng-controller="dataCtr">
<div class="container">
SELECTED:<input type="textfield" ng-model="stxt">
</div>
</div>
heres the js file:
var app = angular.module("myApp", ['ngRoute']);
app.config(function($routeProvider) {
$routeProvider
.when('/angular', {
templateUrl: 'angular.php',
controller: 'dataCtr'
})
.when('/details', {
templateUrl: 'details.php',
controller: 'dataCtr'
})
.otherwise({
redirectTo: '/angular'
});
});
app.controller('dataCtr', function($scope ,$http ,$location ,$route ,$routeParams) {
$http({
method: "GET",
url: "json.php"})
.success(function (response) {$scope.names = response;});
$scope.changeView = function()
{
$location.url('/details');
};
$scope.disp = function(id)
{
$scope.stxt = $scope.names[id-1].Title;
};
});
The disp function is working fine on the angular view. But not being routed on the second view. I think the syntax for calling the two views in ng click is correct. OR if there any other method to call the associated table cell value to the second view. Please Help.
After a lots of research i figured it out.I used a factory service
app.factory('Scopes', function ($rootScope) {
var mem = {};
return {
store: function (key, value) {
$rootScope.$emit('scope.stored', key);
mem[key] = value;
},
get: function (key) {
return mem[key];
}
};
});
Added this to JS.
Because The scope gets lost on second Controller ,Services help us retain the scope value and use them in different controllers.Stored the scope from first controller
app.controller('dataCtr', function($scope ,$http ,$location,$rootScope,Scopes) {
Scopes.store('dataCtr', $scope);
//code
});
and loaded in the seconded controller.
app.controller('dataCtr2', function($scope ,$timeout,$rootScope,Scopes){
$scope.stxt = Scopes.get('dataCtr').disp;
});
Second view is not working because you cannot use $scope.$apply() method.
$apply() is used to execute an expression in angular from outside of the angular framework.$scope.$apply() right after you have changed the location Angular know that things have changed.
change following code part ,try again
$location.path('/detail');
$scope.$apply();
I am building an node js app within which I have one page on angular js. On one of the pages, all the users are listed down(as hyperlinks) and I click on one of them to access the information about that user.
So, if the first page's URL(i.e. all the users) is /users, I want the second URL to look like /users/:id. So, in a way I want to mimic the REST API format.
The user's info has been found and once the user-id has been found, I m trying to use $location.path(index).
Initially I thought using something like ng-if would help me in rendering using the same page. So, on my .jade file, I have added this condition:
div(ng-show='loadAllUsers()')
$scope.loadAllUsers = function(){
console.log($scope.index == -1)
return $scope.index == -1;
};
$scope.index has been initialized to -1. So, I thought something like this would work when a new id value has been generated. But, the problem is it doesn't seem to reload the whole page or div.. I tried using window.location.href as well. That, even though updated the URL but even that breaks...
So, is there a way on doing this on the same jade file by any chance? If not , then how exactly should I go about this problem?
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, UserService) {
$scope.userDetails={};
//get the list of users from data base. here i am taking some dummy values.
$scope.users=[{
id:1,
name:"Rahul"
},
{
id:2,
name:"Ajay"
},
{
id:3,
name:"Mohit"
}];
// get user details
$scope.getUserDetails=function(userId){
UserService.get({id:userId},function(userDetails){
$scope.userDetails=userDetails;
});
};
});
// No need to change the page URL. Above function will
// call 'UserService' factory. UserService will communicate
// with REST API with URL pattern 'app/rest/users/:id'.
// ':id' will be replaced by the id passed by above function.
// like 'app/rest/users/2'
angular.module('myApp').factory('UserService',function ($resource) {
return $resource('app/rest/users/:id', {}, {
get':{method:'GET', isArray: false}
});
});
<div ng-app="myApp" ng-controller="myCtrl">
<div>
<table>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<tr ng-repeat="user in users">
<td>{{user.id}}</td>
<td ng-click="getUserDetails(user.id)">{{user.name}}</td>
<tr>
</table>
</div>
<div>
<h3>User Details</h3>
id : userDetails.id <br/>
name : userDetails.name <br/>
// other details
</div>
</div>
I am using angular JS with ng-tables with rails as an API.
i successfully integrated ng-tables with server side pagination. The issue i am facing is whenever i go back list page from detail page. table data is not visible. i checked in logs and using breakpoints . i can see data but not lucky.
my controller is sampleController.js
function sampleController($scope, $http, ngTableParams, $state, Auth) {
$data = [];
$scope.tableParams = new ngTableParams({
page: 1,
count: 10
}, {
getData: function($defer, params) {
$http.get('sample.json', {
params: {
page: params.page()
}
})
.success(function(response, status) {
$data = response.data;
params.total(response.count);
$defer.resolve($data);
});
}
});
and my html template is as follows
<table ng-table="tableParams" class="table ng-table-responsive">
<tr ng-repeat="data in $data.tasks">
<td data-title="'Title'">
{{data.title}}
</td>
</tr>
</table>
also i referred issue . but i couldn't able to fix it.
I appreciate your time and input. Hoping for solution from you guys.