View not updating even the html changed - javascript

I used Vuejs, and the following code is from a function under the methods property.
I try to manipulate the DOM inside a promise, where I retrieve info from the database and use that info as a selector:
.then(() =>{
for (var i in obj){
for (var j in obj[i]){
document.getElementById(i + j).style.display = 'none';
}
}
})
I tried both jquery and js, and log them out to see if they selected the right element, and they did print out the correct element that I want to manipulate, however, when I try to addClass() removeClass() or hide(), even though these actions are executed successfully(I verified it by logging out the changed element), they are not updated in the view, when I set the display property as "none", and I checked the HTML code, these elements are not in the HTML, but they are displayed normally in the view.
I wonder why this happened, and wanted to know if there's a proper way to update the view as well, thank you.

Vue's DOM objects are kept in a Shadow DOM in browsers that support it. This provides isolation from the rest of the DOM but will appear hidden in some cases. This is why your attempts to alter the DOM aren't having any effect.
The very point of Vue is to remove the need to directly alter the DOM. The first parts of the Vue Guide show just how easy it is to update the view simply by altering data.

You're missing style. document.getElementById(i + j).style.display = 'none';
As an aside, you can and should really do this through Vue using the data object. Vue is data oriented and its reactivity works using data, computed, etc. Directly manipulating the DOM as you are doing will bypass any reactivity.

Related

How to know when iron-list is finished creating html

Background
I am creating a a polymer iron-list and populating the list by setting the items property directly, like so: document.getElementById('itemsList').items = data;
When user changes category I change value of items in the above manner (retrieved with ajax). This works perfectly, but I now need to change the options in a select depending on my list category. I was able to do this using templating but it's cumbersome and may not even work later when the options need to be dynamic.
I would like to simply hide certain options with JavaScript but the select I want to manipulate is not present immediately after doing .items = data. I need a callback or some other way to detect when iron-list is done inserting HTML.
Research
I looked through the documentation and couldn't find any references to callbacks or events other than iron-resize, and that doesn't look helpful.
I could potentially figure it out by listening DOMNodeInserted events but that's probably worse than the solution I've already got.
A setTimeout would also work, but is also a bad solution.
From miyamoto: I could check _itemsRendered on iron-list which gets set to true, but I would probably need to do setInterval to check, also bad.
Question
Is there a callback of any sort to let me know when iron-list is finished creating HTML? Failing that maybe an event or something else I could use to know when it's done?
Update:
Answer seemd to be dom-change event, but now it seems it doesn't always fire. See Polymer iron-list does not always fire dom-change event
Something like this then. The computed bindings computeItems and computeOptions update their value as data or listCategory changes for the former, or item or listCategory for the latter changes. This allows polymer to manage all the data bindings for us: you just have to provide some function to compute it.
NB: That computing functions are not called until all dependent properties are define, i.e. not undefined.
<dom-module is="some-element">
<iron-list items="{{computeItems(data, listCategory)}}">
<template>
<select>
<template is="dom-repeat" items="{{computeOptions(item, listCategory)}}" as="option">
<option value="{{option.value}}"></option>
</template>
</select>
</template>
</iron-list>
</dom-module>
<script>
Polymer({
is: "some-element",
properties: {
data: Array,
listCategory: String
},
computeOptions: function(item, listCategory){
return item.options.filter(e=>e.category === listCategory)
},
computeItems: function(data, listCategory){
return data.filter(e=>e.category === listCategory)
}
})
</script>
We are currently trying to go a different route, but if anyone else needs to accomplish this it looks like the dom-change event should tell you.
Update:
This does not work in all cases, see Polymer iron-list does not always fire dom-change event

add attribute to DOM element

