$watch not displaying change with two ui-views - angularjs

I have $watch function where it watches whenever a country is selected, but it's not saving the changed data to the scope. I had one ui-view, which worked fine, but then I changed the structure and now have two ui-views under same page, both uses the same controller. Reason for the change was for was to have a clean html page, I could always go back to one ui-view, but it's messy. This is where it started having problems.
here is app.js
$stateProvider
.state('homepage',{
templateUrl: '/static/partial/home/homeLayout.html',
controller: 'UploadFileCtrl',
})
.state('homepage.show',{
url: '/',
views: {
querySetup: {
templateUrl: '/static/partial/home/querySetup.html',
},
queryResults: {
templateUrl: '/static/partial/home/queryResults.html',
},
}
})
layout.html - ui-views:
<div class="col-sm-12 panel-container">
<div class="col-sm-6" style="height: 100%;">
<div ui-view="querySetup" ></div>
</div>
<div class="col-sm-6" style="height: 100%;">
<div ui-view="queryResults"></div>
</div>
</div>
controller watch:
$scope.currentCountries = [
{
name: 'United States',
code: 'US'
},
{
name: 'United Kingdom',
code: 'GB'
},
{
name: 'Germany',
code: 'DE'
},
{
name: 'France',
code: 'FR'
},
{
name: 'China',
code: 'CN'
},
{
name: 'Japan',
code: 'JP'
}
];
$scope.$watch('pickedCountry', function(){
if($scope.pickedCountry != null){
$scope.countryCode = $scope.pickedCountry.code;
}
});
here is querySetup view:
<select name="brand" ng-model = "pickedCountry"
data-ng-options="country as country.name for country in currentCountries">
<option value="">Select Brand</option>
</select>
I moved the controller out of the state homepage, and set for each view under the state homepage.show. This worked fine, but an extra operation is not working. When selecting a brand, it's supposed to run a calculation and set it in the queryResults view. The results is set to a scope which have to be displayed to queryResults.
here is queryResults:
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">OSM RESULTS</h3>
</div>
<div class="panel-body">
<textarea rows="50" cols="100" class="xmlOutput">
{{ queryResults }}
</textarea>
</div>
</div>
Please help! Not familiar with Angular $watch, and I think the problem comes when there is two ui-views. Thanks in advance for your help.
UPDATE
Found the solution to my problem, so I need to add $parent to my model and pass the changed data as a param to my $watch function:
html update:
<select name="brand" ng-model = "$parent.pickedCountry"
data-ng-options="country as country.name for country in currentCountries">
<option value="">Select Brand</option>
</select>
controller update
$scope.$watch('pickedCountry', function(data){
if(data != null){
$scope.countryCode = data.code;
}
});
This solved the issue, but still don't quite understand why I needed to add the parent. Can someone explain why?

