I have a controller (called "catalogueController") that manages my search box and my search page. I have the controller initially set the page to automatically call the search function defined in "catalogueController" when the app loads to pre-load my array of items (called Inventory) to be repeated via ng-repeat in the page.
The process runs like this:
1. I submit the search form.
2. "catalogueController" will send the search term to my factory (called "Search").
3. "Search" will have a function which will make a server call to query my database for that particular search.
4. The database will send the results of the search to the "Search" factory.
5. The "Search" factory will send the results to the "catalogueController" controller.
6. "catalogueController" will update the $scope.Inventory to be equal to the new result that I was received.
My problem is that ng-repeat does not refresh itself to display my new and updated $scope.Inventory array. $scope.Inventory definitely is updated (I have made sure of this through various console logs).
I have also tried to use $scope.$apply(). It did not work for me.
Thank you in advance for your help!
Here is my code:
HTML Template
<form role="search" class="navbar-form navbar-left" ng-controller="catalogueController" ng-submit="search(search_term)">
<div class="form-group">
<input type="text" placeholder="Search" class="form-control" ng-model="search_term">
</div>
<button type="submit" class="btn btn-default">Search</button>
</form>
<main ng-view></main>
catalogue.html partial
<div id="main" class="margin-top-50 clearfix container">
<div ng-repeat="items in inventory" class="row-fluid">
<div class="col-sm-6 col-md-3">
<div class="thumbnail"><img src="image.jpg" alt="..." class="col-md-12">
<div class="caption">
<h3>{{ items.itemName }}</h3>
<p>{{ items.description }}</p>
<p>Buy <a href="#" role="button" class="btn btn-default">More Info</a></p>
</div>
</div>
</div>
</div>
"app.js" Angular App
var myApp = angular.module('qcbApp', ['ngRoute', 'ngCookies', 'appControllers']);
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/login', {
templateUrl: 'html/partials/login.html',
controller: 'registrationController'
}).
when('/sign-up', {
templateUrl: 'html/partials/sign-up.html',
controller: 'registrationController'
}).
when('/catalogue', {
templateUrl: 'html/partials/catalogue.html',
controller: 'catalogueController'
}).
when('/', {
templateUrl: 'html/partials/qcbhome.html'
}).
otherwise({
redirectTo: '/'
});
}]);
"catalogueController" Controller
myApp.controller('catalogueController', ['$scope', 'Search', function($scope, Search) {
var time = 0;
var searchCatalogue = function(search) {
$scope.inventory = null;
console.log("Controller -- "+search);
Search.searchCatalogue(search)
.then(function(results) {
console.log(results);
$scope.inventory = results;
});
};
if(time == 0)
{
searchCatalogue('');
time++;
}
$scope.search = function(term) {
searchCatalogue(term);
}
}]);
"Search" Factory
myApp.factory('Search', ['$http', '$q', function($http, $q) {
function searchCatalogue(term) {
var deferred = $q.defer();
console.log("Factory -- "+term);
$http.post('/catalogue_data', {term: term}, {headers: {'Content-Type': 'application/json'}})
.success(function(result) {
console.log(result[0].SKU);
deferred.resolve(result);
console.log("Factory results -- "+result);
});
return deferred.promise;
}
return {
searchCatalogue: searchCatalogue
}; //return
}]);
I think the problem is the ng-repeat can not access the inventory in scope. You have to create a div which contains both the form and the ng-repeat.
The html should be:
<div ng-controller="catalogueController">
<!-- Move the controller from the form to parent div -->
<form role="search" class="navbar-form navbar-left" ng-submit="search(search_term)">
<div class="form-group">
<input type="text" placeholder="Search" class="form-control" ng-model="search_term">
</div>
<button type="submit" class="btn btn-default">Search</button>
</form>
<div id="main" class="margin-top-50 clearfix container">
<div ng-repeat="items in inventory" class="row-fluid">
<div class="col-sm-6 col-md-3">
<div class="thumbnail"><img src="image.jpg" alt="..." class="col-md-12">
<div class="caption">
<h3>{{ items.itemName }}</h3>
<p>{{ items.description }}</p>
<p>Buy <a href="#" role="button" class="btn btn-default">More Info</a></p>
</div>
</div>
</div>
</div>
</div>
I've seen the situation a few times where when you are updating a property directly on the $scope object there are interesting problems around databinding to that value (such as inventory). However if you databind to an object property of an object then the databinding works as expected. So for example use a property on $scope. I believe this is a copy by value vs copy by reference issue.
Update all your inventory references as follows
$scope.data.inventory = result;
Also don't forget to update your inventory reference in the html template:
<div ng-repeat="items in data.inventory" class="row-fluid">
Update: I made this plunk to figure it out - http://plnkr.co/edit/0ZLagR?p=preview
I think the primary problem is you have the controller specified twice. I removed it from the form and it started working.
Related
I have tried to parse my JSON data to angular js app but parsing is not working on my wamp server.
When I click the submit button on login page, there is no response. Please advise where I made mistake in my code.
Controller.js:
var app = angular.module("empApp", []);
app.controller("emp", ['$scope', 'empService', function($scope, empService){
$scope.doSearch = function(){
empService.findEmployeeById($scope.searchEmpno, function(r){
$scope.empno = r.empno;
$scope.ename = r.ename;
$scope.edept = r.edept;
$scope.esal = r.esal;
});
};
}]);
app.service("empService", ['$http', '$log', function($http, $log){
this.findEmployeeById = function(empno, cb){
$http({
url: 'http://localhost/',
method: 'GET'
})
.then(function(resp){
cb(resp.data);
}, function(resp){
$log.error('Error occurred');
});
};
}
}]);
app.directive('empDetails', function(){
return {
templateUrl: 'emp-details.html'
}
});
HTML
<body ng-app="empApp">
<div class="container-fluid">
<div ng-controller="emp">
<form class="form-inline">
<div class="form-group">
<label>Enter Employee No:</label>
<input type="text" name="name" class="form-control" ng-model="searchEmpno"/>
</div>
<div>
<button type="button" class="btn btn-primary" ng-click="doSearch()">Click</button>
</div>
</form>
<hr>
<div emp-details ng-if="empno != undefined">
</div>
</div>
</div>
</body>
emp-details.html
<div class="panel panel-primary">
<div class="panel-heading">
<h3>Employee details</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-sm-4"><strong>Employee No</strong></div>
<div class="col-sm-7">{{empno}}</div>
</div>
<div class="row">
<div class="col-sm-4"><strong>Name:</strong></div>
<div class="col-sm-7">{{ename}}</div>
</div>
<div class="row">
<div class="col-sm-4"><strong>Department:</strong></div>
<div class="col-sm-7">{{edept}}</div>
</div>
</div>
</div>
JSON:
{
"employee":{ "empno": 251, "name":"John", "age":30, "edept":"New York" }
}
Most of the issues are related to project structure and file organization.
I would recommend reading johnpapa-angular-styleguide for properly organizing and coding an angularJS application.
There are multiple changes needed.
You're not referencing your Controller.js file.
There is an additional } is there in your service
There is no file like data.json. You will need to create that file and reference it in your service.
$http({
url: 'data.json',
method: 'GET'
})
In your service you are returning resp.data. It should be resp.data.employee.
cb(resp.data.employee);
When you are referencing paths you should use relative URL.
See the working plnkr
Couple of this:
Change your URL from 'http://localhost/' to 'data.json'
In the callback, change resp.data to resp.data.employee. This will make sure you directly get access to the employee data.
Let me know if this helps.
Controller.js
$scope.doSearch = function(employeeN){
empService.findEmployeeById(employeeN)
.then(function(r){
$scope.empno = r.empno;
$scope.ename = r.ename;
$scope.edept = r.edept;
$scope.esal = r.esal;
});
};
Service
this.findEmployeeById = function(empno){
$http({
url: 'http://localhost/data.json',
method: 'GET'
})
.then(function(resp){
return resp.data.employee;
}, function(resp){
$log.error('Error occurred');
});
};
HTML
<div>
<button type="button" class="btn btn-primary" ng-click="doSearch(searchEmpno)">Click</button>
</div>
I am trying to make a UI using list of product in a json(products.json) file on local and their availability in number from wp rest api call back in html(ionic) I have this:
Controller:
.controller('ShopCtrl', function($scope, $ionicActionSheet, BackendService, CartService, $http, $sce ) {
$scope.siteCategories = [];
$scope.cart = CartService.loadCart();
$scope.doRefresh = function(){
BackendService.getProducts()
.success(function(newItems) {
$scope.products = newItems;
console.log($scope.products);
})
.finally(function() {
// Stop the ion-refresher from spinning (not needed in this view)
$scope.$broadcast('scroll.refreshComplete');
});
};
$http.get("http://example.com/wp-json/wp/v2/categories/").then(
function(returnedData){
$scope.siteCategories = returnedData.data;
console.log($scope.siteCategories);
}, function(err){
console.log(err);
});
Template view:
<div ng-repeat = "product in products">
<div class="item item-image" style="position:relative;">
<img ng-src="{{product.image}}">
<div ng-repeat = "siteCategory in siteCategories">-->
<button class="button button-positive product-price" ng-click="addProduct(product)">
<p class="white-color product-price-price">post <b>{{siteCategory[$index].count}}</b></p>
</button>
</div>
</div>
<div class="item ui-gradient-deepblue">
<h2 class="title white-color sans-pro-light">{{product.title}} </h2>
</div>
<div class="item item-body">
{{product.description}}
</div>
</div>
so how can I achieve that? I tried to use nested ng-repeat but it didn't work out.
I'm using AngularJS + ui-router to make some wizard with nested forms and routes.
here is the main form:
<div id="form-container-wizard">
<div class="form-horizontal" role="form">
<form name="addItem_form" ng-submit="submitForm()">
<div class="page-header text-center">
<h2>Post Your Item</h2>
<!-- the links to our nested states using relative paths -->
<!-- add the active class if the state matches our ui-sref -->
<div id="status-buttons-wizard" class="text-center">
<a ng-class="{ disabled: ItemCheckPass }" ui-sref-active="active" ui-sref=".item"> <span></span>Item</a>
<a ng-class="{ disabled: !ItemCheckPass || LocationCheckPass}" ui-sref-active="active" ui-sref=".location"><span></span>Location</a>
<a ng-class="{ disabled: !ItemCheckPass || !LocationCheckPass || AccountCheckPass}"ng-show="!IsAuthenticated" ui-sref-active="active" ui-sref=".account"><span></span>Account</a>
<a ng-class="{ disabled: !ItemCheckPass || !LocationCheckPass || !IsAuthenticated && !AccountCheckPass }"ui-sref-active="active" ui-sref=".social"><span></span>Social</a>
</div>
</div>
<div class="panel panel-default">
<div id="form-views" ui-view></div>
</div>
</form>
</div>
</div> <!-- wizard container -->
here is the routing for the form:
.state('post_add', {
url: '/post_add',
templateUrl: '/view/post_wizard/form.html',
controller: 'postWizardMainController',
abstract:true
})
.state('post_add.item', {
url: '',
templateUrl: '/view/post_wizard/form-item.html',
controller: 'postWizardController'
})
.state('post_add.location', {
url: '/location',
templateUrl: '/view/post_wizard/form-location.html',
controller: 'postWizardController'
})
.state('post_add.account', {
url: '/account',
templateUrl: '/view/post_wizard/form-account.html',
controller: 'postWizardController'
})
.state('post_add.social', {
url: '/social',
templateUrl: '/view/post_wizard/form-social.html',
controller: 'postWizardController'
});
each view contains a partial form and store the form elements values to $scope.AddItem object by using ng-models like <input type="email" class="form-control" name="email" ng-model="AddItem.email" ng-minlength=3 ng-maxlength=30 required>
postWizardMainController used to keep the validation variables and methods.
So, the problem is:
option 1: the code is as listed here, <form name="addItem_form" ng-submit="submitForm()"> is not getting submitted by <input type="submit" ng-disabled="addItem_form.$invalid" class="btn btn-orange" value="Post An Ad"> located in the last of form views.
option 2: i put the submitForm() into the ng-click in the last form, and locate submitForm() function in the postWizardMainController. In this option, the submitForm() function is called, bit no objects are passed into it. $scope.AddItem is undefined.
So, the question is:
How can i submit the form and pass $scope.AddItem object into submission, which must contain the data from all the nested forms.
The solution was to declare $scope.AddItem = {} in postWizardMainController
i have a angular application with phonegap where i have a login form and login controller.
The problem is that the ng-submit doesn't work. Normaly the submit call the fonction formConnexion() but nothing happens. So, i tried with just a alert, but it's the same result...
After i tried with a ng-click and same result. Then, i wanted try to call a sample variable in the scope in the template ($scope.test) and it doesn't display. And the console.log('salut!!') doesn't dispaly when i am on the login page.
Which made me think that my controller it doesn't associate to the template.
But i have the ng-controller in my div with the good name controller.
Then, i thought that angular it's was worse installing but no because the other directives work (ng-if, ng-src, ...)
So, if you have a idea to solve this problem, I do not mind :)
here the code for the template :
login.html
<div class="row jumbotron" style="margin:10px" ng-controller="LoginCtrl">
<div class="col-lg-8 col-lg-offset-2 col-md-8 col-md-offset-2 col-sm-8 col-sm-offset-2 col-xs-8 col-xs-offset-2">
<h1>{{ test }}</h1>
<form class="form-horizontal" ng-submit="alert('alert')">
<div class="form-group">
<label for="login" class="control-label">Login</label>
<input type="text" name="login" class="form-control" ng-model="formData.login">
</div>
<div class="form-group">
<label for="password" class="control-label">Password</label>
<input type="password" name="password" class="form-control" ng-model="formData.password">
</div>
<button type="submit" class="btn btn-md btn-success">Connexion</button>
</form>
click
<img ng-src="images/accueil.png" class="accueil img-responsive">
</div>
</div>
and here the controller :
login.js
'use strict';
appStat.controller('LoginCtrl', function ($scope, $rootScope, $http, $location) {
console.log("salut!!");
$scope.formData = {};
$scope.test = "test";
$scope.toto = function() { alert('alert'); };
/**
* Connect l'user
*/
$scope.formConnexion = function () {...}
});
and here my app.js :
app.js
'use strict';
var appStat = angular.module('posStatsPhoneGap', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute',
'ngResource',
'ui.bootstrap',
'ngMessages',
'ngAnimate',
'ngAria',
'ngTouch',
'picardy.fontawesome'
])
.config(['$routeProvider', '$compileProvider', function ($routeProvider, $compileProvider) {
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|file|tel):/);
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|file|tel):/);
$routeProvider
.when('/', {
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
})
.when('/main', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.when('/stat', {
templateUrl: 'views/stat.html',
controller: 'StatCtrl'
})
.otherwise({
redirectTo: '/'
});
}
]);
Thank you in advance !
Following from my comment Try something like this:
Create a Controller file & a controller within it Create a angular
module within the controller file
Create a controller.js file then create a angular module see code below:
//Within your controller.js file
var myApp = angular.module('myApp', []);
myApp.controller('myController', function($scope, $http){
$scope.Data = "Alert Alert";
// Create a function within the scope of the module
$scope.myFunc = function(){
alert($scope.Data)
};
});
Then within your HTML file call the function on-Click see code below:
Note: Don't forget to add the controller you created into your HTML code. I usually add it in the body tag
<body ng-controller="myController">
<button ng-click="myFunc()">Make Request Button</button>
</body>
See a live example here:
http://plnkr.co/edit/sQ0z7TlyWv5fM5XyfujK?p=preview
Also a note: ng-click is mostly used for any click events you are trying to perform within your angular app but ng-submit is mostly used when working with a HTML form submission.
Use ng-submit in you form as:
<form ng-submit="submit()">
function Ctrl($scope) {
$scope.list = [];
$scope.text = 'hello';
$scope.submit = function () {
if ($scope.text) {
$scope.list.push($scope.text);
$scope.text = '';
}
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app>
<div ng-controller="Ctrl">
<form ng-submit="submit()">Enter text and hit enter:
<input type="text" ng-model="text" name="text" />
<input type="submit" id="submit" value="Submit" /> <pre>list={{list}}</pre>
</form>
<button ng-click="submit()">Submit 2</button>
</div>
</div>
I found the error. It was caused by having 2 controllers with the name 'LoginCtrl'. It was a stupid copy/paste error.
I have created one working code for the above question for 4 different type of HTML element, for complete code please follow the below URL -
http://plnkr.co/edit/Tm2Rtbt3xsv4pKzcPQCw?p=preview
<body ng-controller="ExampleController">
<button ng-click="setButton()">set button</button>
<div content="snippetContentFirst"></div>
<div content="snippetContentSecond"></div>
<div content="snippetContentThird"></div>
<div>
<ul content="snippetContentFour"></ul>
</div>
</body>
I am building a blog with angular and am currently working on the routes. I have the routes working with routeParams but angular gives it a random number and the url ends like http://localhost/kensproblems/#/posts/2
I want it to either automatically grab the title and use it as the parameter on the URL or use the property called id on the json file such as http://localhost/kensproblems/#/posts/title-of-post
Is this possible with $routeParams?
app.js
angular.module('app', [
'ngRoute',
'app.controllers',
'ui.router',
'ngSanitize'
])
.config(['$routeProvider', '$urlRouterProvider', function($routeProvider, $urlRouterProvider){
$routeProvider
.when('/posts', {
templateUrl: 'views/posts.html',
controller: 'PostListController'
})
.when('/posts/:id', {
templateUrl: 'views/singlepost.html',
controller: 'PostDetailController'
})
.otherwise({
redirectTo: '/'
});
}]);
controllers.js
angular.module('app.controllers', ['app.directives'])
/* Controls the Blog */
.controller('PostListController', ['$scope', '$http', function($scope, $http){
$http.get('data/posts.json').success(function(response){
$scope.posts = response;
});
}])
.controller('PostDetailController', ['$scope', '$http', '$routeParams', '$sce', function($scope, $http, $routeParams, $sce){
$http.get('data/posts.json').success(function(response){
$scope.post = response[$routeParams.id];
console.log($routeParams.id);
console.log($scope.post.id);
});
}])
posts.html
<div class="container" id="postList">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div ng-repeat="post in posts | orderBy:post.date" class="post">
<h2 class="blog-title">{{post.title}}</h2>
<span class="date">Posted on {{post.date | date:'MMM dd, yyyy'}}</span>
<div class="row top10">
<div class="col-md-8">
<div class="post-content">{{post.headline}}</div>
<a class="read-more" href="#/posts/{{posts.indexOf(post)}}">Read more</a>
</div>
</div>
</div>
</div>
</div>
</div>
singlepost.html
<div class="container" id="singlePost">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<h1>{{post.id}}</h1>
<h1>{{post.title}}</h1>
<span class="date">Posted on {{post.date | date:'MMM dd, yyyy'}}</span>
<h3>{{post.headline}}</h3>
<div class="postContent" ng-bind-html="post.content"></div>
</div>
</div>
</div>
You will need to change two main things, first how the URLs are generated, and second how the post is recovered.
Generate URL from title
Not any title will be valid as URL, so you need to use slugs.
In the view:
<a class="read-more" href="#/posts/{{getSlug(post.title)}}">Read more</a>
In the controller:
$scope.getSlug = function(text){
return text.replace(/\W+/g, '-');
};
Search post from slug
Currently, you are just fetching the post from the array as an index, which is conceptually wrong (you will notice this after remove one post). Instead, make a search through the array. This will be really simple if you use lodash.
Something like this would work:
$scope.post = _.find(response, function(post) {
return post.title.replace(/\W+/g, '-') === $routeParams.id;
});
Stack Overflow won't let me comment yet so I'll post here. I looks like your passing the index of the post in the array instead of one of the properties. Look at changing:
href="#/posts/{{posts.indexOf(post)}}"
To something like:
ng-href="#/posts/{{post.title}}"