Editing resolved object is not updating parent/child controller data - angularjs

I am sharing data between parent and child controllers using resolve option available in ui-router.
PFB the code,
var app = angular.module('app',['ngRoute','ui.router']);
app.config(function($stateProvider,$urlRouterProvider){
$urlRouterProvider.otherwise('/first/second');
$stateProvider
.state('first', {
url: '/first',
template: "<div>First Page :: Name - <input type='text' ng-model='parentName'/></div> <div ui-view=''></div>",
resolve: {
name : function(){return 'Dheepan Raju';}
},
controller : function($scope,name){
$scope.parentName = name;
}
})
.state('first.second', {
url: '/second',
template: "<div>Second Page :: Name - <input type='text' ng-model='childName'/></div>",
controller : function($scope,name){
$scope.childName = name;
}
});
});
index.html
<!DOCTYPE html>
<html ng-app='app'>
<head>
<script src="https://code.angularjs.org/1.4.2/angular.js" data-semver="1.4.2" data-require="angular.js#*"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-route.js" data-semver="1.3.15" data-require="ngRoute#1.3.15"></script>
<script data-require="ui-router#*" data-semver="0.2.15" src="//rawgit.com/angular-ui/ui-router/0.2.15/release/angular-ui-router.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div ui-view=""></div>
</body>
</html>
Here is the plunker. When the page is loaded both child and parent input fields are having same name. But if I edit any of those values, it is not updating the other property. Both looks individual. How can I make both the controllers to use same data and if edit that in one controller, other controller is also updated(without any $broadcast or $watch)

This can be accomplished by using a shared service with the controllers. By setting the ng-model of both to use a shared object, you can keep both controllers in sync through the service. The service handles the shared object and the controllers just reference it. No watch or broadcast required.
Updated script.js
app.config(function($stateProvider,$urlRouterProvider){
$urlRouterProvider.otherwise('/first/second');
$stateProvider
.state('first', {
url: '/first',
template: "<div>First Page :: Name - <input type='text' ng-model='parentName.name''/></div> <div ui-view=''>Hell</div>",
resolve: {
name : function(){return 'Dheepan Raju';}
},
controller : function($scope,name, SharedData){
// Init
SharedData.name = name;
$scope.parentName = SharedData;
}
})
.state('first.second', {
url: '/second',
template: "<div>Second Page :: Name - <input type='text' ng-model='childName.name'/></div>",
controller : function($scope, SharedData){
$scope.childName = SharedData;
}
});
}).service("SharedData", function() {
return { name: "" }
});
I have updated your plunker showing this: http://plnkr.co/edit/XIvHXSy6oDuuuKsmE3AF?p=preview

Related

Make two layouts share the same $scope