You have your controller set up to live in homepage, but your templates live in homepage.show which is a child state of homepage. In order to access the variables from that controller you can use $parent (as you've discovered) to access the parent state's scope.
I would recommend restructuring your state definition though so you don't have to deal with that.
.state( 'homepage', {
url: '/homepage',
views: {
'':{
templateUrl:'/static/partial/home/homeLayout.html',
controller: 'UploadFileCtrl',
},
'querySetup#homepage': {
templateUrl:'/static/partial/home/querySetup.html'
},
'queryResults#homepage':{
templateUrl:'/static/partial/home/queryResults.html'
}
}
})

I believe it is because child states operate the same as prototypical inheritance (scope: true) on directives.
You might consider defining in your controller:
$scope.input = {};
$scope.$watch('input.pickedCountry', function(data){
if(data != null){
$scope.countryCode = data.code;
}
});
and in the querySetup template:
<select name="brand" ng-model = "input.pickedCountry"
data-ng-options="country as country.name for country in currentCountries">
<option value="">Select Brand</option>
</select>

Related

KendoUI dropdown filter doesn't work with AngularJS

I try to add the filter to KendoUI dropdown list and It seems like not working. filter works fine without the angular. But when I add that to with angular it doesn't show the type filter inside the dropdown. I used the same example which is in the official website.
<div ng-controller='myctrl'>
<h4 style="padding-top: 2em;">Remote data</h4>
<select kendo-drop-down-list
k-data-text-field="'ProductName'"
k-data-value-field="'ProductID'"
k-data-source="productsDataSource"
style="width: 100%">
</select>
<div>
Controller
angular.module('myApp', ["kendo.directives"])
.controller('myctrl', ['$scope', function($scope) {
$scope.productsDataSource = {
type: "odata",
serverFiltering: true,
filter: "startswith",
transport: {
read: {
url: "https://demos.telerik.com/kendo-ui/service/Northwind.svc/Products",
}
}
};
}]);
This is the jsfiddle.
You are placing your "filter" property incorrectly. Please see the demo guide.
The filter property should be in the kendo-drop-down-list element but since you are not using the kendo-drop-down-list as a tag and just using it as a property of the select element you need to add the filter property in the element tag as well. See below:
<select kendo-drop-down-list
k-data-text-field="'ProductName'"
k-data-value-field="'ProductID'"
k-data-source="productsDataSource"
filter="'startsWith'"
style="width: 100%"></select>
<div>
and of course remove your filter property from your angular module
angular.module('myApp', ["kendo.directives"])
.controller('myctrl', ['$scope', function($scope) {
$scope.productsDataSource = {
type: "odata",
serverFiltering: true,
transport: {
read: {
url: "https://demos.telerik.com/kendo-ui/service/Northwind.svc/Products",
}
}
};
}]);
See the JSFilddle fork of your JSFiddle

how to keep selected option from user when views are changed

Demo:
I have a dropdown, and options are being fecthed from JSON.
say
<select ng-model="projectModel" ng-options="project as project.projectName">
<option>one</option>
<option>two</option>
<option>three</option>
<option>four</option>
<option>and so on... </option>
</select>
i have two views page1.html and page2.html
in page1.html i have the following code.
select box with above options when ever user select an option say, user has selected three. this has to be saved somewhere. i dont where to save, so next when user clicks page2.html the data selected before in page1.html should not reset.
HTML :
<body ng-app="staticSelect">
<div ng-controller="ddController">
<form name="myForm">
<label for="singleSelect"> Single select: </label><br>
<select name="singleSelect" ng-model="data.singleSelect">
<option ng-repeat="x in options">{{x}}</option>
</select><br>
<tt>singleSelect = {{data.singleSelect}}</tt>
</form>
</div>
</body>
JS
app.controller('ddController', ['$scope', function($scope) {
$scope.data = {
singleSelect: null,
};
$scope.options = ["red","blue","yellow"];
}]);
app.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: '01.html',
controller: 'htmlCtrl',
})
.when('/html', {
templateUrl: '02.html',
controller: 'htmlCtrl',
})
})
any help would be much appreciated!
Thanks
Added a reference for 'ng-cookies' in the main view and a service to set and get a cookie.
In Html:
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular-cookies.js"></script>
The binding html has the method to set the cookie through ng-change
<select name="singleSelect" ng-model="data.singleSelect" ng-change="setSelect()">
<option ng-repeat="x in options">{{x}}</option>
</select>
You can see the CookieService in the Working Demo
In Controller:
When the setSelect() method is fired the selected value is set in cookie, when the view is returned back to it will re store the value from the cookie
if(cookieService.getAppData("selected") !== null && cookieService.getAppData("selected") !== undefined){
$scope.data.singleSelect = cookieService.getAppData("selected");
}
$scope.setSelect = function (){
cookieService.setAppData("selected", $scope.data.singleSelect);
}

Angular: further dynamic filtering issue

