Partial view with Javascript - javascript

I have a drop down list (telerik dropdownlist) and for every option i call a different partial view from my controller.
onChange = function (e)
{
var product = e.value;
if (product)
{
$.post( myUrl,
{ CodigoProduto : product }, // passing the product to my controller
function (retorno) {
// insert the partial view in a div
$('#AreaGenerica').html(retorno);
}
);
}
}
the first option returns a partial view that contains a javascript code
<script type=\"text/javascript\">
function PartialViewFunction () {
alert("test");
}
</script>
<h2>Option A</h2>
and the second option returns a partial view without any javascript code.
In my view i call the function inside the partial view
ViewFunction = function () {
// check if the function 'PartialViewFunction' exists.
if (typeof PartialViewFunction === "function")
{
PartialViewFunction();
}
};
My problem is that if i choose the first option and then the second the 'PartialViewFunction' is still being called but it shouldn't because it doesn't exists inside the second partial view.
I tried to remove the div containing my partial view following this answer but it didn't work.
Thanks in advance and i hope you can understand my english.
EDIT:
#(Html.Telerik().DropDownList()
.Name("grpAutorizacaoPublicacao")
.SelectedIndex(0)
.Effects(e => e.Opacity())
.ClientEvents(e =>
{
e.OnChange("onChange");
})
.DataBinding(db => db.Ajax().Select("_ListaGrupoAutorizacao", "Publicador")))

As a general rule I would avoid including JavaScript in partial views. You can search for advice about that. This might mean refactoring your JS to be generic and always included from the view proper. Such a design pattern would, I think, solve your problem, or at least make it easier to understand and fix.
Alternatively, it seems like you may need to limit onChange function to just the first select element. The code you have shown does not make it clear what calls the onChange event. You could include the html of the select element before and after the first selection is made.

Related

How to run a script after an Angular component displays?