I want to propose two layouts (ie, horizontal and vertical) for my contents. So switching in the selector will lead automatically to the corresponding layout. Here is the JSBin:
<html ng-app="flapperNews">
<head>
<script src="https://code.jquery.com/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.2/angular-ui-router.js"></script>
<script type="text/ng-template" id="horizontal.tpl">
<textarea ng-model="one"></textarea>, <textarea ng-model="two"></textarea>
<br><br>{{one}}+{{two}}
</script>
<script type="text/ng-template" id="vertical.tpl">
<textarea ng-model="one"></textarea><br><textarea ng-model="two"></textarea>
<br><br>{{one}}+{{two}}
</script>
<script>
var app = angular.module('flapperNews', ['ui.router']);
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('entry', {
url: '/',
params: { tpl: 'vertical' },
templateUrl: function (params) {
return params.tpl + ".tpl"
}
})
}]);
app.controller('MainCtrl', ['$scope', '$state', function ($scope, $state) {
$scope.one = "one";
$scope.two = "two";
$scope.layouts = ["horizontal", "vertical"];
$scope.$watch('layout', function () {
$state.go('entry', {tpl: $scope.layout});
})
}])
</script>
</head>
<body ng-controller="MainCtrl">
<select ng-model="layout" ng-init="layout = 'horizontal' || layouts[0].value" ng-options="x for x in layouts"></select>
<br><br>
<ui-view></ui-view>
</body>
</html>
However, with the above code, each time we change the view, $scope.one and $scope.two are reset to their initial values. I would hope the change in their textarea would remain regardless of the change of layout.
Does anyone know how to solve this?
Easy sharing same data between different views by using factories (AngularJS factory documentation). Try this example, it uses a simple factory named myFactory to share data between controllers. This also does work on the same controller as in your case.
var myApp = angular.module("myApp",[ "ui.router"]);
myApp.config(function ($stateProvider, $urlRouterProvider){
$stateProvider.state("state1", {
url: "#",
template: '<p>{{ aValue }}</p><button ng-click="bindValue(\'its me\')">Bind value</button>',
controller: "myController"
}).state("state2", {
url: "#",
template: '<p>{{ aValue }}</p><button ng-click="bindValue(\'its me\')">Bind value</button>',
controller: "myController"
});
});
myApp.controller( "myController", function($scope, myFactory) {
$scope.aValue = myFactory.myValue;
$scope.bindValue = function (value) {
$scope.aValue = value;
myFactory.myValue = value;
}
});
myApp.factory('myFactory', function () {
return {
myValue: ''
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.8/angular-ui-router.min.js"></script>
<div ng-app="myApp">
<nav>
<a ui-sref="state1">State 1</a>
<a ui-sref="state2">State 2</a>
</nav>
<div ui-view></div>
</div>
I think that you should use nested views - you can define main controller on parent route state and define two nested states corresponding to two views. This way parent controller will remain (it's not re-initialised when child states are switched) and only nested states views will be changed. Something like this:
$stateProvider
.state('myState', {
url: '/test/',
template: '<div ui-view></div>',
controller: function() {
//main controller shared by child states
...
}
})
.state('myState.view1', {
url: '/test/view1'
templateUrl: 'tpl-1.hmtl',
...
})
.state('myState.view2', {
url: '/test/view2'
templateUrl: 'tpl-2.hmtl',
...
})

Angular add dynamic html(directive) with object as parameter

Doesn't sound like it would be a big deal but I don't know what to google for to solve this. I want to be able to fetch an object with $http and then render that info with the help of a directive.
JS:
angular.module("test", []);
angular.module("test").directive('myTest', function() {
return {
templateUrl: 'myTest.html'
};
});
angular.module("test").controller("myCtrl", function($http, $compile){
var vm = this;
vm.name = "Viktor";
vm.country = "Sweden";
vm.origin = "controller";
vm.click = function(){
$http.get("data"+Math.floor((Math.random() * 2) + 1)+".json").success(function(data){
$("body").append($compile("<my-test></my-test>")(data));
})
}
})
Template:
<div>
<div>My name is: {{vm.name}}</div>
<div>I live in : {{vm.country}}</div>
<div>Source origin : {{vm.origin}}</div>
</div>
index.html
<!DOCTYPE html>
<html ng-app="test">
<head>
<script data-require="jquery#*" data-semver="2.1.4" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script data-require="angular.js#*" data-semver="1.4.7" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="app.js"></script>
</head>
<body ng-controller="myCtrl as vm">
<button ng-click="vm.click()">Add panel</button>
<my-test></my-test>
</body>
</html>
Plunker: http://embed.plnkr.co/YcG9ZFxuR3PYBYASjzm2/preview
I just can't edit your plnkr file.
But bindToController should help you. It should be somethind like this, I create a controller for the directive :
angular.module("test").directive('myTest', function() {
return {
restrict: 'E',
scope: {},
templateUrl: 'myTest.html',
controller: function(){},
controllerAs: 'ctrl',
bindToController: {
name: '=',
country: '=',
origin: '='}
}
});
Also I change the alias of the controller in the template :
<div>
<div>My name is: {{ctrl.name}}</div>
<div>I live in : {{ctrl.country}}</div>
<div>Source origin : {{ctrl.origin}}</div>
<input ng-model="ctrl.name"/>
</div>
Here is a working example modified on your plunkr : plunkr.
I am still stuck in the jQuery way of thinking, I guess I found out how to do it the Angular way. Just to add the newly fetched item to a list and then render the list. Not what I was originally aiming for but that is probably the prettiest way.
And if one now HAVE to add new items instead of redrawing the whole list, I guess one could send in the object as a json-string or set scope variables on the directive for each parameter in the object.
Plunker: http://plnkr.co/edit/5ElTIx?p=preview
Html:
<body ng-controller="myCtrl as vm">
<button ng-click="vm.click()">Add panel</button>
<my-test ng-repeat="panel in vm.panels" obj="panel"></my-test>
</body>
Js:
angular.module("test").controller("myCtrl", function($http, $compile){
var vm = this;
vm.name = "Viktor";
vm.country = "Sweden";
vm.origin = "controller";
vm.panels = [{
name:"Viktor2",
origin:"Controller array",
country:"Sweden"
}];
vm.click = function(){
$http.get("data"+Math.floor((Math.random() * 2) + 1)+".json").success(function(data){
vm.panels.push({
name:data.name,
origin:data.origin,
country:data.country
})
})
}
})

AngularJS : how to get directive input field model value in controller?

I created a custom directive name kid. In that I have one input field with having usermodel object. I need to get its value in my controller. Can we get user model object in my controller. Actually I used to same directive in my view. I need to get both directive input values in my controller .
Here is my Plnkr
var app =angular.module('Testappp',[]);
app.controller('testcontroller',function(){
})
app.directive('kid',function(){
return {
restrict:"E",
scope:{},
template:"<input type='text' ng-model='usermodel'/>{{usermodel}}",
}
})
I updated your plunkr: updatedMyPlunker
I am passing the usermodel to the kid directive via its isolated scope.
The = sign makes sure that the two models will update through two way data binding
<body ng-app="Testappp">
<div ng-controller="testcontroller">
<kid usermodel="usermodel"></kid>
<kid usermodel="usermodelSecondKid"></kid>
</div>
</body>
var app =angular.module('Testappp',[]);
app.controller('testcontroller',function($scope){
$scope.usermodel = '';
$scope.usermodelSecondKid = '';
$scope.$watch("usermodel", function(newvalue,oldvalue){
console.log(newvalue);
})
})
app.directive('kid',function(){
return {
restrict:"E",
scope:{ usermodel: "=usermodel"
},
template:"<input type='text' ng-model='usermodel'/>{{usermodel}}",
}
})
Forked your plnkr. Passed two way data model from controller to directive. kid1 and kid2 are controller variable. Which will value you enter in text box.
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#*" data-semver="2.0.0" src="scruipt"></script>
<link rel="stylesheet" href="style.css" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
</head>
<body ng-app="Testappp">
<div ng-controller="testcontroller">
<kid ng-model="kid1"></kid>
<kid ng-model="kid2"></kid>
</div>
</body>
<script>
var app =angular.module('Testappp',[]);
app.controller('testcontroller',function(){
})
app.directive('kid',function(){
return {
restrict:"E",
scope:{
ngModel: '=ngModel'
},
template:"<input type='text' ng-model='ngModel'/>{{ngModel}}",
}
})
</script>
</html>

AngularJS data binding not working

I wrote some simplified code on http://jsfiddle.net/gsche/1/
When I click the "Refresh" link, the "customer.name" model doesn't update the view.
I wrote the code on my local computer and debugged it with Batarang in Chrome.
The console doesn't show any errors. The Model page in Batarang shows the customer name changing on the right, associated with an old scope id, but the id's of the $scopes also change.
Can anyone point me in the right direction?
<div ng-app>
<div ng-controller="MainCtrl">
<p> Refresh </p>
<p>
<input type="text" ng-model="customer.name">
</p>
<p>{{ customer.name }}</p>
</div>
</div>
function MainCtrl($scope) {
$scope.customer = {
name: 'TTT',
id: 0
};
$scope.Refresh = function ($scope) {
$scope.customer.name = 'Test';
};
}
Update 1 08.08.2013
Thank you all (#EpokK, #Stewie, #Hippocrates). I undestand now the problem with jsfiddle, and the example works as it should.
However, in my test application, the {{customer.name}} binding works, but the "Refresh" click still doesn't change the {{customer.name}} in the view.
Here is the content of my application. I think it is the same as the jsfiddle example:
index.html
<!doctype html>
<head>
<title>Test</title>
</head>
<body ng-app="roaMobileNgApp">
<script src="scripts/angular.js"></script>
<script src="scripts/angular-ui.js"></script>
<link rel="stylesheet" href="scripts/angular-ui.css">
<div class="container" ng-view=""></div>
<script src="scripts/app.js"></script>
<script src="scripts/controllers/main.js"></script>
</body>
</html>
app.js
'use strict';
angular.module('roaMobileNgApp', ['ui'])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.otherwise({
redirectTo: '/'
});
});
main.js
'use strict';
angular.module('roaMobileNgApp')
.controller('MainCtrl', function ($scope) {
$scope.customer = {name: '',
id: 0};
$scope.getDetails = function() {
$scope.customer.name = 'Test';
};
});
main.html
<div ng-controller="MainCtrl">
Refresh
<p><input type="text" ng-model="customer.name"> {{ customer.name }} </p>
</div>
Check my solution, I have edit your JSFiddle: http://jsfiddle.net/gsche/10/
function MainCtrl($scope) {
$scope.customer = {
name: 'TTT',
id: 0
};
$scope.getDetails = function () {
$scope.customer.name = 'Test';
};
}
Just select "No wrap - in " in fiddle options (because otherwise your demo won't work), and remove the $scope parameter from your Refresh function. You don't need to pass in $scope because the function itself is defined inside the $scope.
$scope.getDetails = function(){
$scope.customer.name = 'Test';
};
It's just the way you loaded your scripts.
I like to load angular followed by my app.js containing controllers right at the end of the body:
...
<script src="/assets/js/angular.min.js"></script>
<script src="/assets/js/app.js"></script>
</body>
To get your fiddle working, all I did was change it from "onReady" to "in body" (It's the little dropdown near the top left.)
Check this out: http://jsfiddle.net/gsche/3/
onReady means your controller definition was wrapped in an onReady function by jsfiddle. By the time angular loaded and got to work, your controller was still undefined and angular had to go ahead and spit out the raw template.

AngularJS Controller $scope not displaying variable

I am new to AngularJs.
In app.js I have the following
angular.module('module1', ['module2'])
.config(function($routeProvider) {
$routeProvider
.when('/',
{
controller: 'Controller1',
templateUrl: '/app/module/module1/partials/module1.html'
});
});
My module1 controller
angular.module('module1').controller('Controller1', function($scope) {
$scope.module1Name = "Module1";
});
In module2 folder I have Index.js
angular.module('module2', []).config(function($routeProvider) {
$routeProvider
.when('/test',
{
controller: 'Controller1',
templateUrl: '/app/module/module2/view/test.html'
});
});;
Module2 controller
angular.module('module2').controller('Controller1', function ($scope) {
$scope.module2Name = "Module2";
});
Here is my index.html
<html data-ng-app="module1">
<head>
<meta name="viewport" content="width=device-width" />
<title>Angular</title>
<script src="~/Scripts/angular.min.js"></script>
<script src="~/App/app.js"></script>
<script src="~/App/module/module2/index.js"></script>
<script src="~/App/module/module2/controller/Controller1.js"></script>
<script src="~/App/module/module1/controller/Controller1.js"></script>
</head>
<body>
<div data-ng-view=""></div>
</body>
</html>
and module1.html
<div>
f4b view {{module1Name}}
<br/>
<a data-ng-href="#/test">Test page</a>
</div>
and test.html
<div>
Test view {{module2Name}} <br/>
<a data-ng-href="#/">f4b page</a>
</div>
when I start the application the module1 name is displayed but when I click the link all I see is "Test view" without module2
{{module2Name}} is not displayed...
Can someone tell me what am I doing wrong?
Thank you
Angular's $injector can't disambiguate between controllers that use the same name. One workaround is to manually namespace them:
angular.module('module1').controller('mod1.Controller1',
...
angular.module('module2').controller('mod2.Controller1',
jsfiddle
See also https://groups.google.com/d/topic/angular/SZMFAKfx1Q8/discussion

Resources