I have below string :
"/root/get";
Now I am generating a query string in above string with 1 scope variable but problem is when the value of that variable change then that new value is not getting update in my URL automatically.
You can see in below demo that I have 2 button Update and Check. In update I am generating query string and on check button I am updating the value of scope variable but that is not getting reflected in my URL.
I am not getting why this is happening.
Expected output when I click on check button without calling generateQueryParameters method:
/root/get?no=2
var app = angular.module("myApp", []);
app.controller("myController", function ($scope) {
$scope.no = 1;
$scope.str = "/root/get";
$scope.update = function (data) {
$scope.str=generateQueryParameters($scope.str,
"no",$scope.no);
console.log($scope.str);
};
$scope.check = function () {
$scope.no=2;
console.log($scope.str);
};
function generateQueryParameters(url,name,value)
{
var re = new RegExp("([?&]" + name + "=)[^&]+", "");
function add(sep) {
url += sep + name + "=" + encodeURIComponent(value);
}
function change() {
url = url.replace(re, "$1" + encodeURIComponent(value));
}
if (url.indexOf("?") === -1) {
add("?");
} else {
if (re.test(url)) {
change();
} else {
add("&");
}
}
return url;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController">
<input type="button" value="Update" ng-click="update()">
<input type="button" value="Check" ng-click="check()">
</div>
Update : I know when the value of $scope.no will change so I don't think I need watcher and I don't want to call generateParameters again.Why angular is not automatically updating the value in my str like the way Angular bindings works?
Let's start from the issue in your code. You generate the request string through a function, which is actually called only inside the update function
$scope.update = function(data) {
$scope.str = generateQueryParameters($scope.str,
"no", $scope.no);
console.log($scope.str);
};
$scope.check = function() {
$scope.no = 2;
console.log($scope.str);
};
You change the version number in the check function, but without calling again the function in order to change the $scope.str, so the value of $scope.str is still the same.
You can easily test this doing the following steps in your snippet:
Click on update (v0)
Click on check (v0)
But then click again on update and you will see that now it is updated (v2)
So in step 2 you actually change the version, you simply don't generate again the str to be used.
So easy fix for your code is simply to arrange your code in order to call the function for assigning the new str, every time you change your version:
$scope.update = function(data) {
$scope.no = 1;
$scope.str = generateQueryParameters($scope.str,
"no", $scope.no);
console.log($scope.str);
};
$scope.check = function() {
$scope.no = 2;
// here you need to call again your function for composing your URL and changing the value of str
$scope.str = generateQueryParameters($scope.str,
"no", $scope.no);
console.log($scope.str);
};
function generateStringRequest() {
}
Otherwise, you need to watch on the no version parameter, and automatically refresh the str value every time the version changes. But this solution implies that you will have a watcher in every single controller where you call the APIs, and also all the parameters always hardcoded inside the controllers:
$scope.$watch('no', function() {
$scope.str = generateQueryParameters($scope.str, 'no', $scope.no);
});
Even if this solution works, actually it sucks.
The logic for managing calls is inside the controllers, which is a really bad practice (you should think to use a centralized service for that).
So much better, in AngularJS you can use a custom interceptor and manage there all the actions to be performed regarding HTTP requests.
So you can specify as a parameter of the HTTP request, the version of the API you want to use.
This will keep your code clean. Moreover, if you want to change in the future some request, you can simply change that request parameter.
If you want to change all the requests, inside the interceptor you can simply set that all the requests to version 1 will be replaced by version 2.
Here a sample code on how to define the interceptor:
angular.module('myApp').factory('MyHTTPFactory', MyHTTPFactory)
.config(function($httpProvider) {
// register the new interceptor in AngularJS
$httpProvider.interceptors.push('MyHTTPFactory');
});
MyHTTPFactory.$inject = [];
function MyHTTPFactory() {
// this is the base URL of your rest requests, so this interceptor will be applied only to the requests directed to your service
const MY_REST_URL = 'blablabla';
// methods exposed by the service
let factory = {
request: request,
requestError: requestError,
response: response,
responseError: responseError
};
return factory;
// ============================================================
function request(config) {
if (config.url.includes(MY_REST_URL)) {
let versionToUse = config.version;
// here use a function for elaborating your query string, directly in the interecptor code
config.url = elaborateQueryString();
}
return config;
}
function requestError(config) {
return config;
}
function response(res) {
return res;
}
function responseError(res) {
return res;
}
// function for getting the query string you want to
function elaborateQueryString() {
// elaborate your requests
}
}
Then simply perform as always the HTTP requests through $http adding the version you want to use inside the request as a parameter:
// perform your request as usual simply specifying the new version parameter
let request = {
url: `myserviceurl`,
version: '2' // or whatever you qNR
};
$http(request);
So the interceptor will "sniff" all your requests before to be performed, it will correctly compose the version and the query string as you want to, and all your operations are managed within it and centralized.
Just as the last tip, when defining constants like version numbers, the endpoint of the rest service, whatever, use AngularJS constant and inject them where you need to use them. Hard-coded strings are not a good practice.
You can easily define a property str using the new controller as syntax (allows you to directly bind to controller properties and methods) as such:
Object.defineProperty(vm,
"str", {
get: function() {
return vm.generateQueryParameters("/root/get","no", vm.no);
},
set: function(newValue) {
// setter function
},
enumerable: true,
configurable: true
});
This means everytime you access the value of str, it re-evaluates the url string. This makes your code independent of $scope and $watch and is more forward-looking.
var app = angular.module("myApp", []);
app.controller("myController", function() {
var vm = this;
vm.no = 1;
Object.defineProperty(vm,
"str", {
get: function() {
return vm.generateQueryParameters("/root/get","no", vm.no);
},
set: function(newValue) {
// setter function
},
enumerable: true,
configurable: true
});
vm.update = function(data) {
console.log(vm.str);
};
vm.check = function() {
vm.no = 2;
console.log(vm.str);
};
vm.generateQueryParameters = function(url, name, value) {
var re = new RegExp("([?&]" + name + "=)[^&]+", "");
function add(sep) {
url += sep + name + "=" + encodeURIComponent(value);
}
function change() {
url = url.replace(re, "$1" + encodeURIComponent(value));
}
if (url.indexOf("?") === -1) {
add("?");
} else {
if (re.test(url)) {
change();
} else {
add("&");
}
}
return url;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController as ctrl">
<input type="button" value="Update" ng-click="ctrl.update()">
<input type="button" value="Check" ng-click="ctrl.check()">
</div>
For the below object (from discussion):
[
{
"name": "Node-1",
"isParent": true,
"text" : [
{
"str" : "/root/get",
},
{
"str" : "/root/get",
}],
"nodes": [
{
"name": "Node-1-1",
"isParent": false,
"text" : [
{
"str" : "/root/get",
},
{
"str" : "/root/get",
}],
"nodes": [
{
"name": "Node-1-1-1",
"isParent": false,
"text" : [
{
"str" : "/root/get",
},
{
"str" : "/root/get",
}],
"nodes": []
}
]
}
]
}
]
we can extend this approach - see demo below:
var app = angular.module("myApp", []);
app.controller("myController", function() {
var vm = this;
vm.no = 1;
vm._records=[{"name":"Node-1","isParent":true,"text":[{"str":"/root/get",},{"str":"/root/get",}],"nodes":[{"name":"Node-1-1","isParent":false,"text":[{"str":"/root/get",},{"str":"/root/get",}],"nodes":[{"name":"Node-1-1-1","isParent":false,"text":[{"str":"/root/get",},{"str":"/root/get",}],"nodes":[]}]}]}];
vm.setRecords = function(node, url) {
node.text && node.text.forEach(function(e){
e.str = url;
});
// recursively set url
node.nodes && node.nodes.forEach(function(e){
vm.setRecords(e, url);
});
}
Object.defineProperty(vm,
"records", {
get: function() {
let url = vm.generateQueryParameters("/root/get", "no", vm.no);
vm._records.forEach(function(e){
vm.setRecords(e, url);
});
return vm._records;
},
set: function(newValue) {
// setter function
},
enumerable: true,
configurable: true
});
vm.update = function(data) {
console.log(vm.records);
};
vm.check = function() {
vm.no = 2;
console.log(vm.records);
};
vm.generateQueryParameters = function(url, name, value) {
var re = new RegExp("([?&]" + name + "=)[^&]+", "");
function add(sep) {
url += sep + name + "=" + encodeURIComponent(value);
}
function change() {
url = url.replace(re, "$1" + encodeURIComponent(value));
}
if (url.indexOf("?") === -1) {
add("?");
} else {
if (re.test(url)) {
change();
} else {
add("&");
}
}
return url;
}
});
.as-console-wrapper{top:25px;max-height:calc(100% - 25px)!important;}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController as ctrl">
<input type="button" value="Update" ng-click="ctrl.update()">
<input type="button" value="Check" ng-click="ctrl.check()">
</div>
You need to add watcher for no which call you method called generateQueryParameters and change the value of $scope.str
var app = angular.module("myApp", []);
app.controller("myController", function ($scope) {
$scope.no = 1;
$scope.str = "/root/get";
$scope.check = function () {
console.log($scope.str);
};
$scope.$watch('no', function() {
$scope.str = generateQueryParameters($scope.str,
"no",$scope.no);
});
$scope.update = function (data) {
$scope.no=2;
};
function generateQueryParameters(url,name,value)
{
var re = new RegExp("([?&]" + name + "=)[^&]+", "");
function add(sep) {
url += sep + name + "=" + encodeURIComponent(value);
}
function change() {
url = url.replace(re, "$1" + encodeURIComponent(value));
}
if (url.indexOf("?") === -1) {
add("?");
} else {
if (re.test(url)) {
change();
} else {
add("&");
}
}
return url;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController">
<input type="button" value="Update" ng-click="update()">
<input type="button" value="Check" ng-click="check()">
</div>
First update and then check
you need to call generateQueryParameters method again in check method.
$scope.check = function () {
$scope.no=2;
$scope.str=generateQueryParameters($scope.str,
"no",$scope.no);
console.log($scope.str);
};
try this, problem is with javascript primitive values are passed as values not references.
var app = angular.module("myApp", []);
app.controller("myController", function ($scope) {
$scope.testObject = {
no: 1,
str: "/root/get"
};
$scope.update = function (data) {
$scope.str=generateQueryParameters($scope.testObject,
"no");
console.log($scope.testObject);
};
$scope.check = function () {
$scope.testObject.no=2;
console.log($scope.testObject.str);
};
function generateQueryParameters(urlAndValue, name)
{
var re = new RegExp("([?&]" + name + "=)[^&]+", "");
function add(sep) {
urlAndValue.str += sep + name + "=" + encodeURIComponent(urlAndValue.no);
}
function change() {
urlAndValue.str = urlAndValue.str.replace(re, "$1" + encodeURIComponent(urlAndValue.no));
}
if (urlAndValue.str.indexOf("?") === -1) {
add("?");
} else {
if (re.test(urlAndValue.str)) {
change();
} else {
add("&");
}
}
return urlAndValue.str;
}
});
Try this:
$scope.str = "/root/get";
$scope.$apply();
You can use
$scope.$apply()
to apply a change to your scope in a function. It essentially helps force your two-way data-binding to kick off and catch on to new changes in your scope. If you implement data-binding correctly, you don't have to necessarily use this command, but it does work as a dirty quick hack.
I use $scope.watch and set and get to pass one data from one controller to another. This works fine, but when I do this with different html files, data is not transferred in my first click. because browser is refreshed. But when I click go back arrow in chrome and set a value and clicked, I can get the value in my second page. How can I resolve this. Please tell me solution.
var data = {
Quantity: ''
};
return {
getQuantity: function() {
return data.Quantity;
},
setQuantity: function(quantity) {
data.Quantity = quantity;
}
};
.controller('DetailsCtrl', function($scope, $http, Data) {
$scope.quantity = '';
$scope.$watch('quantity', function(newValue50, oldValue50) {
if (newValue50 !== oldValue50)
Data.setQuantity(newValue50);
});
})
.controller('FifthCtrl', function($scope, $http, Data) {
$scope.$watch(function() {
return Data.getQuantity();
}, function(newValue50, oldValue50) {
if (newValue50 !== oldValue50)
$scope.quantity = newValue50;
});
})
If you want to store the value between different sessions in the browser you could add the quantity value to the local storage.
Here's an angularjs service/factory example
app.factory('Data', function () {
var data = {
Quantity: ''
};
return {
getQuantity: function () {
if (localStorage.getItem("data") === null) {
data.Quantity = 0;
} else {
var jsonData = localStorage.getItem('data');
data.Quantity = JSON.parse(jsonData);
}
return data.Quantity;
},
setQuantity: function (quantity) {
data.Quantity = quantity;
localStorage.setItem("data", JSON.stringify(data));
}
};
});
Note 1: You actually don't need the object 'data' in this case as you store the value in the storage but I let it stand as is.
Note 2: The localStorage can't store complext types such as objects and you need to make strings of them before you store them, thats why the JSON.stringify() is used.
I'm sending a data ...
....
// upload on file select or drop
$scope.upload = function (file, id) {
id = typeof id !== 'undefined' ? id : null;
Upload.base64DataUrl(file).then(function(base64){
//auth
var fbAuth = FirebaseURL.getAuth();
//Ref
var ref = FirebaseURL.child("users_photos");
ref.push({'image': base64,'removed': true, 'user_id': fbAuth.uid, 'dt_created':Firebase.ServerValue.TIMESTAMP ,'dt_updated':Firebase.ServerValue.TIMESTAMP}, function(error){
if (error) {
alert('Error');
} else {
var newID = ref.key();
//I would like display data insert here?
console.log(DATA RESULT INSERT);
}
});
});
I would like display data inserted.
It is possible to display the last inserted object without query by the key?
Use AngularFire for synchronized collections.
Create a query using limitToLast(1) to always sync the last inserted object.
angular.module('app', ['firebase'])
.constant('FirebaseUrl', '<my-firebase-app>')
.service('rootRef', ['FirebaseUrl', Firebase)
.factory('userItems', UserItems)
.controller('MyCtrl', MyController);
function UserItems($firebaseArray, rootRef) {
return function userItems(uid) {
var itemRef = rootRef.child('items');
var query = itemRef.orderyByChild('uid').equalTo(uid);
return $firebaseArray(query);
}
}
function MyController($scope, userItems, rootRef) {
$scope.items = userItems(rootRef.getAuth().uid);
$scope.addItem = function addItem(item) {
$scope.items.$add(item).then(function(ref) {
var record = $scope.items.$getRecord(ref.key());
// save the data to the other structure
});
};
}
See the section on Complex queries for more info.
contactManager.controller('contactsList',
function contactsList($scope){
$scope.myId = 0;
$scope.contacts = [{id:$scope.myId,name:'Default',mail:'test#cognizant.com',mobile:'000000'},
{id:$scope.myId++,name:'andefined',mail:'undefined#cognizant.com',mobile:'1111'}];
});
contactManager.controller('addContactCtrl',
function addContactCtrl($scope,$location){
$scope.contact = {};
$scope.add = function(){
if($scope.contact.name){
$scope.contact.id = $scope.myId++; // **Increment Doesn't happen Here. It assigns the same value evertime**
$scope.contacts.push($scope.contact);
$location.url('/');
}
else{
alert('Name is mandatory');
}
};
});
Increment doesn't happen in $scope.myId++ !
I'm trying the assign id's to every new contact added to the list, but the id's are not getting incremented !!
You are better off using a service that provides the ID for you. You can create a service as follows:
contactManager.service('uniqueIds', function () {
var currentId = null;
return {
getNextId: function () {
if (currentId === null) {
currentId = 0;
} else {
currentId = currentId + 1;
}
return currentId;
}
}:
});
You can then use this service in your controllers as follows:
contactManager.controller('contactsList', ['$scope', 'uniqueIds', function ($scope, uniqueIds) {
$scope.contacts = {
id: uniqueIds.getNextId(), //Service call
name: 'Default',
mail: 'test#abc.com',
mobile:'000000'
}, {
id: uniqueIds.getNextId(), //Service call
name: 'undefined',
mail: 'undefined#xyz.com',
mobile:'1111'
}];
});
contactManager.controller('addContactCtrl', ['$scope', '$location', 'uniqueIds', function ($scope, $location, uniqueIds) {
$scope.contact = {};
$scope.add = function(){
if($scope.contact.name){
$scope.contact.id = uniqueIds.getNextId(); //Service call
$scope.contacts.push($scope.contact);
$location.url('/');
} else {
alert('Name is mandatory');
}
};
});
EDIT: If you are looking to generate uniqueIds, then this is not the way to go - You may want to check this out to generate them.