I understand how two-way data binding works with AngularJS but I'm struggling to make it work in a situation described below. I've a index.template.html, home.template.html, search.template.html, app.js, search.controller.js and search.service.js files. I've a input form on a home page at '/' url.
<li ng-controller="searchCtrl"> //searchCtrl() in search.controller.js file
<form role="search">
<input type="text" ng-model="searchstring"
placeholder="Enter string here"> <br />
<button type="submit"
ng-click="searchString(searchstring)">Search</button>
</form>
</li>
And I've search.template.html to display the results as follows,
<html>
<body>
<h1> SearchedText</h1>
<h2> {{ searchedtext }} </h2>
</body>
</html>
search.controller.js is as follows,
(function () {
angular
.module('twowaybindapp')
.controller('searchCtrl', searchCtrl);
searchCtrl.$inject = ['$scope', 'SearchService'];
function searchCtrl($scope, SearchService) {
$scope.searchString = function(searchtext) {
SearchService
.getSearchResults(searchtext) //getSearchResults in search.service.js
.success(function(data) {
console.log('Service call is a success with result:');
console.log(data);
$scope.searchedtext = data;
}).error(function(e) {
console.log(e);
});
};
}
})();
My app.js file is as follows,
var myApp = angular.module('twowaybindapp', [ 'ngRoute' ]);
myApp.config(['$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider) {
$routeProvider.
when('/', {
templateUrl: 'home.view.html'
}).
when('/search', {
templateUrl: 'search.view.html',
controller: 'searchCtrl'
}).
otherwise({ redirectTo: '/' });
}]);
With this background, When I've both form and results html text (content of search.template.html) in the same home.template.html, things work fine and I see all the results but if I put them in separate files as above, either I don't know how to open the results in new view after the call or don't know if data-binding is not working. I do see results in console.log() in both cases that mean service call is working okay as expected. This might be silly question but I really need help with this. Would it work if the search form is a part of navigation directive at the top of every page and results needs to be shown below in a main ng-view? Any help is appreciated. Thanks.
-- Atarangp
When you write {{smth}} in html it parsed to $scope.smth, each controller has its own scope object, by default scopes are nested:
<div ng-controller="c1" ng-init="smth = 1">
<div ng-controller="c2">
{{smth}} << 1
</div>
</div>
<div ng-controller="c3">
{{smth}} << undefined
</div>
When you defined several views - they will have different scopes, so you can not see one scope from another.
Fastest way to make it work is change to $rootScope.searchedtext = data; cause all your scopes nests from root scope.
Other way is to make common parent state and access and set your property there.
Yet another ways are: use your own service to store data, pass data using events, pass data using properties in state obj...
Related
so I have a component with a template containing a form.
mycomponent.html:
<div>
<form name="myForm">
<!-- more html code -->
</form>
</div>
How can I access myForm inside the component controller?
Currently I'm injecting $scope to get it from that.
Or is that the only way to get the form?
Edit: Added some code to better illustrate in javascript
angular.module('example')
.component('myComponent', {
templateUrl: 'mycomponent.html',
controller: function($scope) {
$scope.myForm // This works
this.myForm // undefined, can I access it through the component scope instead of $scope somehow?
}
});
The name attribute of a form is what angular uses to decide what to bind to. So, if you're using the controllerAs syntax, you have to use that in the form name:
<body ng-controller="MainCtrl as vm">
<form name='vm.myForm'>
</form>
</body>
This will allow you to refer to it in your controller without using $scope, but only after the controller has been successfully created:
app.controller('MainCtrl', function($scope, $timeout) {
var vm = this;
console.log(vm.myForm); // undefined
$timeout(function() {
console.log(vm.myForm); // FormController object
}, 100);
});
Here is a working plunk.
Use the name syntax but also a components postLink lifecycle hook, that function is called once a template and controller have been connected see https://docs.angularjs.org/guide/component
I load some data from the server, and then update the view using ng-repeat. The user has the ability to edit the data in another page. Now, is the part where I'm a bit confused. I can pass some parameters using $routeProvided combined with $routeParams, lets say id and fetch the data again from the server depending on that id. I believe there's a better way, something like passing the whole object to the next controller, rather than conducting another call the server. That's what I've got working right now:
app.config(function($routeProvider) {
$routeProvider
.when('/editPost/:postId/:author', {
templateUrl: 'editPost.html',
controller: 'editPostCtrl'
});
});
app.controller('editPostCtrl', function($scope, $routeParams) {
console.log($routeParams.author + ", " + $routeParams.postId);
});
Now, I cant extract the object with the following code:
HTML:
<div ng-repeat="post in posts">
<button class="btn" ng-click="editPost(post)">Edit</button>
</div>
JS:
app.config(function($routeProvider) {
$routeProvider
.when('/editPost/:post', {
templateUrl : 'editPost.html',
controller : 'editPostCtrl'
});
});
app.controller('editPostCtrl', function($scope, $routeParams) {
console.log($routeParams.post); //results in [object object]
});
Is there a better way for achieving this?
[EDITED] My app has the following structure:
index.html
<body ng-app = "myApp" ng-controller ="mainController">
<ng-view></ng-view>
</body>
mainView.html (loaded into ng-view through routeProvider in app.js)
<div ng-include src="subview1">
<div ng-include src="subview2">
subview1 and subview2 are set within mainController (mainView's controller) as scope variables:
$scope.subview1= "templates/subview1.html";
$scope.subview2= "templates/subview2.html";
controller1 and controller2 are subview1 and subview2's controllers.
subview1.html (loaded in first div of mainView)
<div ng-controller="controller1">
<button ng-click="loadNewView()"></button>
</div>
controller1.js
.controller('controller1', function($scope){
$scope.loadNewView = function(){
$scope.$parent.subview1 = "templates/view3.html";
}
}
scope.loadNewView should load a different view (and relative controller) within the div with src="subview1" in mainView.html). Basically it's about refreshing the view itself by raplacing it with another view (and related controller).
I use $parent to update the view in subview1's parent view (i.e. mainView).
however nothing happens and if I try to use $scope.$apply() I get error (digest already in progress).
Any clue?
you can try something like this...
In your stateProvider or in your routeProvider if you using.
var mod = angular.module('example.states', ['ui.router']);
mod.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('exampleState', {
url: '/main',
templateUrl: 'mainView.html',
controller: mainController
});
}
]);
return mod;
So here you have associated your parent controller(let's say the mainController which will be the parent of all others) with its template mainView.html.
Then in your mainView.html,Load all the subview templates.
<div ng-repeat="template in templates">
<ng-include src="template.url"></ng-include>
</div>
templates is an array in your mainController which has the url or path of all your subtemplates.When you use ng-include inside the main template then all subTemplates will automaticlly become the child of the mainTemplate and its Controllers too.In a way it will inherit from the parent Controller.
So suppose if subView1.html is one of the template url you had given in ng-include.Then it will look like
<div ng-controller="subView1Controller">
//Here your code
</div>
And subview2 as
<div ng-controller="subView2Controller">
//Here your code
</div>
This way you will have multiple views on the same page with one url and different controllers with its associated templates and each will inherit from the parent controller which is mainController here.
There, might be better approach than this.
This is what i had used in my project,and its simple to keep your code simple manage.
Okay,so using routeProvider,you can use it like this
var app = angular.module("app",[]);
app.config(function($routeProvider){
$routeProvider
.when('/main',{
templateUrl:"mainView.html",
controller:mainController
})
});
app.controller("mainController",function($scope){
});
app.controller("subView1Controller",function($scope){
});
app.controller("subView1Controller",function($scope){
});
Then in your mainView.html,Load all the subview templates.
<ng-include src="yoursubtemplate1path"></ng-include>
<ng-include src="yoursubtemplate2path"></ng-include>
And then in yoursubtemplate1 use
<div ng-controller="subView1Controller">
//Here your code
</div>
Same for the other templates.
You can set the template src of the subtemplates from your mainController.
app.controller("mainController",function($scope){
$scope.templatesrc="/app/template1.html";
});
And then use it in your template,where you are using ng-include directive.
<ng-include src="templatesrc"></ng-include>
Its better to store template url's in an array and use ng-repeat directive like i had stated before,if you are loading more templates.
And if you want to show the div on some button click lets say in parent controller then use ng-if in the sub-view main and make it true on button click.
This answer is regarding your updated question.
The solution which you had used before,will load all temlplate and once in ng-include and its associated controller making the mainController as parent.
But if you want to load a different view with its newController then you can try something like this.
Just add one more route and call on your event click,but remember this newView's Controller will have no parent-child relation with the mainView's controller.
var app = angular.module("app",[]);
app.config(function($routeProvider){
$routeProvider
.when('/main',{
templateUrl:"mainView.html",
controller:mainController
})
.when('/anyName',{
templateUrl:"templates/view3.html",
controller:temp3Controller
})
});
And in your controller1.js
.controller('controller1', function($scope){
$scope.loadNewView = function(){
$location.path('/anyName');
}
}
Inject location service in controller1.
I finally found the solution.
The tricks is using
$scope.$parent.$parent.subview1 = "templates/view3.html";
instead of
$scope.$parent.subview1 = "templates/view3.html";
since, basically:
ng-include is the child of mainView
subview1 is the child of ng-include
I have the following URL:
http://myUrl.com/#/chooseStyle?imgUpload=6_1405794123.jpg
I want to read the imgUpload value in the query string - I'm trying:
alert($location.search().imgUpload);
But nothing alerts, not even a blank alert - but console reads:
$location is not defined
I need this value to add into a controller to pull back data, and also to carry into the view itself as part of a ng-src
Is there anything I'm doing wrong? this is my app config:
capApp.config(function($locationProvider, $routeProvider) {
$locationProvider.html5Mode(false);
$routeProvider
// route for the home page
.when('/', {
templateUrl : '/views/home.html',
controller : 'mainController'
})
// route for the caption it page
.when('/capIt', {
templateUrl : '/views/capIt.html',
controller : 'mainController'
});
}):
This is the view:
<div class="container text-center">
<h1 class="whiteTextShadow text-center top70">Choose your photo</h1>
</div>
<script>
alert($location.search().imgUpload);
</script>
Main controller:
capApp.controller('mainController', function($scope) {
$scope.message = 'Whoop it works!';
});
My end goal is that I can find a solution to capturing and re-using data from the query string.
I will also mention, this is only my first week in Angular, loving it so far! A lot to learn...
<script>
alert($location.search().imgUpload);
</script>
You're making two mistakes here:
executing code while the page is loading, and the angular application is thus not started yet
assuming $location is a global variable. It's not. It's an angular service that must be injected into your controller (or any other angular component). This should cause an exception to be thrown and displayed in your console. Leave your console open always, and don't ignore exception being thrown.
You should not do this
<script>
alert($location.search().imgUpload);
</script>
// you need to inject the module $location
//(either in service, or controller or wherever you want to use it)
// if you want to use their APIs
capApp.controller('mainController', function($scope, $location) {
$scope.message = 'Whoop it works!';
//use API of $location
alert($location.search().imgUpload);
});
I am trying to get a small angularJS app running. The app should receive asynchronous messages via STOMP / Websockets. If a message via the web socket arrives, the angular app is supposed to display a changed value in the UI. In general angular's data binding works as expected, but if the scope is updated within the callback function on_message() nothing happens in the UI. I have read various post on similar topics and tried the suggested solutions like using $apply within the callback function, but without success.
In the debugger I can see that the $scope.SoC gets assigned the correct value, but the UI remains unchanged.
If the function on_message(m) is called directly - just for testing - and not from socket-client, the UI gets updated correctly.
This is the abbreviated structure of the controller code
App.controller('showCaseDataCtrl', function($scope){
var mySoC = 0.7;
$scope.status = {statusMessage: "No Message", SoC: 0.77, power: 5.4, numDevices: 89};
function on_message(m) {
mySoC ++;
$scope.$apply(function() {$scope.status.SoC = mySoC;});
console.log(mySoC);
}
});
This is the HTML
<html ng-app="App">
...
<div class="col-md-4" >
<h1> <span ng-bind="status.SoC" /> SoC</h1>
<p>Charge Status</p>
</div>
...
</html>
Any suggestions what else to try are appreciated.
Update:
The problem has to to with mg-route and a separate view in the main HTML
in the main HTML I am using a statement like
<!-- views selected by the route will be injected here -->
<div ng-view ="" </div>
and the route provider looks like
App.config(function($routeProvider){
$routeProvider
.when('/',
{
controller: 'showCaseDataCtrl',
templateUrl: 'partials/Overview.html'
})
.when('/test',
{
controller: 'showCaseDataCtrl',
templateUrl: 'partials/Overview.html'
})
.otherwise({redirectTo: '/'});
});
Removing the route provider and putting all HTML directly in the main HTML with
<div ng-controller="showCaseDataCtrl" class="col-md-4" >
<h1> <span ng-bind="status.SoC" /> SoC</h1>
<p>Charge Status</p>
</div>
solved the problem.
--- But I have no clue why ---
Any explanation appreciated
The reason the $scope is not updating is because the callback is happening outside of Angular's $digest() cycle. Try wrapping it in $scope.$apply.
function on_message(m) {
// console.log('Received:' + m);
// var MyJSON = $.parseJSON(m.body);
mySoC ++;
$scope.$apply(function() {
$scope.SoC = mySoC;
});
console.log(mySoC);
}
});
Seems like you had it commented out, but that should work.