Sharing data between Angularjs components - javascript

I'm trying to refactor an Angularjs 1.4 webapp to use components.
The webapp has a header with a "Search" input box: the main section shows a list of contacts filtered according to the value entered in the Search input text.
I'm trying to use ui-router (to switch from list to details. I've created Header and Index component, defined a service in which I store the Search value, defined a state app.index with "resolve" that calls the service.
The problem is that the resolve is done just once when the state is loaded, while I want to update the filtering for each jeypress.
In my code I've already binded the search model between a parent component and header so I could just create components repeating the header component inside each template and I think it would work but I wonder if there is a more elegant way to do it (signals is not very elegant, don't you think so?)
Here is my
App.html
<header-cnt-component search="$ctrl.search"></header-cnt-component>
<ui-view> </ui-view>
App.js
angular
.module('app')
.component('app', {
templateUrl: 'app/containers/App.html',
controller: App
});
function App() {
this.search = {};
}
HeaderCnt.html
<nav class="navbar navbar-default" role="navigation">
<div class="collapse navbar-collapse" id="nav-toggle">
<form class="navbar-form navbar-right" role="search">
<input type="text" class="form-control" placeholder="Search" ng-model="$ctrl.search.name" ng-keyup="$ctrl.startSearch()">
</form>
</div>
</nav>
HeaderCnt.js
angular
.module('app')
.component('headerCntComponent', {
templateUrl: 'app/components/HeaderCnt.html',
controller: HeaderCnt,
bindings: {
search: '='
}
});
/** #ngInject */
function HeaderCnt(contactsService, searchService, $location) {
this.contactsService = contactsService;
this.searchService = searchService;
this.location = $location;
this.contacts = contactsService.get();
}
HeaderCnt.prototype = {
startSearch: function () {
this.searchService.set(this.search);
this.location.path('/');
}
};
Index.html
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Email Address</th>
<th>Phone Number</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="contact in $ctrl.contacts | filter:$ctrl.search">
<td>{{contact.name}}</td>
<td>{{contact.email}}</td>
<td>{{contact.phone}}</td>
</tr>
</tbody>
</table>
</div>
Index.js
angular
.module('app')
.component('indexComponent', {
templateUrl: 'app/components/Index.html',
controller: Index,
bindings: {
search: '='
}
});
/** #ngInject */
function Index(contactsService) {
this.contactsService = contactsService;
this.contacts = contactsService.get();
}
Index.prototype = {
};
search.js
function SearchService() {
var search = {};
this.get = function () {
return search;
};
this.set = function (searchData) {
search = searchData;
};
}
route.js
angular
.module('app')
.config(routesConfig);
/** #ngInject */
function routesConfig($stateProvider, $urlRouterProvider, $locationProvider) {
$locationProvider.html5Mode(true).hashPrefix('!');
$urlRouterProvider.otherwise('/');
$stateProvider
.state('app', {
component: 'app'
})
.state('app.index', {
url: '/',
component: 'indexComponent',
resolve: {
search: function (searchService) {
// var search = {};
// search.name = 'aus';
return searchService.get();
// return search;
}
}
});
}

