Changing the value of an area variable is not reflected in my line

I have a line below:

"/root/get"; 

Now I am generating a query string in the above line with 1 scope variable, but the problem is when the value of this variable changes and then the new value does not get an update in my URL automatically.

Below you can see that I have a Refresh and Verify button. In the update, I generate a query string, and on the check button, I update the value of the scope variable, but do not get reflected in my URL.

I do not understand why this is happening.

The expected result when you click the test button without calling the 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 an observer, and I don’t want to call generateParameters again. Why doesn't angular automatically update the value in my str , how does angular binding work?

+8
javascript angularjs angularjs-scope
source share
7 answers

Let's start with a question in your code. You generate a query string through a function that is actually only called 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 the function again to change $scope.str , so the value of $scope.str remains the same.

You can easily verify this by following these steps in your snippet:

  1. Click on update (v0)

  2. Click on check (v0)

  3. But then click again on the update, and you will see that it is now updated (v2)

So in step 2 you actually change the version, you just don't generate str again for use.

So it's easy to fix your code - just streamline the code to call a function to assign a 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 monitor the no version parameter and automatically update the str value each time the version changes. But this solution implies that you will have an observer in each controller where you call the API, as well as all parameters that are always hardcoded inside the controllers:

 $scope.$watch('no', function() { $scope.str = generateQueryParameters($scope.str, 'no', $scope.no); }); 

Even if this solution works, it actually sucks. The call control logic is inside the controllers, which is a very bad practice (for this you should think about a centralized service).

Much better, in AngularJS you can use a custom interceptor and manage there all the actions that you need to perform regarding HTTP requests. Thus, you can specify the version of the API that you want to use as the parameter of the HTTP request.

This will keep your code clean. Moreover, if you want to change a query in the future, you can simply change this query parameter. If you want to change all requests, inside the interceptor you can simply set that all requests to version 1 will be replaced by version 2 .

Here is a sample code on how to detect an 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 execute as always HTTP requests via $http adding as the parameter the version that you want to use inside the request:

 // perform your request as usual simply specifying the new version parameter let request = { url: 'myserviceurl', version: '2' // or whatever you qNR }; $http(request); 

Thus, the interceptor will β€œanalyze” all your requests before execution, it will correctly compile the version and query string the way you want, and all your operations will be controlled and centralized.

Just like the last tip, when defining constants, such as version numbers, the endpoint of the rest of the services uses constant AngularJS and enters them where you need to use them. Hard-coded strings are not good practice.

+3
source share

You can easily define the str property using the new controller as syntax (allows you to directly communicate with the controller properties and methods):

  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 that every time you access the str value, it re-evaluates the url string. This makes your code independent of $scope and $watch and more promising.

 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 object below (from the 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 expand this approach - see the 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> 
+1
source share

You need to add watcher for no , which calls the generateQueryParameters method and changes 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

0
source share

you need to call the generateQueryParameters method in the validation method again.

 $scope.check = function () { $scope.no=2; $scope.str=generateQueryParameters($scope.str, "no",$scope.no); console.log($scope.str); }; 
0
source share

you can use

 $scope.$apply() 

to apply changes to your scope in a function. This greatly simplifies data binding to two-way communications to trigger new changes in your scope. If you correctly apply data binding, you do not have to use this command, but it works like a dirty fast hack.

0
source share

try this, the problem with javascript primitive values ​​is passed as values, not links.

 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; } }); 
0
source share

Try the following:

 $scope.str = "/root/get"; $scope.$apply(); 
0
source share

All Articles