Изменение значения переменной области не отражается в моей строке
У меня есть строка ниже:
"/root/get";
Теперь я генерирую строку запроса в приведенной выше строке с 1 переменной области, но проблема заключается в том, что когда значение этой переменной изменяется, это новое значение не получает обновления в моем URL автоматически.
В демонстрации ниже вы можете видеть, что у меня есть 2 кнопки Обновить и Проверить. При обновлении я генерирую строку запроса, а по кнопке проверки я обновляю значение переменной области, но это не отражается в моем URL.
Я не понимаю, почему это происходит.
Ожидаемый результат при нажатии кнопки проверки без вызова метода generateQueryParameters:
/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>
Обновление: я знаю, когда значение $scope.no изменится, поэтому я не думаю, что мне нужен наблюдатель, и я не хочу звонить generateParameters
снова. Почему angular не обновляет автоматически значение в моем str
как работает угловая привязка?
7 ответов
Давайте начнем с проблемы в вашем коде. Вы генерируете строку запроса через функцию, которая на самом деле вызывается только внутри update
функция
$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);
};
Вы меняете номер версии в check
функция, но без повторного вызова функции, чтобы изменить $scope.str
, так что значение $scope.str
все тот же.
Вы можете легко проверить это, выполнив следующие шаги в своем фрагменте:
Нажмите на обновление (v0)
Нажмите на чек (v0)
Но затем нажмите еще раз на обновление, и вы увидите, что теперь оно обновляется (v2)
Таким образом, на шаге 2 вы фактически меняете версию, просто не генерируете снова str
использоваться.
Таким простым решением для вашего кода является просто упорядочить код, чтобы вызвать функцию назначения нового str
, каждый раз, когда вы меняете свою версию:
$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() {
}
В противном случае вам нужно смотреть на no
параметр версии, и автоматически обновить str
Значение каждый раз, когда изменяется версия. Но это решение подразумевает, что у вас будет наблюдатель в каждом контроллере, где вы вызываете API, а также все параметры, всегда жестко заданные внутри контроллеров:
$scope.$watch('no', function() {
$scope.str = generateQueryParameters($scope.str, 'no', $scope.no);
});
Даже если это решение работает, на самом деле это отстой. Логика управления вызовами находится внутри контроллеров, что является очень плохой практикой (для этого следует подумать об использовании централизованного сервиса).
Намного лучше, в AngularJS вы можете использовать пользовательский перехватчик и управлять там всеми действиями, которые нужно выполнить в отношении HTTP-запросов. Таким образом, вы можете указать в качестве параметра HTTP-запроса версию API, которую вы хотите использовать.
Это сохранит ваш код в чистоте. Более того, если вы хотите изменить в будущем какой-либо запрос, вы можете просто изменить этот параметр запроса. Если вы хотите изменить все запросы, внутри перехватчика вы можете просто установить, что все запросы version 1
будет заменен на version 2
,
Вот пример кода о том, как определить перехватчик:
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
}
}
Затем просто выполните как всегда HTTP-запросы через $http
добавив версию, которую вы хотите использовать внутри запроса, в качестве параметра:
// perform your request as usual simply specifying the new version parameter
let request = {
url: `myserviceurl`,
version: '2' // or whatever you qNR
};
$http(request);
Таким образом, перехватчик будет "анализировать" все ваши запросы перед выполнением, он будет правильно составлять версию и строку запроса так, как вы хотите, и все ваши операции будут управляться внутри нее и будут централизованы.
Как и последний совет, при определении констант, таких как номера версий, конечная точка остальной службы, независимо от того, использует AngularJS constant
и вводить их там, где вам нужно их использовать. Закодированные строки не очень хорошая практика.
Я надеюсь это имеет смысл
Вы можете легко определить свойство str
используя новый контроллер в качестве синтаксиса (позволяет напрямую связать свойства и методы контроллера) следующим образом:
Object.defineProperty(vm,
"str", {
get: function() {
return vm.generateQueryParameters("/root/get","no", vm.no);
},
set: function(newValue) {
// setter function
},
enumerable: true,
configurable: true
});
Это означает, что каждый раз, когда вы получаете доступ к значению str
, он повторно оценивает строку URL. Это делает ваш код независимым от $scope
а также $watch
и более дальновидный.
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>
Для объекта ниже (из обсуждения):
[
{
"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": []
}
]
}
]
}
]
мы можем расширить этот подход - см. демонстрацию ниже:
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>
Вам нужно снова вызвать метод generateQueryParameters в методе check.
$scope.check = function () {
$scope.no=2;
$scope.str=generateQueryParameters($scope.str,
"no",$scope.no);
console.log($scope.str);
};
Вам нужно добавить наблюдателя для no
который вызывает вас метод называется generateQueryParameters
и изменить значение $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>
Сначала обновите, а затем проверьте
Попробуйте это, проблема с примитивами JavaScript передаются как значения, а не ссылки.
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;
}
});
Ты можешь использовать
$scope.$apply()
применить изменения к вашей области в функции. По сути, это помогает заставить ваше двустороннее связывание данных начать новые изменения в вашей области. Если вы реализуете привязку данных правильно, вам не обязательно использовать эту команду, но она работает как грязный быстрый взлом.