See twiddle here: https://ember-twiddle.com/2150099882893760cef237ff2bd22e85
Basically, in crit-service I create Ember Objects "Crits" and "Crit", and fill them with some data.
The crit-service is used by two different components, which basically do the same thing: display the Crits.
The problem is that the "change" buttons do not work. By debugging, I see that the values are changed, but the view is not updated. Why is this? Since Ember.Object is Observable, shouldn't setting a value notify the template? And most importantly, how can I get this to work?
P.S. I've seen a workaround by using Ember.A() instead of Objects. However, this would add boilerplate, as my data model is really objects and not arrays of key-value pairs.
This seems to be an issue with the {{#each-in}} helper which does not reload on changes. A quick fix is to use the {{get}} helper.
So instead of this:
{{#each-in obj as |key val|}}
{{key}}={{val}}
{{/each-in}}
Do this:
{{#each-in obj as |key|}}
{{key}}={{get obj key}}
{{/each-in}}
However, this will never work if you add additional properties.
here is a working twiddle with that solution.
Another solution that will always work is to call .rerender() on the component. This is save thanks to glimmer, which does only update the parts of the DOM that have changed. However, you would have to call it on your common root component of the two components, or on both components.
Related
I'm trying to implement such algo using vueJS:
We have two input fields, for example, we translate from one language to another. These two fields belongs to the two components that are made for easier integration;
These two components are the same type and are in the one parent "host" component;
Whenever we type something inside one component, it immediately being caught by the parent component and then translates using some algo to value for another one component. Then parent should put that value to the second child's field;
Communication should be in two ways in both child components with their parent.
I'm done with this algo, but I think I'm doing wrong.. I'm receiving warning that I mutate prop which is a bad idea.
If you know the best way of doing such algo, PLS, advice it.
Here is my implementation:
Parent has two values: for the first input and for the second. They are bound to children' props using .sync modifier. Like this: :value.sync="firstValue" and :value.sync="secondValue";
Inside children we have prop named value(on which we sync our parent's data). Then we simply bind this prop using v-model: v-model="value";
And after all raise event for .sync updates: #keyup="$emit('update:value', value);";
On the parent bind some functions for events handling, that doesn't matter.
Again, algorithm performing well. But I think I'm doing bad with such things. I mutate prop value in each component using v-model, which isn't good. But .sync modifier won't work on value - I've checked, it only works on props, because v-bind only works on props as I can see...
So, I'm very glad to hear advises for improvement..
You could consider using a store like Vuex in this case.
After parent component did the transformation, dispatch action and mutate global store property.
In child 2 component, watch to the changes to this store property and then update it's input model
You can use v-model with components. Docs
That said, I would do something like this: Codepen
<field v-model="text"></field>
<field v-model="processedText"></field>
and then use watchers
watch: {
text(text) {
this.processedText = this.process(text);
},
processedText(text) {
this.text = this.process(text);
}
}
Of course there's probably more than one way to do it, but this seems like the shortest route :)
Thanks to all answers you guys provided. I've learned some new tricks from these advises. But I'd like to make a clarification and provide the best(for my opinion) choice!
For the start, my method:
In the child component: Note, that I'm using VueJs2, so the configuration of CHILD might be different to use v-model. For inputs inside children we set: #input="$emit('input', $event.target.value)" v-bind:value="value", where value is a defined prop;
In the parent component: We don't need to set watchers. The problem of this decision(which is provided in vch's answer) is that it might be the root of infinity recursion inside parent's component. So we could just listen for input events inside children components, like this: #input="someValueChanged">. And we could even have a two-way listener field inside parent, by just using v-model on our custom components: v-model="some value".
The pros of my methods are clear: we have universal component for ANY input type. We can make a custom listeners by listening directly to emitted events. And we can put a needed modifiers, like .lazy on those custom inputs. There are no disadvanatages tbh, or I haven't found them yet.
vch's answer:
Cool method, I've tried it, but it seems to be badly recursive inside parents component. Your codepen works, but in my case(VueJS2) it seems to be broken..
But I've started from your method and just removed useless watchers and added event listeners.
Pavan's answer:
Again, working method. We could really use storage for such case, but I see sort of disadvantages here:
We use not an out-of-box extension - VueX, which have to be installed. I'm fan of built-in methods tbh;
We can't provide separated logic for every input - we'll have to make custom component for each mechanism, cuz they have to listen to different storage variables.
Thx you all! It was very useful!
http://codepen.io/adamchenwei/pen/yagLLZ?editors=0010
I have FormatModule component which is two levels deep from RepeatModule. I want whenever onClick is triggers onClick={this.props.changeFormat.bind(this)} on the FormatModule, it will change all the statues for all the of islamic to islamic: '09999999',, or whichever got passed in from changeFormat function inside the RepeatModule
I heard this is the only way to manipulate state massively when its nested inside a list of components.
For now, when I click on where onClick={this.props.changeFormat is implemented, values are not response to the change. I wonder where is the place I missed link? Since changeFormat is a function that got passed in from the top parent component down to the FormatModule... unless its not the way to do it?
NOTE: I need a solution that not involving Redux or Flux
With a help of colleague, its already fixed in my code pen: http://codepen.io/adamchenwei/pen/yagLLZ?editors=0010
So the issue was that:
I bind(this) in the wrong scope. Should only bind inside the RepeatModule
the newFormat state should follow the format of the original format ( in this case it was an array so newFormat should be an
array as well!
Enjoy!
(Hope whoever voted down my questions come back and learn their stuff before put others down first, especially without any reason)
So I have this computed property inside my component.js: contexts: Ember.computed.oneWay('myService.contexts'),
And I am able to get the content from another action
openHelp(){
console.log(this.get('contexts'))
alert(this.get('contexts'))
}
}
But when I try to use the computed property in Handlebars ({{contexts}}) it's just blank.
I created an Ember Twiddle for this question: https://ember-twiddle.com/38de64d58dcf3298df6d4176f15cbc0e?openFiles=components.my-component-help.js%2Ctemplates.components.my-component-help.hbs
If I have an array foo: [ 'foo','bar'] and I do {{foo}} it outputs in handlebars. But if I make foo a computed property that gets [ 'foo','bar'] from and do {{foo}} I get nothing.
Here's the solution: https://ember-twiddle.com/e9c2ef05e27013a389e0b2bfdaec3d40?openFiles=services.my-service.js%2Ctemplates.components.my-component-help.hbs
There were two issues:
contexts is an array. When you console.log or alert it, those methods internally in some browsers JSON.stringify the object for you for your convenience. Ember will not do that. You need to format the array yourself or, as I did, each over it. For debugging purposes, feel free to use the log helper.
Computed properties on arrays are watching for array mutations through Ember's methods such as pushObject and removeObject. Simply using push or splice won't update the computed property.
Can't comment on the above answer which is correct because I don't have enough reputation, but I wanted to add a link to the documentation relating to Ember's observable methods for enumerables:
https://guides.emberjs.com/v2.5.0/object-model/enumerables/
I have a JSON object that I used to create a form. This JSON object is parsed by KnockoutJS.
Now, when I modify the form, I want the JSON object to be updated according to the modifications made in the form. The thing is that I don't know in advance how the form will be like but I know in the JSON Object which fields need to be updated.
I really don't know what is the best way to procede. I know that I could reconstruct the JSON Object each time something has changed but this seems like a bad idea and a tedious process.
Is there a simple way to map each JSON Object field to form items in KnockoutJS ?
Here's a JSFiddle of what I'm currently doing:http://goo.gl/ZBaV7
Update :
I realized something interesting with this line:
<input type="text" data-bind="value: $data.value, attr : { disabled: $data.disabled }" />
I'm accessing the value directly from the array via ($data.value). Is there a way in the html to say to knockout to bind to this particular attribute in the array. I know that if the array would get reordered everything would get messed up but since I know that the only thing that can changed is this property I'm ready to take this risk ?
In other words, is there a way to manually say that when this value changes to change it in the array such as
data-bind="onChange: $data.value = this.value"
Is there a simple way to map each JSON Object field to form items in
KnockoutJS ?
Yes, If I understand what you want to do correctly. As of now, the values in your view model are not observables and won't be updated automatically as the form values change. There is a plugin to handle this mapping.
http://knockoutjs.com/documentation/plugins-mapping.html
Example: Using ko.mapping
To create a view model via the mapping plugin, replace the creation of
viewModel in the code above with the ko.mapping.fromJS function:
var viewModel = ko.mapping.fromJS(data);
This automatically creates observable properties for each of the
properties on data. Then, every time you receive new data from the
server, you can update all the properties on viewModel in one step by
calling the ko.mapping.fromJS function again:
ko.mapping.fromJS(data, viewModel);
Hopefully this helps.
If your Knockout ViewModel matches your form, you could just use the built in ks.toJSON()
http://knockoutjs.com/documentation/json-data.html
A better option, especially if your form is large or complex, is to use either the mapping or viewmodel plug-ins
http://knockoutjs.com/documentation/plugins-mapping.html
http://coderenaissance.github.io/knockout.viewmodel/
The simplest way to turn your json model into a usable knockout model is with the mapping plugin.
Alternatively, you can manually copy fields from your json model into ko.observable members of your view model, which give you more control and lets you choose to skip read-only properties.
I'm trying to create a reactive array similar to that seen here (How can I make a reactive array from a Meteor collection?) but the solution isn't working as expected.
The code below creates an array and updates it properly, but any subsequent updates to the 'foo' collection are not seen by the typeahead. I've also tried using the jquery-ui autocomplete but with the same result.
#Coll = new Meteor.Collection "foo"
if Meteor.isClient
Template.myForm.rendered = ->
Meteor.defer ->
$('.inputs').typeahead
source: Template.myList.test()
Meteor.autorun ->
Template.myList.test = ->
_(Coll.find().fetch()).pluck "Name"
I'm guessing the problem is related to the fact that I'm relying on the fairly hacky "Template.myList.test" to store the array. I tried using the following:
Meteor.autorun ->
test = _(Coll.find().fetch()).pluck "Name"
but the typeahead wasn't able to find "test".
As such, it's possible that the solution here will be to change how I'm storing the array rather than changing how the find() is executed.
If you want an array to be reactive, you should probably be using a Meteor collection. Reactive updates to Arrays are horribly inefficient because the entire array gets changed with a change to a single element.
In general however, autocomplete solutions in Meteor shouldn't depend on static arrays like in traditional code. I encourage you to try out my Meteor-aware autocomplete package that is built specifically on top of meteor collections:
https://github.com/mizzao/meteor-autocomplete
Use Session
Meteor.autorun ->
Session.set 'test', _(Coll.find().fetch()).pluck "Name"
Template.myList.test = ->
Session.get 'test'