I'm trying to get these dynamic filters working and its almost there I think. Looks like the model isn't being passed back to the watch function. I would've expected it to work with the first set of filters at least but it doesn't.
What I'm trying to achieve is that when the user selects an option from the select list and sets a value in the input box the data is filtered. The user can optionally add another set of filters by clicking the "add fields" button. If there user completes the second set of filters then the data is filtered further.
This is what I've got so far. If there's only one filter showing then shouldn't it just work?
This is the code that creates the user defined filters.
<div data-ng-repeat="filter in filters">
<select ng-model="filter.id">
<option value="age">Age</option>
<option value="customerId">CustomerID</option>
<option value="productId">ProductID</option>
<option value="name">Name</option>
</select>
<input type="text" ng-model="data.search[filter.id]">
<button class="remove" ng-show="$last" ng-click="removeFilter()">-</button>
Add fields
I think I'm almost there, got a better understanding of hierarchical scope. I've referred to a few tutorial and examples but I'm not quite there yet. I think my issue with this is that I'm not communicating the models properly. Still a little bit lost with this. Any further tips/suggestions would be great. I'm wondering if I should move some of he code from the controller in my directive into the resultsCtrl controller. Really not sure.
This fiddle gave me the idea to use filter.id within my template ng-repeat
This plnkr was helpful.
I'm getting somewhere now. This plnkr shows it working, the last thing I want to do with it is when you remove a filter it automatically updates the search object to remove the relevant filter.
Any suggestions on how to do this?
You are breaking the golden rule of "always have a dot in ng-model".
Why? because objects have inheritance and primitives do not.
ng-model="filterType"
is being defined inside a child scope created by ng-repeat. Because it is a primitive the parent scope in controller can not see it. However if it was a property of an object that was available at parent level, the reference would be shared between parent and child.
Once you fix that bug you will run into a bug where scope.search is not an object either so your $watch function will throw an exception also. In main controller can set:
$scope.search ={};
Do some reading up on how hierarchical scopes work in angular. There is plenty to be found in web searches and angular docs
I eventually got this solved :)
directive:
angular.module('dynamicFiltersDirective', [])
.directive('dynamicFilters', function() {
return {
restrict: 'AE',
scope: {
filtersArray: '=',
searchObject: '='
},
link: function(scope) {
scope.addFilter = function() {
var newItemNo = scope.filtersArray.length+1;
scope.filtersArray.push({'id':'filter'+newItemNo});
};
scope.removeFilter = function(option) {
var lastItem = scope.filtersArray.length-1;
scope.filtersArray.splice(lastItem);
delete scope.searchObject[option];
};
scope.updateFilters = function (model) {
scope.searchObject[model];
}
},
templateUrl: 'filtertemplate.html'
}
});
controller:
angular.module('app', ['dynamicFiltersDirective']).controller('resultsCtrl', function($scope){
$scope.filters = [{id: 'filter1'}];
$scope.search = {};
$scope.persons = [{
name: 'Jim',
age: 21,
customerId: 1,
productId: 4
},{
name: 'Frank',
age: 20,
customerId: 2,
productId: 4
},{
name: 'Bob',
age: 20,
customerId: 3,
productId: 5
},{
name: 'Bill',
age: 20,
customerId: 3,
productId: 5
}];
});
template:
<div data-ng-repeat="filter in filtersArray">
<select ng-model="filter.id">
<option value="age">Age</option>
<option value="customerId">CustomerID</option>
<option value="productId">ProductID</option>
<option value="name">Name</option>
</select>
<input type="text" ng-model="searchObject[filter.id]" ng-change="updateFilters(searchObject[filter.id])">
<button class="remove" ng-show="$last" ng-click="removeFilter(filter.id)">-</button>
</div>
<button class="addfields" ng-click="addFilter()">Add fields</button>
<div id="filtersDisplay">
{{ filters }}
</div>
index.html
<div ng-controller="resultsCtrl">
<dynamic-filters filters-array="filters" search-object="search"> </dynamic-filters>
<div ng-repeat="person in persons | filter: search">
{{person.name}}
</div>
Here's my example plnkr

Can't access options for ng-options inside an ng-repeat