I have a table that gets populated with data after a search. I would like to run a script to hide some of the columns after the table is loaded.
I know of ngAfterViewInit() but don't want to use that because the script needs to run after the search button has been clicked and data populated, not after the page load
I have the function where the code to hide columns after the data is supposed to be loaded but when I step through in the browser debugger, the function is getting hit before there's actual data displayed in the table which is why it's not working
//not working
this.customerService.getCustomers(query).toPromise().then(data => {
this.customers = data;
this.hideColumns();
});
hideColumns() {
$('.addressOld').hide()
}
Where can I call this function instead?
You can do what you need this way (and don't need to use jQuery to only hide/show elements, use Angular and pure html properties):
.html
<button (click)="doSearch()">search</button>
<div [hidden]="customers"></div>
.ts
class YourComponent {
customers = null;
doSearch() {
const query = 'something';
this.customerService.getCustomers(query).toPromise().then(data => {
this.customers = data;
});
}
}
Use the [hidden] property from html element to hide it, as explained here.

Share Angular Expression with onclick Function

Let me try explaining things again. I have an app running in a javascript framework on Node.js w/ Bootstrap. In one of the bootstrap panels I embedded an Angular Elasticsearch search client. Here's some code that displays the results:
<section class='results'><article class='result' ng-repeat='result in results track by $id(result)' /><div id="addThisInfo" ng-repeat='ng-repeat='result in results track by $id(result)' style="display: none;"> {{$id}},{{result.code}},{{result.expression}},{{result.source}}</div>Code:{{result.code}}<br>Description: {{result.expression}}<br>div ng-if='result.source ==4'> Type: Source 1</div><div ng-if='result.source ==10'> Type: Source 2</div></article> </section>
There are 5 results per page.
Note that the only portion of this app written in Angular is this search mechanism.
Ultimately I want the user to be able to click on either the text of the desired result or click on a button that would add their choices to a separate table.
Here's how it works rignt now. When I click on any of the 5 results per page it invokes this:
document.addEventListener("click", function() {
var packy = document.getElementById("addThisInfo").innerHTML; //div above
addInfo(packy);
});
It doesn't matter which ever one of the results I click, it always sends the "first" result of the active page. Even though the div "addThisInfo" has been repeated and is part of the result, it doesn't
The web socket call looks something like this:
{"params":"0ij,I77.812ZZ,"Full Expression","100"}
I'm not having any luck uploading an image of the Inspect Element. But there you can see that all of the data is there.
I'd like to schedule a http://join.me session with someone so I could show you how it's currently working. Help is greatly appreciated!!
Thanks!
why must you pass them individually? I'm unsure of what you're trying to accomplish, exactly, but I'd handle it something like this:
for the markup:
<button ng-click="addInfo(result)">
then in the controller
$scope.addInfo = function(result) {
var foo = result.code,
bar = result.source;
etc...
}
Angular. directive('notNgClick', function () { return {
restrict: 'A',
scope: {
values: '&'
},
link: function (scope,elem, attr) {
elem.addEventListener(function () {alert (scope.values.someValue ) } )
}
}
}

How to get a tag-specific custom Javascript function called from a jQuery click() function

I have a bunch of HTML toggle (two-state) widgets. I declare them in my HTML like this:
<div class='togglewidget'></div>
<div class='togglewidget'></div>
This looks nice because it is clean and simple.
Then I use jQuery to fill in the elements that make the widgets actually look like they are supposed to. I also attach a function to click events:
$(".togglewidget").append("/* stuff to make the widget work */");
$(".togglewidget").click(function() {toggleWidget(this);});
The toggleWidget() function does animation and handles state change. This all works great.
What I want is to add a custom function to each widget that gets called from the toggleWidget() function depending on state. I would like something like this:
<div class='togglewidget' customfunc='clicked1'></div>
<div class='togglewidget' customfunc='clicked2'></div>
function toggleWidget(this) {
var widget = $(s);
if (/* check for toggle state */) {
// state change of widget is animated (I know how to do this)
...
// call custom function for this individual widget (how to do this?)
widget.GetCustomFunc(true);
} else {
// state change of widget is animated (I know how to do this)
...
// call custom function for this individual widget (how to do this?)
widget.GetCustomFunc(false)
}
}
Then I define clicked1() for the first widget and clicked2() for the second widget. Each widget animates correctly based on behavior in toggleWidget, and clicked() and clicked2() define what actually happens when the individual widget is toggled.
What is the best way of doing this?
I think I could add an onClick="" to each togglewidget div, but then I don't have easy access to the state of the widget, and ordering might be an issue. I think the idea of having a "customfunc" property of the tag might work, but I think that would not validate as HTML.
You could check for the customfunc attribute using jquery:
if($(this).attr('customfunc')) =='customfunc1') {
}
else {
}
Try
function toggleWidget(this) {
var widget = $(s);
if ( /* check for toggle state */ ) {
// state change of widget is animated (I know how to do this)
...
// call custom function for this individual widget (how to do this?)
var cufn = widget.attr('customfun');
if ($.isFunction(widget[cufn])) {
widget[cufn]();
}
widget.GetCustomFunc(true);
} else {
// state change of widget is animated (I know how to do this)
...
// call custom function for this individual widget (how to do this?)
var cufn = widget.attr('customfun');
if ($.isFunction(widget[cufn])) {
widget[cufn]();
}
widget.GetCustomFunc(false)
}
}
Try this:
function toggleWidget(this) {
var widget = $(s);
var indexOfWidget = $('.togglewidget').index($(this));
// for example to add custom property
$(this).attr('customFunc', 'onClick' + indexOfWidget);
}
Try this:
var fun=$(this).attr('customfunc');
if(fun){
eval(fun+"()");
}
This will call any function returned by "customfunc" attr

Windows 8 Grid Template JS App, CSS manipulations dont show on all selected items in groupedItems view

I'm using the Win8 Grid View Template to display infos from a news site. In the lower menu bar i have implemented a function wich shuts off the titles, so that only the pictures are still visible.
This function is in a "global.js" file which is included in the "default.html" so it's available everywhere and it looks like this:
//function to turn titles off and on
function titleToggle() {
var titles = document.getElementsByClassName("item-overlay");
for (var i = 0; i < titles.length; i++) {
if (Global.titlesAreOn) {
titles[i].style.display = "none";
}
else {
titles[i].style.display = "";
}
}
Global.titlesAreOn = !Global.titlesAreOn;
};
So when i call this function from the menu bar it works for the first items, but when i scroll the end of the groupedItems view (hubview) the titles are still there. When i then scroll back to the beginning the titles are there again too.
I'm also calling the titleToggle function from the ready() function of the "groupedItems.js" to check whether or not to display the titles depending on a global variable. When i do that (whenever i come back to the hubpage) it works all the way, just as expected.
ui.Pages.define("/pages/groupedItems/groupedItems.html", {
navigateToGroup: function (key) {
nav.navigate("/pages/groupDetail/groupDetail.html", { groupKey: key });
},
ready: function (element, options) {
appbar.winControl.disabled = false;
appbar.winControl.hideCommands(["fontSizeBt"]);
appbar.winControl.showCommands(["titleToggle"]);
if (Global.titlesAreOn == false) {
Global.titlesAreOn = true;
Global.titleToggle();
}
I made a short video to show the problem, because its kinda hard to explain --> http://youtu.be/h4FpQf1fRBY I hope you get the idea?
Why does it work when i call it from the ready() function?
Does anyone have an idea? Is it some kind of automatic item caching in order to have better performance? And how could this be solved?
Greets and thanks!
First, here is why this might be happening - WinJS is using single page navigation for the app experience. This means that when you navigate to a new page, actually you don't. Instead the content is removed from the page and the new content is loaded in the same page. It is possible that at the moment you press the button not all elements have been loaded in the DOM and therefore they cannot be manipulated by your function. This is why when you call it from the ready() function it works - all contents are loaded in the DOM. It is generally better to do things in the ready() function.
About the behavior when you slide back left and the items are again reloaded with titles - for some reason the listView items are reloading. Maybe you are using live data from the news site and they are refreshing with the listView control's template again. I cannot know, but it doesn't matter. Hiding the elements is not the best approach I think. It is better to have two templates - one with a title element and one without. The button click handler should get the listView controls(they have to be loaded) and change their templates.
ready: function (element, options) {
var button = document.getElementById('btn');
button.addEventListener("click", btnClickHandler);
}
And the handler:
function btnClickHandler(e) {
var listView = document.getElementById("listView").winControl;
var template2 = document.getElementById("template2");
listView.itemTemplate = template2;
};

Why is Template.mytemplate.rendered getting triggered twice when trying to animate live changes in Meteor.js?

I have some issues figuring out how Meteor.rendered works exactly. I'm trying to animate a live change in Meteor, very much like this question does. Whenever I click on an element, I want it to blink.
However, the template gets rendered twice every time an element is clicked, which in turn triggers the animation two times. Here is the code:
Template
<body>
{{> myItemList}}
</body>
<template name="myItem">
<div class="item {{selected}}">
<h4>{{name}}</h4>
<p>{{description}}</p>
</div>
</template>
<template name="myItemList">
{{#each items}}
{{> myItem}}
{{/each}}
</template>
Javascript
Template.myItemList.helpers({
items: function () {
return Items.find();
}
});
Template.myItem.helpers({
selected: function () {
return Session.equals('selected', this._id) ? "selected" : "";
}
});
Template.myItem.events({
'click .item' : function () {
Session.set('selected', this._id);
}
});
Template.myItem.rendered = function () {
$(".item.selected").fadeOut().fadeIn();
};
My understanding is that every time Session.set is called, every template that uses Session.get for the same key is re-runed, as explained in the documentation. So my guess is that somehow, Session.equals causes 2 reruns of the template, maybe? If I change the selected helper with this code:
Template.myItem.helpers({
selected: function () {
if (Session.get("selected")) === this._id)
return "selected";
else
return "";
}
});
Then the animation gets triggered 3 times instead of 2.
With all that in mind, my questions are:
Why is Template.myItem.rendered getting triggered twice? What exactly goes on behind the scenes?
How can I fix this to only have my animation triggered once?
ANSWER DETAILS
So to comment on #Xyand solutions:
Moving the animations to the click event with a Meteor.setTimeout() definitely works, but felt a bit hacky to me.
The solution was actually quite simple, and already given in this question. The code looks like this:
Template.myItem.rendered = function () {
if (Session.equals('selected', this.data._id))
$(this.firstNode).fadeOut().fadeIn();
};
I first tried that on a different project that had a different template markup and it didn't work, this is why I created this minimal project to see where things went wrong. Turns out that for this to work, you absolutely need to have a sub-template myItem called inside the myItemList template. If the template looks like this,
<template name="myItemList">
{{#each items}}
<div class="item {{selected}}">
<h4>{{name}}</h4>
<p>{{description}}</p>
</div>
{{/each}}
</template>
and thus every helper function and the rendered function are called for the myItemList template, the code won't work because inside the rendered function, the template instance will have a .data attribute that is undefined.
The template gets rendered twice because two different instances of the same template get rendered. One that changes selected to "selected" and another that changes it to "". This also explains why you have 3 renders when you switch equals to get.
Mixing imperative javascript with declarative is always a mess. So here are my two suggestions:
Add an if inside the rendered function to make sure you call the transition only in the selected item template instance.
Move the transition to the event itself. You might need to use Meteor.setTimeout.
Placing the transition in rendered might not be the best option as it will happen every time the template renders. For instance if name changes. Think what would happen when the code gets longer...
As others have said, template gets rerendered every time client fetch another portion of data, whether it's because the data has changed or because of latency.
Thankfully, there's another function that's only called once when the template is created, that is - template.created. It has the downside that you cannot access view elements from there, as they weren't yet created. You can, however, mark the template as "fresh" and schedule animation on the next render:
var animate;
Template.myItem.created = function() {
animate = true;
};
Template.myItem.rendered = function() {
if(animate) {
animate = false;
... // Do the animation here.
}
};
Update:
Template.myItem.events({
'click .item' : function () {
animate = true;
Session.set('selected', this._id);
},
});

Categories