How can I assign data to a dynamic variable.
What I'm trying to achieve is this:
My vue data variable:
test: '',
Adding a click
<div #click="assignData(test)"
Method:
assignData(value) {
// Now I don't want this
this.test = 'lorem..';
// Instead I want something like this
value = 'new value for test';
// Or
this.value = 'new value for test';
}
Now obviously this is not gonna work, but I hope you'll get the idea.
I need to change many variables and I don't want to add all the variables in the function like this
assignData(value) {
this.test = 'lorem..';
this.anotherVar = 'ipsum';
this.newTest = '....';
}
You could send the property you want to change with the function.
<div #click="assignData('test', 'new value for test)">
and then in your code
assignData(prop,val) {
this[prop] = val;
console.log(this.test);
}
Here is a snippet from jsfiddle: https://jsfiddle.net/49gptnad/6447/
assignData(key, value) {
this[key] = value;
}
You'll have to specify the variable you're assigning when you activate it with the click caller.
You should use either vm.$set( target, key, value ) or Vue.set( target, key, value )
Vue.set docs
vm.$set docs
Related
Currently, I have a select element in my html which has a ngModel to the object details:
[ngModel]="details?.publicInformation?.firstname"
However, publicInformation may not exist in that object, or if it does, maybe firstname does not exist. No matter the case, in the end, I want to create the following:
[ngModel]="details?.publicInformation?.firstname" (ngModelChange)="details['publicInformation']['firstname'] = $event"
Basically, if the select is triggered, even if neither of publicInformation nor firstname exist, I would like to create them inside details and store the value from the select.
The issue is that I am getting
Cannot set property 'firstname' of undefined
Can someone explain what I am doing wrong here and how can I achieve the result I desire?
You need to initialize details and publicInformation to empty object
public details = {publicInformation : {}};
You should do that when you load the form data.
For example, you might have something like this:
ngOnInit() {
this._someService.loadForm().then((formData: FormData) => {
this.details = formData;
});
}
Then, you could modify that to fill in the missing empty properties you need:
ngOnInit() {
this._someService.loadForm().then((formData: FormData) => {
this.details = formData || {};
if (!this.details.publicInformation) {
this.details.publicInformation = { firstname: '' };
} else if (!this.details.publicInformation.firstname) {
this.details.publicInformation.firstname = '';
}
});
}
However, it would be better to place this logic in the services, so that they are responsible for adding all the necessary empty properties to the data they load, or if you are using Redux, then it should go into the reducers.
Let's say I have these global variables:
var name, car;
Then I declare some values:
const languageStrings = {
WELCOME_MESSAGE: 'Welcome #name, you have a #car',
NAME_USER: "#name",
CAR_USER: '#car'
};
And then I need to assign it to a function.
For example:
if (firstTime){
welcomeMsg = WELCOME_MESSAGE;
}
Now, I have two questions:
1) How would I insert a variable inside of a string so it is dynamically updated when the value pair is called?
2) How would I do the same using JSON?
You can't have a JSON structure or string "automatically" update when some variable changes. There are other ways to do this type of templating, though. You could create a function to create a welcome message when you need one:
function getWelcomeMessage(name, car) {
return "Welcome "+name+", you have a "+car;
}
Then you'd do something like welcomeMsg = getWelcomeMessage("Joe", "Camry");
If you don't want to write a function for every template (i.e. if you have lots of them), then you could use String.replace like this:
function applyTemplate(template, params) {
return template.replace(/#(\w+)/g, (m, name) => params[name]);
}
Example usage:
function applyTemplate(template, params) {
return template.replace(/#(\w+)/g, (m, name) => params[name]);
}
const WELCOME_TEMPLATE = "Welcome #name, you have a #car";
var name = "Joe";
var car = "Camry";
var welcomeMessage = applyTemplate(WELCOME_TEMPLATE, {name, car});
console.log(welcomeMessage);
You would have to make a function that returns the value of the variable.
In your case:
welcomeMessage = function(){
return WELCOME_MESSAGE
}
and you would reference the variable with:
welcomeMessage()
So, you'd be assigning a variable as a function that returns the current value of the other variable. You get the value by calling your variable as a function.
String in JavaScript is primitive type, it's passed by value. So once a variable is assigned with a string, it will never change until you explicitly assign another value (or object reference) to it.
However, you can ask object type for help, which could make your data reactively (or dynamically, if you prefer this word) update under certain conditions.
var languageStrings = {
WELCOME_MESSAGE: '',
NAME_USER: '',
CAR_USER: ''
}
Object.defineProperty(languageStrings, 'name', {
get: function (name) {
return this.NAME_USER
},
set: function (name) {
this.NAME_USER = name
this.WELCOME_MESSAGE = `Welcome ${this.name}, you have a ${this.car}.`
}
})
Object.defineProperty(languageStrings, 'car', {
get: function (car) {
return this.CAR_USER
},
set: function (car) {
this.CAR_USER = car
this.WELCOME_MESSAGE = `Welcome ${this.name}, you have a ${this.car}.`
}
})
Now, whenever you change languageStrings.name or languageStrings.car, all three strings you want will automatically adopt the new value you just set:
languageStrings.name = 'Leo'
languageStrings.car = 'BMW'
for (let key in languageStrings) {
console.log(`${key}: ${languageStrings[key]}`)
}
// WELCOME_MESSAGE: Welcome Leo, you have a BMW.
// NAME_USER: Leo
// CAR_USER: BMW
You don't have to manually call applyTemplate all the time, like in #qxz's answer (I'm not saying his wrong, though).
Also, please notice that even name and car are not enumerable - they will not be accessed with for in, for of, or Object.keys! This is great, your implementation details are concealed, no worries or confusions to other developers who use your code.
In fact, such reactive model is widely used in front-end MV* frameworks nowadays, e.g. Vue.
Regarding your second question, I didn't get it honestly. Just JSON.parse then it's all ordinary JavaScript, isn't it?
the answer to your question on how to insert variables inside a string is:
WELCOME_MESSAGE: 'Welcome ' + name + ', you have a ' + car,
or before defining:
function mesg(name, car){
return "Welcome" + name + ", you have a " + car;
}
mesg(bob, BMW);
in this case, the name and car is defined after.
RequestPager sends all the attributes in server_api to the request as query string. However, sometime I want to exclude a parameter on some condition. This is how, i'm setting the param:
server_api: {
query: function () {
return this.searchQuery
},
type: function(){ return this.searchType }
}
If this.searchQuery is empty, it makes the URL like ?query=&type=1. But I don't want to send query or type when it's empty or when my some other condition fails.
I know the dirty way like:
if(!myCollection.searchQuery){
delete(myCollection.server_api.licensed);
}
But this is not maintainable. Because text time I've to create this function. So, I'm looking for a better way of doing this. Any Help?
If you look at how server_api is used:
_.each(_.result(self, "server_api"), function(value, key){
if( _.isFunction(value) ) {
value = _.bind(value, self);
value = value();
}
queryAttributes[key] = value;
});
you'll see that it uses _.result:
result _.result(object, property)
If the value of the named property is a function then invoke it;
otherwise, return it.
var object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};
_.result(object, 'cheese');
=> "crumpets"
_.result(object, 'stuff');
=> "nonsense"
That means that you can make server_api a function which returns the appropriate object.
I have two Controllers and one shared Service.
First Controller renders some text. Other Controller has a button that had to change the First Controllers scope and rerender text (with new value)
HTML
<div ng-controller="Controller1">
Value is: {{object}}
</div>
<div ng-controller="Controller2">
<button ng-click="changeValue()">Click to change Value</button>
</div>
JS
function Controller1($scope, mainServices) {
$scope.object = mainServices.getValue();
};
function Controller2($scope, mainServices) {
$scope.changeValue = function(){
mainServices.getValue('Hello');
console.log('button is pressed');
};
};
testApp.factory('mainServices', function(){
return {
getValue: function(update) {
var defaultValue = 'Hi';
if( typeof(update)==='undefined') { value = defaultValue; }
else { value = update; }
console.log('should be changed to', update);
return value;
}
}
});
On Plunker http://plnkr.co/edit/IWBEvAfvLbzJ0UD2IqGB?p=preview
Why doesn't this work? How to tell Angular to watch for changes?
The code
Well, I think this is not the best solution, but it is possible for your problem.
Instead of just changing the value itself, you have to create an object before the return statement in the factory.
var obj = {}
And then you just change a property of this object:
if( typeof(update)==='undefined') { obj.val = defaultValue; }
else { obj.val = update; }
And return the object:
return obj;
The other controller can be left unaffected, however you have to change your html:
You have to put in
{{object.val}}
in order to listen to changes.
http://plnkr.co/edit/rjsrkbaQ8QyWGGFI9HYl?p=preview
Why does this work?
It is quite simple: In javascript, if you call a function with a primitive return value like a string (yes, strings CAN be primitives in javascript), it is just a copy of this primitive, so with
$scope.object = mainServices.getValue();
you just pass a value to $scope.object, which is not affected by other calls of getValue()
But if we return an object from getValue(), we get a reference to this object. So if we make changes to this referenced object in getValue(), angularjs will be able to notice the changed object because it is tracked in both controllers.
Therefore we have to reference the same object again and again, but since javascript supports closures, this is pretty easy.
Is there a way to update an observable when the <input> value is changed, but programatically, i.e. by Javascript?
Here is a jsfiddle of this use case that I am not able to make it work: http://jsfiddle.net/qYXdJ/
As you see when the "Update input value by Javascript" link is clicked the observable is obviously not updated, since it is not reflected in the <span>
If you absolutely can't modify the observable directly (which is the best way), you can trigger the "onchange" event (which Knockout uses internally). With jQuery, it's a simple matter:
$('#update').on('click', function() {
$('#input2').val('New Value').trigger('change');
});
If you don't want to use jQuery for whatever reason, have a look at this question.
As cyanfish pointed out the correct way is to update the observable.
If the problem is your code doesn't have access to the observable, for example you're writing a bookmarklet to automatically fill out a form, then you can gain access to the observable like this:
function setValue(input, value) {
var bindingsString = input.getAttribute('data-bind');
if (bindingsString) {
var bindings = ko.bindingProvider.instance.parseBindingsString(bindingsString, ko.contextFor(input), input);
if (bindings.value) {
bindings.value(value);
} else if (bindings.checked) {
bindings.checked(value);
} else {
input.value = value;
}
} else {
input.value = value;
}
}
You have to change the viewModel 'name' property instead of input field value, because it's observable, and any changes on the property will be reflected to all binded html elements.
var viewModel = {
name: ko.observable()
};
ko.applyBindings(viewModel);
document.getElementById('update').onclick = function(){
viewModel.name('New Value');
//document.getElementById('input2').value = 'New Value';
}