If I got you correctly this might help.
I would add new state for search:
.state('app.index.search', {
url: '/search/:searchString',
component: 'indexComponent',
resolve: {
search: $stateParams => $stateParams.searchString
}
}
})
if you have binding on search in indexComponent rest should be fairly simple.
/search/some user search
will resolve to your state with search string and you can use if you want to switch to particular searchString state on your controller:
$state.go('app.index.search', searchString)
one more thing, you can use ng-model-options={debounce: 500} to get delay after typing (0.5s) and put watcher on controller for changes on model.
so your html for input:
<input type="text" class="form-control" placeholder="Search" ng-model="$ctrl.search.name" ng-model-options="{debounce: 500}"/>
and in controller:
this.$scope.$watch('$ctrl.search.name', (newVal, oldVal) => {
newVal && $state.go('app.index.search', newVal');
});
Let me know if it helps.

Related

Unknown provider: myFactProvider <- myFact <- thirdCtrl

Hi all my requirement is to share input field data using get set method in factory with another controller.
angular.module('dataModule',[]).factory('myFact',function($http){
var user = {};
return {
getDetails: function () {
return user ;
},
setDetails : function (name,add,number) {
user.name = name;
user.add = add;
user.number = number;
}
}
});
Here is controller code.
angular.module('dataModule',[]).controller('thirdCtrl',function(myFact,$scope) {
$scope.saw=function(){
alert("hello get set method");
$scope.user=myFact.user.getDetails();
console.log(user);
};
});
Here is my html code
<div ng-controller="thirdCtrl">
<h1>hello gaurav come here after click one.</h1>
<div>
<lable>NAME</lable>
<div><input type="text"ng-model="user.name"></div>
</div>
<div>
<lable>ADDRESS</lable>
<div><input type="text"ng-model="user.add"></div>
</div>
<div>
<lable>MOBILE</lable>
<div><input type="number"ng-model="user.number"></div>
</div>
</br>
</br>
<button type="button" ng-click="saw()">Click</button>
</div>
Here is my app.js
var app = angular.module('sapient',['ui.router','dataModule']);
app.config(['$stateProvider','$urlRouterProvider',function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('one', {
url: '/one',
templateUrl: 'view/sap.html',
controller: 'sapCtrl'
})
.state('two', {
url: '/two',
templateUrl: 'view/two.html',
controller: 'secondCtrl'
})
.state('three', {
url: '/three',
templateUrl: 'view/three.html',
controller: 'thirdCtrl'
});
$urlRouterProvider.otherwise('two');
}])
Any Suggestions
Thanks in advance
The problem is you're recreating the module. Remove the [] (array of module dependencies) to make angular retrieve the previously created module, rather than create a new one.
Change:
angular.module('dataModule',[]).controller('thirdCtrl',function(myFact,$scope)
To:
angular.module('dataModule').controller('thirdCtrl',function(myFact,$scope)

Angular state resolve not injecting into controller

I'm trying to get ui-router's resolve to pass its value to the controller portalsForUserCtrl.
Here is the router:
(function () {
'use strict';
var myApp = angular.module("myApp", ["common.services", "ui.router", 'ngMessages']);
myApp.config(["$stateProvider", "$urlRouterProvider",
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state("portalsForUser", {
url: "/userPortal/portalsForUser/:id",
templateUrl: "app/userPortal/portalsForUser.html",
controller: "portalsForUserCtrl as vm",
resolve: {
userPortalService: "userPortalService",
portalsForUser: function (userPortalService, $stateParams) {
var userId = $stateParams.id;
console.log(userId); //shows userId correctly
return userPortalService.getPortalsForUserPromise(userId)
.then(function (response) {
var userPortals = response.data;
console.log("userPortals", userPortals); //shows portals
return userPortals;
});
}
}
})
}]
);
Here is the entire controller:
(function () {
"use strict";
angular.module("myApp")
.controller("portalsForUserCtrl", portalsForUserCtrl);
portalsForUserCtrl.$inject = ['portalsForUser', 'userPortalService'];
function portalsForUserCtrl(portalsForUser, userPortalService) {
console.log("in portalsForUserCtrl");
var vm = this;
vm.portalsForUser = portalsForUser;
console.log(portalsForUser);
}
}());
In mainCtrl, which is the controller for index.html, I call:
$state.go("portalsForUser", ({ "id": userId }));
Here is the code for the view app/userPortal/portalsForUser.html:
<div class="container">
<table class="table table-condensed table-striped table-bordered">
<tbody>
<tr>
<th class="col-md-2"> </th>
<th class="col-md-4">
Portal Name
</th>
</tr>
<tr ng-repeat="userPortal in vm.portalsForUser">
<td>
{{userPortal.portal.portalName}}
</td>
<td class="">
<a class="btn btn-primary" ui-sref="goSomewhere({id: userPortal.portal.id})">
Go
</a>
</td>
</tr>
</tbody>
</table>
Here is the code for the userPortalService:
(function () {
"use strict";
angular.module("myApp")
.service('userPortalService', userPortalService);
userPortalService.$inject = ['userPortalResource', '$http', 'appSettings']
function userPortalService(userPortalResource, $http, appSettings) {
var getPortalsForUserPromise = function (id) {
return $http.get(appSettings.serverPath + '/api/UserPortal/GetPortalsForUser/' + id);
};
return {
getPortalsForUserPromise: getPortalsForUserPromise
};
}
}());
The url changes to the correct /userPortal/portalsForUser/:id but the portalsForUserCtrl function does not fire. It is only when I hit enter on the same url that portalsForUserCtrl is instantiated and the data appears in the view. What am I missing?
You have a syntax error in the $state.go statement.
Change this:
$state.go("portalsForUser", ({ "id": userId }));.
to this:
$state.go("portalsForUser", { "id": userId });
On the documentation (https://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$stateProvider) the specification of the method mentions the following:
The map object is:
key - {string}: name of dependency to be injected into controller
factory - {string|function}: If string then it is alias for service.
Otherwise if function, it is injected and return value it treated as
dependency. If result is a promise, it is resolved before its value is
injected into controller.
with the following as an example:
resolve: {
myResolve1:
function($http, $stateParams) {
return $http.get("/api/foos/"+stateParams.fooID);
}
}
So I suggest you change your code into one of these options to make it as simple a possible and, using chrome developer tool, place a breakpoint on the first line on the method:
resolve: {
portalsForUser: ['userPortalService', '$stateParams', function (userPortalService, $stateParams) {
var userId = $stateParams.id; //place your breakpoint here
return userPortalService.getPortalsForUserPromise(userId);
}]
}
Check what is going on with $stateParams; it is not impossible that, for some reason, at this moment, everything is not initialized yet because values don't come from the url, therefore, the id property is undefined. Try to inject "$state" and see if $state.params.id contains what you expect instead. (like mentioned in here: https://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$state).
Here is what it could look like:
resolve: {
portalsForUser: ['userPortalService', '$state', function (userPortalService, $state) {
var userId = $state.params.id; //place your breakpoint here
return userPortalService.getPortalsForUserPromise(userId);
}]
}
Hope that if it doesn't solve your problem, at least it will help you to find it.
EDIT:
It seems all the previous doesn't go to the right direction.
Here is my new direction:
I used your plunker to create a hosted site on my computer (using http-server: https://www.npmjs.com/package/http-server). My version that doesn't seem to be very different than yours works perfectly. Here is the full code:
app.js:
(function () {
'use strict';
var myApp = angular.module("myApp", ["ui.router"]);
myApp
.config(config)
.controller("portalsForUserCtrl", portalsForUserCtrl)
.service('userPortalService', userPortalService)
.controller("mainCtrl", mainCtrl)
mainCtrl.$inject = ["userPortalService", "$state"];
function mainCtrl(userPortalService, $state) {
var vm = this;
vm.clickMe = function () {
var userId = 1;
$state.go("portalsForUser", { "id": userId });
}
};
config.$inject=["$stateProvider"];
function config($stateProvider) {
$stateProvider
// PortalsForUser GET
.state("portalsForUser", {
url: "/userPortal/portalsForUser/:id",
templateUrl: "portalsForUser.html",
controller: "portalsForUserCtrl as vm",
resolve: {
portalsForUser: ['userPortalService', '$stateParams', function (userPortalService, $stateParams) {
return userPortalService.getPortalsForUserPromise($stateParams.id).then(function(response){return response.data;});
}]
}
})
}
userPortalService.$inject = ['$http', '$q', '$timeout']
function userPortalService($http, $q, $timeout) {
var getPortalsForUserPromise = function (id) {
var myId=id;
var deferred=$q.defer();
$timeout(function(){
deferred.resolve({data:[
{
id: 16,
portal: {
portalName: "Portal1-" + myId,
portalId: 1
}
},
{
id: 17,
portal: {
portalName: "Portal2-" + myId,
portalId: 2
}
}
]});
},5000);
return deferred.promise;
};
return {
getPortalsForUserPromise: getPortalsForUserPromise
};
};
portalsForUserCtrl.$inject = ['portalsForUser', 'userPortalService'];
function portalsForUserCtrl(portalsForUser, userPortalService) {
console.log("in portalsForUserCtrl");
var vm = this;
vm.portalsForUser = portalsForUser;
console.log(portalsForUser);
};
}());
index.html:
<html>
<head></head>
</html>
<body ng-app="myApp">
<!-- bower:js -->
<script src="/bower_components/angular/angular.js"></script>
<script src="/bower_components/angular-ui-router/release/angular-ui-router.js"></script>
<!-- endbower -->
<!-- inject:js -->
<script src="app.js"></script>
<!-- endinject -->
<body ng-app="myApp" ng-controller="mainCtrl as vm">
<button type="submit" class="btn btn-default" ng-click="vm.clickMe()">
Click Me
</button>
<div ui-view></div>
</body>
</body>
portalsForUser.html:
<div class="container">
Portals For User
<table class="table table-condensed table-striped table-bordered">
<tbody>
<tr>
<th class="col-md-2"> </th>
<th class="col-md-4">
Portal Name
</th>
</tr>
<tr ng-repeat="userPortal in vm.portalsForUser">
<td>
{{userPortal.portal.portalName}}
</td>
<td class="">
<a class="btn btn-primary" ui-sref="goSomewhere({id: userPortal.portal.id})">
Go
</a>
</td>
</tr>
</tbody>
</table>
</div>
bower.json
{
"name": "test",
"description": "just a test",
"main": "index.js",
"authors": [
"me"
],
"license": "ISC",
"homepage": "index.html",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"angular": "^1.5.8",
"angular-ui-router": "ui-router#^0.3.1"
}
}
I added the div ui-view in index.html like suggested by somebody else, but I believe this was already in your initial project.
I also tried to simulate the service like the real one would work (with a promise and with a property data).
Are you sure you have correct versions of ui-router and angular?
There might be an issue with dependency injection. Try this -
resolve: {
portalsForUser: ['userPortalService', '$stateParams', function (userPortalService, $stateParams) {
var userId = $stateParams.id;
return userPortalService.getPortalsForUserPromise(userId)
.then(function (response) {
var userPortals = response.data;
console.log("userPortals", userPortals);
return userPortals;
});
}]
}
Based in your code, I've seen that the your controller it's associated to the module clubSkedApp and your config it's associated to the myApp module.
Use the same module for both, or include the module of your controller like this.
var myApp = angular.module("myApp", ["clubSkedApp","common.services", "ui.router", 'ngMessages']);
Another approach is check why the state it's not loaded.
Ui-router isn't good to raise errors, the only way that i find to check the errors in a route's change is the following:
myApp.run(runFn);
runFn.$inject = ['$rootScope'];
function runFn($rootScope){
//Show the errores caused by the resolve function
$rootScope.$on('$stateChangeError', function (event, toState, toParams,
fromState, fromParams, error) {
event.preventDefault();
console.log(error);
});
}
I know the problem. The solution is very simple.
You need to add <div ui-view></div> into index.html to display your view in the later state like below code.
<body ng-app="myApp" ng-controller="mainCtrl as vm">
<button type="submit" class="btn btn-default" ng-click="vm.clickMe()">
Click Me
</button>
<div ui-view></div>
</body>
For more detail
Check the doc from UI-Router https://angular-ui.github.io/ui-router/
Check an example from my friend:
http://codepen.io/trungk18/pen/EgYyJd

AngularJS ng-controller directive does not accept variable (scope function) from javascript, does not give any error either

I am relatively new to angularJS, I am trying to set up a page where inturn multiple pages are called depending upon the selection made previously.
All the pages have their own controller, so I am trying to set the controller and view src through the javascript and using them in HTML tags.
Following is what I am doing:
HTML page:
<div ng-if="sidebarName=='sidebar-device-wire'">
<div ng-controller="getSidebarCtlr">
<div ng-include src="sidebarSrc"></div>
</div>
</div>
javascript:
$scope.sidebarSrc="views/sidebars/sidebar-device.html";
$scope.sidebarCtlr="SidebarDeviceCtrl";
$scope.getSidebarCtlr = function(){return $scope.sidebarCtlr;}
For some reason though, this does not work. i can get the HTML page but the controller is not being called. Can anyone please tell me what I am doing wrong?
I would also recommend to use ngRoute or ui.router because there are many features that aren't easy to implement from scratch (like named views, nested views / nested states or resolves) and these modules are well tested.
Not sure why your controller isn't running but I guess that the expression of the controller is evaluated before your controller that is setting the name is running. So it will be always undefined at compile time.
But if you really like to implement a very basic router you could do it like in the following demo (or in this fiddle).
Update 21.12.2015
Here are some router examples that I wrote for other SO questions:
simple ui.router example - jsfiddle
more complex nested state example ui.router - jsfiddle
dynamic link list with ngRoute - jsfiddle
Please also have a look at ui.router github pages to learn more about it.
angular.module('simpleRouter', [])
.directive('simpleView', simpleViewDirective)
.provider('simpleRoutes', SimpleRoutesProvider)
.controller('MainController', MainController)
.controller('HomeController', HomeController)
.config(function(simpleRoutesProvider) {
simpleRoutesProvider.state([{
url: '/',
templateUrl: 'home.html',
controller: 'HomeController'
}, {
url: '/view1',
templateUrl: 'view1.html'
}, {
url: '/view2',
templateUrl: 'view2.html',
controller: function($scope) {
$scope.test = 'hello from controller'
}
}]);
simpleRoutesProvider.otherwise('/');
})
function HomeController($scope) {
$scope.hello = 'hello from home controller!!';
console.log('home controller started')
}
function MainController($scope) {
$scope.hello = 'Main controller test';
}
function simpleViewDirective() {
return {
restrict: 'EA',
scope: {},
template: '<div ng-include="templateUrl"></div>',
controller: function($scope, $location, $controller, simpleRoutes) {
var childControllerInst;
$scope.templateUrl = simpleRoutes.currentRoute.templateUrl || simpleRoutes.otherwise.templateUrl;
$scope.$watch(function() {
return $location.path();
}, function(newUrl) {
//console.log(newUrl)
$scope.templateUrl = simpleRoutes.changeRoute(newUrl);
childControllerInst = $controller(simpleRoutes.currentRoute.controller || function() {}, {$scope: $scope});
});
$scope.$on('$destroy', function() {
childControllerInst = undefined;
})
},
link: function(scope, element, attrs) {
}
}
}
function SimpleRoutesProvider() {
var router = {
currentRoute: {
templateUrl: ''
},
states: [],
otherwise: {},
changeRoute: function(url) {
var found = false;
angular.forEach(router.states, function(state) {
//console.log('state', state);
if (state.url == url) {
router.currentRoute = state;
found = true;
}
});
if (!found) router.currentRoute = router.otherwise;
//console.log(router.currentRoute);
return router.currentRoute.templateUrl;
}
};
this.state = function(stateObj) {
router.states = stateObj;
};
this.otherwise = function(route) {
angular.forEach(router.states, function(state) {
if (route === state.url ) {
router.otherwise = state;
}
});
//console.log(router.otherwise);
};
this.$get = function simpleRoutesFactory() {
return router;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="simpleRouter" ng-controller="MainController">
<script type="text/ng-template" id="home.html">home route {{hello}}</script>
<script type="text/ng-template" id="view1.html">view1</script>
<script type="text/ng-template" id="view2.html">view2 {{test}}</script>
<div simple-view="">
</div>
home
view1
view2
<br/>
{{hello}}
</div>
What's that code means? $scope.getSidebarCtlr = function(){return $scope.sidebarCtlr;}
the ng-directive requires a Controller name, its argument type is string and you cannot pass a simple function, you need to register a valid controller associating it to a module via the controller recipe.
https://docs.angularjs.org/guide/controller
angular.module('test', []).controller('TestCtrl', function($scope) {
$scope.greetings = "Hello World";
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<section ng-app="test">
<article ng-controller="TestCtrl">{{ greetings }}</article>
</section>

data-ng-repeat in partial view does not display data

This is my module.js
var app = angular.module("ApplicationModule", ["ngRoute"]);
app.factory("ShareData", function () {
return { value: 0 }
});
//Showing Routing
app.config(['$routeProvider', '$locationProvider', function
($routeProvider, $locationProvider) {
debugger;
$routeProvider.when('/ShowAll',
{
templateUrl: 'Home/ShowAll',
controller: 'ShowAllController'
});
$routeProvider.otherwise(
{
redirectTo: '/'
});
}]);
This is my Services.js
app.service("SampleService", function ($http) {
this.getSamples = function () {
return $http.get("/api/Sample");
};
this.getSample = function (id) {
return $http.get("/api/Sample" + id);
};
});
This is my ShowAllController.js
app.Controller('ShowAllController', function ($scope,
SampleService) {
loadAll();
function loadAll() {
var promiseGet = SampleService.getSamples();
promiseGet.success(function (data) { $scope.Samples = data
},
function (errordata) {
$scope.error = errordata;
}
);
}
});
This is my index.cshtml
#{
ViewBag.Title = "API's";
}
#section scripts{
<script src="~/Scripts/angular.js"></script>
<script src="~/Scripts/angular-route.min.js"></script>
<script src="~/Scripts/Module.js"></script>
<script src="~/Scripts/Services.js"></script>
<script src="~/Scripts/ShowAllController.js"></script>
}
<div class="container" data-ng-app="ApplicationModule">
<div class="panel panel-default">
<div class="panel-header">
<div class="row">
<div><h4 class="col-xs-6">GET api/Patient</h4>Patients List</div>
</div></div>
<div class="panel-body" data-ng-view>
</div>
</div>
This is my partial view (ShowAll.cshtml)
<div data-ng-controller="ShowAllController">
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Age</th>
<th>Address</th>
</tr>
<tr data-ng-repeat="person in Samples">
<td>{{person.Id}}</td>
<td>{{person.Name}}</td>
<td>{{person.Age}}</td>
<td>{{person.Address}}</td>
</tr>
</table>
</div>
My return PartialView code is in my HomeController inside the ShowAll ActionResult. The problem is that when I click the button it just displays the header of the table without the data.
Note: This is a web api.
Thank you.
Add the following statement to your ShowAllController before the ajax call so Angular is aware of this scope var in Config time
app.Controller('ShowAllController', function ($scope, SampleService) {
$scope.Samples = []; // <- NEW LINE HERE!
loadAll();
function loadAll() {
var promiseGet = SampleService.getSamples();
promiseGet.success(function (data) { $scope.Samples = data
},
function (errordata) {
$scope.error = errordata;
}
);
}
});
Angular Dependency Injection might not be setting up the scope var Samples in Config time. Then in Run time, it won't recognize this var as something that should be updated.
If that doesn't work, also try to wrap the success result of of your promise into $scope.$apply() as angular might not be aware of it when running its digest function.
$scope.$apply(function () {
$scope.Samples = data;
})

AngularJS Routing problems with view

I'm trying to create a sigle-page app that contains shop list, in every shop card is the link to another view that contains table with products.
A shop looks like:
shop = {
id: 1,
name: "foo",
description: 'bar',
products: [item1, itemn];
};
app.js:
angular
.module('lightpointTestApp', [
'ngCookies',
'ngRoute',
'ui.sortable'
])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.when('/about', {
templateUrl: 'views/about.html',
controller: 'AboutCtrl'
})
.when('/products/:shopID', {
templateUrl: 'views/products.html',
controller: 'ProductsCtrl'
})
.otherwise({
redirectTo: '/'
});
});
Main.html view where are shop list:
<h3>Shop list</h3>
<div class="row shopsContainer" ui-sortable ng-model="shops">
<div class="col-lg-3 shopCard" ng-repeat="shop in shops">
<button class="btn close cardClose" ng-click="removeShop($index)">×</button>
<div class="cardNumber">{{ shops.indexOf(shop) + 1 }}</div>
<div class="cardHeader">{{ shop.name }}</div>
<div class="cardBody">
{{ shop.address }}<br />
{{ shop.hours }}<br />
View {{ shop.products.length }} products
</div>
</div>
</div>
<div class="row">
<input type="text" ng-model="newShop.name" placeholder="Shop name" class="col-lg-3" />
<input type="text" ng-model="newShop.address" placeholder="Shop address" class="col-lg-3" />
<input type="text" ng-model="newShop.hours" placeholder="Shop hours" class="col-lg-3" />
<button class="btn btn-primary col-lg-3" type="button" ng-disabled="!newShop.name || !newShop.address || !newShop.hours" ng-click="addShop()">Add Shop</button>
</div>
</span>
</div>
</div>
products.js - controller for products page
angular.module('lightpointTestApp')
.controller('ProductsCtrl', function ($scope, $routeParams, shops) {
$scope.shopList = shops;
$scope.shop = {};
$scope.getShop = function (id) {
for (var i = 0; i < $scope.shopList.length; i++) {
if ($scope.shopList[i].id === id) {
return $scope.shopList[i];
}
}
return null;
};
var shopID = $routeParams.shopID;
$scope.shop = $scope.getShop(shopID);
})
products.html where is the table with products
<h2>{{ shop.name }}</h2>
<table class="table table-hover">
<tr>
<th>Product Name</th>
<th>Product Description</th>
</tr>
<tr ng-repeat="product in shop.products">
<td> {{ product.name }} </td>
<td> {{ product.description }} </td>
</tr>
</table>
The problem is that products.html doesn't bind with products.js and show something like {{shop.name}} and an empty table.
P.S. I think that products.js isn't correct, but I tried everything to do it well.
Thanks.
You have a parameter shops in ProductsCtrl, but there is nothing that will pass a value for it, so it is going to be null. You set the value of $scope.shopList to it, and then try to iterate over a NULL array, so you get an exception.
You can store the values of shops in a service, and then pass them around your app via injection. You can initialize their values within main.js, or within the service itself, and then the values will be available if you inject them into ProductsCtrl, something like
angular.module('lightpointTestApp')
.controller('ProductsCtrl', ['$scope', '$routeParams', 'shopsService',
function ($scope, $routeParams, shopsService) {
$scope.shopList = shopService;
$scope.shop = {};
$scope.getShop = function (id) {
for (var i = 0; i < $scope.shopList.length; i++) {
if ($scope.shopList[i].id === id) {
return $scope.shopList[i];
}
}
return null;
};
var shopID = $routeParams.shopID;
$scope.shop = $scope.getShop(shopID);
}]);
shopsService could look something like
angular.module('lightpointTestApp')
.service('shopsService', function() {
return [
// add whatever fields you need here from code in main.js
{ name: 'shop1', address: 'addr1' },
{ name: 'shop2', address: 'addr2' }
];
});
Where are your shop objects coming from? You are passing in shop, in products.js but not referencing it in the code. You should also use $q to use promises for async data. Also use the filter() function rather than a for loop to find the shop by shopId.
Are you hitting an API with shops or storing a local json for now?
With angular, you should separate your data logic manipulation in a factory or service as such:
productService.js
angular.module('lightpointTestApp')
.factory('shopService',function($http, $q){
var shops = [];
return {
getShops: function () {
var deferred = $q.defer();
$http.get('<path to product.json or api>').success(function(data){
shops = data;
deferred.resolve(data);
})
return deferred.promise;
},
getShopById: function(shopID) {
var deferred = $q.defer();
deferred.resolve(shops.filter(function(chain){
return chain.id === shopID;
})[0]);
return deferred.promise;
}
}
});
product.js
angular.module('lightpointTestApp')
.controller('ProductsCtrl', function ($scope, $routeParams, $q,shopService) {
$scope.shopList = [];
$scope.shop = {};
var shopID = $routeParams.shopID;
shopService.getShops.then(function(shops){
$scope.shopList = data;
})
$scope.getShopById = function(shopID) {
shopService.getShopById(shopID).then(function(shop){
$scope.shop = shop;
});
}
});

Categories

Resources