I have a modal form that is generated using bootstrap 3. It doesn't look like there is a reliable way to determine when that form is being shown onscreen. I am attempting to create one. I attached two events to my DOM element that signal when it is shown and when it is hidden.
jq_modal_login_form = $('#modal-login-form')[0]
jq_modal_login_form.on('shown.bs.modal', function () {
jq_modal_login_form.active_onscreen = true;
});
jq_modal_login_form.on('hidden.bs.modal', function () {
jq_modal_login_form.active_onscreen = false;
});
I tried to give an attribute named active_onscreen to the DOM element above. When I look at the DOM element in the debugger later, the attribute is not present.
I should mention that I am VERY new to javascript. Is attribute even the right word to use here? It looks like attribute is a bit of a misnomer as well. It could be an attribute of the object but could also be an attribute of the object.attributes attribute, right? I assume the later is where styling ect., goes and is not what I want to change. Does anyone have some insight as to what I should be doing here?
In jQuery:
$('selector').attr('attribute_name', 'value');
However, you can should only use predefined attributes as creating custom attributes requires additional setup (see this question) that is not necessary in your case.
In your case, you may just want to add a active_onscreen class to the element. Classes are meant to be used to identify elements (and not just for CSS), so they are perfect for this applicaiton. You would use this to add a class to an element:
$('selector').addClass('active_onscreen').
When it is no longer active, you would use this to remove the class:
$('selector').removeClass('active_onscreen').
What you are doing here is adding a property of the DOM object - not an attribute of the element.
Adding an attribute does not necessarily make the property mirror it. Only built-in properties do this.
If you want to set an attribute, but not the property, you can use jQuery's .attr() method.
If you just want to see if a given modal is open, Bootstrap does that for you. You can check the bs.modal data attribute:
$("element").data('bs.modal').isShown;
or a class (but this method is prone to race conditions):
$('#myModal').hasClass('in');

backbone not rendering the $el but can reference element directly

This is killing me, being reading the examples on this site but can't figure out why it works like this.
I want to pass back values to my view, which has buttons that you can use to change the values.
If I use the following
this.$el.empty().html(view.el)
View.el contains the correct html, but those not render on the screen. If I use the following
$("#handicap").html( view.el);
The values get displayed on screen but the events no longer get picked up eventhough if I put an onclick function in the html code it kicks off.
Ideally I would like to get this.$el.empty().html(view.el) working. It has to do with context but can't see why.
I have created a jsbin here http://jsbin.com/iritex/1/edit
If I have to use $("#handicap").html( view.el), do I need to do something special to unbind events. I have tried undelegate everything but that didn't do the trick either.
thanks
A Backbone View's el property will always contain a reference to a valid DOM object. However, that DOM object may or may not be in your display tree. It's up to you to make sure it's in the display tree when you need it to be. This functionality lets Backbone maintain the state of it's View element without it being rendered to the screen. You can add and remove a view from the screen efficiently, for example.
There are a few ways to get your View's element into the display tree.
1) Associate the view with an existing DOM element on the page by passing in a jquery selector to the initializer as the "el" property.
var view = new MyView({el: '#MyElementSelector'});
2) Associate the view with an existing DOM element on the page by hardcoding the jQuery selector it into the view's "el" property.
var MyView = Backbone.View.extend({
el: '#MyElementSelector'
});
3) Render it to the page from within another view
var view = new MyView();
view.render();
this.$el.empty().html(view.el);
If you're interested, I show examples in a Backbone Demo I put together.
You need to put both views into the DOM. Wherever you create the view that above is this needs to be inserted into the DOM. If you do that, then the first line will work fine this.$el.empty().html(view.el).

knockout.js: update bindings?

when I inject any new elements into the DOM after ko.applyBindings(); was called, then knockout won't recognize these new elements.
I can understand why this is happening - they are just not indexed by knockout.
So, at first I thought this would be solved by just calling ko.applyBindings() again, after adding my new elements, BUT then I realized that for every ko.applyBindings() call you make, the according events get fired multiple times. So after applying five times, a click: binding will be fired five times, so this is not a desireable solution ;)
Is there anything like ko.updateBindings() or something else, to tell knockout to, well... update the element bindings?
greetings,
Chris
Each time you invoke ko.applyBindings the entire DOM is inspected for bindings. As a result you will get multiple bindings for each element if you do this more than once. If you just want to bind a new DOM element you can pass this element as a parameter to the applyBindings function:
ko.applyBindings(viewModelA, document.getElementById("newElement"));
See this related question:
Can you call ko.applyBindings to bind a partial view?
Without knowing what you're up to exactly, it seems like you're going the wrong way about this. Your view should be driven by your view model. So you shouldn't be directly adding DOM elements you then need to apply knockout bindings to.
Instead you should be updating your view model to reflect the change in the view, which then causes your new element to appear.
So for example, for your $('body').append('Click me!');, rather than adding the DOM element when the button should be visible, control the button visibility using the view model.
So your view model includes
var viewModel = { clickMeAvailable: ko.observable(false) }
And your HTML includes
Click me!
When the application state changes so the click me button is available, you then just viewModel.clickMeAvailable(true).
The point of doing this, and a big part of knockout, is to separate business logic from presentation. So the code that makes click me available doesn't care that click me involves a button. All it does is update viewModel.clickMeAvailable when click me is available.
For example, say click me is a save button that should be available when a form is filled in validly. You'd tie the save button visibility to a formValid view model observable.
But then you decide to change things so after the form is valid, a legal agreement appears which has to be consented to before saving. The logic of your form doesn't change - it still sets formValid when the form is valid. You would just change what occurs when formValid changes.
As lassombra points out in the comments on this answer, there are cases when direct DOM manipulation may be your best approach - for example a complex dynamic page where you only want to hydrate parts of the view as they are needed. But you are giving up some of the separation of concerns Knockout provides by doing this. Be mindful if you are considering making this trade-off.
I just stumbled upon a similar problem. I tried to add new elements to container and give those a onclick function.
At first tried the things you did, and even tried the approach ColinE recommended. This wasn't a practical solution for me so I tried SamStephens approach and came up with that, which works perfectly for me:
HTML:
<div id="workspace" data-bind="foreach:nodeArr, click:addNode">
<div class="node" data-bind="attr:{id:nodeID},style:{left:nodeX,top:nodeY},text:nodeID, click:$parent.changeColor"></div>
</div>
JavaScript:
<script>
function ViewModel() {
var self = this;
var id = 0;
self.nodeArr = ko.observableArray();
self.addNode = function (data, event) {
self.nodeArr.push({
'nodeID': 'node' + id,
'nodeX' : (event.offsetX - 25) + 'px',
'nodeY' : (event.offsetY - 10) + 'px'
})
id++;
}
self.changeColor = function(data, event){
event.stopPropagation();
event.target.style.color = 'green';
event.target.style.backgroundColor = 'white';
}
}
ko.applyBindings(new ViewModel());
</script>
You can play with it in the JS Fiddle I made.