I would like to be able to access a set of select options within an ng-options inside an ng-repeat, but the ng-repeat creates a new scope where I can no longer access this. Is there a way around this without adding it to the repeating data?
<body ng-controller="mainCtrl" class="container">
<test repeater="repeater"></test>
</body>
Template:
<div ng-repeat="repeat in repeater">
<span>{{repeat.type}}</span>
<select ng-options="value for value in options">
<option value="">Choose one:</option>
</select>
</div>
JS:
angular.module('app').controller('mainCtrl', function($scope) {
$scope.repeater = [
{
type: 'best'
},
{
type: 'worst'
},
{
type: 'ok'
}
];
});
angular.module('app').directive('test', function() {
return {
scope: {
repeater: '='
},
templateUrl: 'test.html',
controller: function($scope) {
$scope.options = ['Nope', 'Yup', 'Sure'];
}
};
});
Plunker
You're close, but missing 1 thing. ng-model
<select ng-model="selectedOption" ng-options="value for value in options">
<option value="">Choose one:</option>
</select>

Getting values from an object into ng-model

I have resource that gets an object:
.factory('Product', ['$resource', function($resource) {
return $resource( 'api/product/:id', { Id: '#Id' }, {
update: { method: 'PUT', isArray: false }
});
}])
I call this resource from the ng-route, in the /edit/product/:sku using 'resolve'
.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/index', {
templateUrl: 'lord/partials/main'
}).
when('/product/edit/:SKU', {
controller: 'EditCtrl',
templateUrl: function(params){ return 'lord/partials/product/edit' },
resolve: {
product: function( Product, $routeParams ){
return Product.get({ id: 101 }).$promise;
}
}
}).
otherwise({
redirectTo: '/index'
});
}]);
In my controller, I receive product successfully:
.controller('EditCtrl', ['$scope', 'product', function ($scope, product) {
console.log(product.measures); // I get the object -> Object {weight: 100, length: 200, width: 180, height: 200}
//Initialize object:
$scope.datos = {};
$scope.datos.measures = {};
However, when i want to "copy" that object to a scope variable, it doesn't work. The scope variable is $scope.datos, which is an object, in a form. I populate this object like this:
<div class="col-md-1">Peso</div>
<div class="col-md-2">
<div class="input-group">
<input type="number" class="form-control" ng-model="datos.measures.weight" >
<span class="input-group-addon">gr</span>
</div>
</div>
<div class="col-md-1">Largo</div>
<div class="col-md-2">
<div class="input-group">
<input type="number" class="form-control" ng-model="datos.measures.lenght">
<span class="input-group-addon">cm</span>
</div>
</div>
<div class="col-md-1">Ancho</div>
<div class="col-md-2">
<div class="input-group">
<input type="number" class="form-control" ng-model="datos.measures.width">
<span class="input-group-addon">cm</span>
</div>
</div>
<div class="col-md-1">Alto</div>
<div class="col-md-2">
<div class="input-group">
<input type="number" class="form-control" ng-model="datos.measures.height">
<span class="input-group-addon">cm</span>
</div>
I tried, in the controller, several methods to copy the value that I receive into the object.
I tried:
angular.copy(product.measures,$scope.datos.measures);
And:
$scope.datos.measures = angular.copy(product.measures);
And:
$scope.datos.measures = product.measures;
$scope.datos.measures.weight = product.measures.weight;
And none of these worked.
However, assigning the product.measures.width into any variable, like $scope.dwidth, works. But this is not what I want, since my object is far more complex.
How can I get to copy the values of the object I receive (product.measures) into another object ($scope.datos.measures) and reflect that value in the with ng-model?
Thank you.
I was able to determine what was happening. I was calling another controller that was resetting these values.
So the route controller was called (EditCtrl) but after that, the controller in the page reset the values.
After that, it was enough with the following code:
angular.copy(product.measures,$scope.datos.measures);
That made the trick and copied the whole object.
Also, I had to change tue $scope.datos.measures.length name to $scope.datos.measures.len due to the Javascript property.

Resources