CKEditor: Class or ID for editor body

I have an instance of CKEditor on a page. I am trying to give the CKEditor's body a class or ID so it matches some styles I have defined in a stylesheet.
There is a API documentation that should give access to the respective DOM elements, but I can't seem to get it working. All objects I try to query that way turn out undefined.
Does anybody know how to do this, or how to properly address CKEditor's dom elements?
Edit: Thanks folks, nemisj's answer did it for me but for some reason, I don't get to set the "accepted" checkmark in this question.
Although that part of the API wasn't ported from 2.x at the time that this question was placed, now it's easier to use the bodyId and bodyClass config options.
Of course, the explanation by nemisj is good and can be useful for other things, but you must remember that each time that you switch away from design (to source view), the iframe is destroyed, so you'll need to reassign your attributes if you do it manually.
If you are talking about CKEditor( version 3), then there is a possibility to get any DOM instance inside the editor itself. Every CKEditor instance has reference to it's document via "document" property.
var documentWrapper = edit.document;
This reference represent some public wrapper for all CKEditor nodes, but it also has the direct reference to its node. You can retrieve by getting ["$"] property.
var documentNode = documentWrapper.$; // or documentWrapper['$'] ;
documentNode will represent the DOM instance of the document node inside the iframe. After you have the DOM instance, you can do whatever you want to do with DOM structure, Append, remove, replace classes, rebuild, etc. For example
documentNode.body.className = "zork";
I hope this should be enough.
I had the same problem today in trying to set the bodyClass like this:
CKEDITOR.replace( 'editor1',
{
fullPage : true,
bodyClass : 'myClass'
});
What I found is that in this version (3.3.1), if you set fullpage = true, setting the bodyId or bodyClass does not work, but if you set fullPage = false, it does work.
Hope this helps.
From the Manual:
<static> {String|Array} CKEDITOR.config.contentsCss
The CSS file(s) to be used to apply style to the contents. It should reflect the CSS used in the final pages where the contents are to be used.
config.contentsCss = '/css/mysitestyles.css';
config.contentsCss = ['/css/mysitestyles.css', '/css/anotherfile.css'];
Default Value:
<CKEditor folder>/contents.css
Don't know that editor, but as they all work the same way, you probably can't access the DOM elements created by the instance because they are created after the page has finished loading, and the DOM is ready as well. So, any new DOM elements added after that, theorically will not exist.
Still, you can try TinyMCE editor, wich has a "partnership" with jQuery javascript library to manipulate all you want, and the editor itself is pretty easy to change in almost every way.
Your queries may return undefined because the editor instances are placed inside an iFrame and your query is not looking there?
In config.js, write this code
config.bodyId = 'contents_id';
then you see body id in Ckeditor but when you want to access this id please use
$('#parent_id').find('iframe').contents().find('#within_iframe')
there $('#parent_id') means form_id or any parent which is simply way to access iframe. follow this code to access element in iframe

Categories

Resources