I'm currently building an application using a dotnet core backend and some Vue.js elements in the front. I was able to build the application using regular Vue (non template, plain non es6 javascript syntax) in a cshtml file, but I've been trying to move towards a more modular structure using vue components in .vue files.
The problem I'm running into is that in the original version, I would have access to Json objects when instantiating the root element:
var jsonRenderedWithHtml = #Html.Raw(Json.Serialize(Model.SomeJsonObject));
vm = new Vue({
el: "#root-element",
data: {
vueData: jsonRenderedFromHtml;
}, ...
When I switch over to the component version, from what I've found, I need to render the root element from a javascript file, doing something like this:
import RootElement from "../Vue/RootElement.vue";
import Vue from 'vue';
let vm = new Vue({
el: '#root-element',
render: h => h(RootElement)
});
Then, I would import this script in the #section Scripts part of the cshtml file. Unfortunately, from within the javascript file, there doesn't seem to be a way to pass in data from outside (from the cshtml file). It seems that if I were to instead write an AJAX request inside the vue root instance, it would need to do two server requests to do the same job.
It also seems that I can't use import statements within cshtml (don't seem to be any webpack loaders for cshtml?). Otherwise, I would skip rendering the element in a separate javascript file.
What we did in our application was to output the data added to the ViewData into the view through as json into a script tag and added an object to the window in the script with the previously rendered json. In the beforeCreate we then read the data from the object we added to the window and commit it to our store. Something like:
#{
IHtmlString myObj = null;
if(ViewData["SomeObject"] is ContentResult)
myObj = Html.Raw(((ContentResult)ViewData["SomeObject"]).Content);
<script type="text/javascript">
(function(){
window.obj = JSON.parse(myObj);
});
</script>
You could however add a prop to your App and pass the object in through the prop just create your vue instance in a script tag.
Related
I'm looking for a little guidance and suggestions here. My attempts and theories will be at the bottom.
I have a NextJS project from which I want to export the top level component (essentially the entry file) so that I can use it as a preview in my dashboard.
The nextjs project is very simple. For the sake of simplicity, let's imagine that all it renders is a colored <h1>Hello world</h1>. Then in my dashboard, I want to render a cellphone with my NextJS component embedded and then from the dashboard change the color of the text, as a way to preview how it would look like. I hope this makes sense.
I'm lost at how I could export this component from NextJS and import it into my dashboard. The dashboard is rendered in Ruby on Rails. It would be simple enough to just import the repo from git and access the file directly form node_modules, but I'm looking for a solution that doesn't require installing npm on our Rails project.
Paths I have thought about:
1 - Install npm on Rails and just import the source code from NextJS repo and access the file and render with react (Simple, but we're looking for a non-npm solution)
2 - Bundle the component with webpack and load it directly into rails (does this even work?) - I exported the js and all it did was freeze everything :P Still trying this path for now
3 - Using an iframe and just accessing the page (then I can't pass any callbacks into the iframe to change the color directly from the dashboard)
4 - I cannot separate this component from NextJS to use as a library in both repos. The component we are exporting is the "ENTIRE" NextJS app jsx and it wouldn't make sense to separate in a different repo
Does anyone have a suggestion on how I could achieve this?
I think you could use an iframe with the nextjs app url. Then if you want to change the color, simply add the color in query parameter of the iframe and handle it on nextjs app.
Simple example
Rails view (erb)
<iframe src="#{#nextjs_url}?color=#{#color}" />
NextJS
# do something to get the query param of the page and and set to prop of the component
const YourComponent = ({color}) => {
return <h1 style={{color}}>Lorem</h1>;
}
While trying Hoang's solution, I decided to dive deeper into how to communicate with an iframe and the solution actually feels quite good.
You can set up listeners on either side and post messages in between the projects.
So in my dashboard:
function handleEvent(e) {
const data = JSON.parse(e.data)
if (data.type === "card_click") {
//if type is what we want from this event, handle it
}
}
// Setup a listener with a handler
// This will run every time a message is posted from my app
window.addEventListener("message", handleEvent, false)
const postMessage = (color) => {
const event = JSON.stringify({
type: "color_update",
color,
})
// Find the iframe and post a message to it
// This will be picked up by the listener on the other side
document.getElementById("my-iframe-id").contentWindow.postMessage(event, "*")
}
And on my app:
function handleEvent(e) {
const data = JSON.parse(e.data)
if (data.type === "color_update") {
// Do whatever is necessary with the data
}
}
// Setup listener
// This will fire with every message posted from my dashboard
window.addEventListener("message", handleEvent, false)
const handleCardClick = (cardIndex) => {
const event = JSON.stringify({
type: "card_click",
cardIndex,
})
// post message to parent, that will be picked up by listener
// on the other side
window.parent.postMessage(event, "*")
}
It feels pretty straight forward to communicate with an iframe with this solution.
Im trying to load my script tag or some jquery function after page is completely loaded in vaadin. Im using javascript annotation to load my external js. like this
#JavaScript("vaadin://themes/custom/resources/js/custom.js");
Is there any way out for this to call some js function after page load or dom object created in html.
Yes, there is. Try this approach:
import com.vaadin.ui.JavaScript;
...
StringBuilder script = new StringBuilder();
script
.append("var head = document.getElementsByTagName('head')[0];")
.append("var script = document.createElement('script');")
//...do other stuff to set up the "script" tag (add content, source, whatever)
.append("head.appendChild(script);");
JavaScript.getCurrent().execute(script.toString());
If you do this before the page is fully loaded, then all is good. But if you want to do this after the fact (through some event or click listener), you'll also have to use Vaadin push to push this to the JavaScript to the client. Like so:
import com.vaadin.ui.UI;
...
UI.getCurrent().access(new Runnable()
{
#Override
public void run()
{
//code from above that leads to JavaScript.getCurrent().execute(...)
UI.getCurrent().push();
}
});
And of course, this assumes that you have push enabled in your Vaadin application. Reference https://vaadin.com/book/-/page/advanced.push.html
As per the docs, add the following in your custom.js
$(document).ready(function() {
//call your function
});
I'm trying to implement some static resource improvements into my ASP.net MVC 4 project (VB.net) by changing how static resources such as javascript and css files are retrieved.
I've been following this link (ASP.NET & MVC 4: Cookieless domain for bundling and static resources ) to help accomplish this but I've come across an issue whereby unbundled javascript and css files are not rendered.
Normally when rendering .js or .css bundles you use the following:
#Scripts.Render("~/bundles/jquery")
This will then render each script tag separately in the ~/bundles/jquery bundle when in development mode, and render a single script tag pointing to the minified bundle when in production.
According to the link above, when the scripts are bundled into a single file, you can use the following line:
<script src="#Url.StaticContent("~/bundles/jquery")" type="text/javascript"></script>
This works fine for me for bundled files since the src property is valid and the StaticContent function is able to change the src URL. But when in development mode, the bundled file does not exist as no bundling takes place and all scripts are rendered seperately to the browser by #Scripts.Render and so this method does not work.
Does anyone know if it is possible to create an extension method for the Scripts helper that will accomplish what I need, or am I going to have to do something like this?
#If Misc.IsLocalDev Then
#Scripts.Render("~/bundles/jquery")
Else
#<script src="#Url.StaticContent("~/bundles/jquery")" type="text/javascript"></script>
End If
I managed to find a solution to this problem, so hopefully by putting it up here for all to see this will help others with a similar problem that I had.
Working off the same idea as the workaround I posted in my original question, I created 2 new helper functions to help generate the necessary Script and Style references in my views...
Scripts
<ExtensionAttribute()> _
Public Function RenderScripts(helper As HtmlHelper, async As Boolean, ParamArray Paths() As String) As IHtmlString
If Misc.IsLocalDev Then
Return Optimization.Scripts.Render(Paths)
Else
Dim url As UrlHelper = New UrlHelper(HttpContext.Current.Request.RequestContext, RouteTable.Routes)
Dim html As String = ""
For Each Path In Paths
If async = True Then
html = html & "<script async src=""" & url.StaticContent(Path) & GetAppVersionSuffix() & """ type=""text/javascript""></script>"
Else
html = html & "<script src=""" & url.StaticContent(Path) & GetAppVersionSuffix() & """ type=""text/javascript""></script>"
End If
Next
Return New HtmlString(html)
End If
End Function
So instead of using:
#Scripts.Render("~/bundles/jquery")
I replaced the calls with:
#Html.RenderScripts(False, "~/bundles/jquery")
A few notes on the above method...
I added an async parameter to the function call to allow me to utilise modern browser aynsc scripts.
The GetAppVersionSuffix() function call returns the assembly version which is appended to the end of the scripts source as ?v=1.2.3.4 for example. This ensures that the browser gets a new copy of the scripts and style-sheets when a new version is released.
The Misc.IsLocalDev function is a special function I use to change the way certain parts of the web application behave when I'm developing on my local machine. In this case, it ensures that unbundled scripts and styles are rendered instead of minified/bundled ones to ease debugging.
Styles
<ExtensionAttribute()> _
Public Function RenderStyles(helper As HtmlHelper, ParamArray Paths() As String) As IHtmlString
If Misc.IsLocalDev Then
Return Optimization.Styles.Render(Paths)
Else
Dim url As UrlHelper = New UrlHelper(HttpContext.Current.Request.RequestContext, RouteTable.Routes)
Dim html As String = ""
For Each Path In Paths
html = html & "<link href=""" & url.StaticContent(Path) & GetAppVersionSuffix() & """ rel=""Stylesheet"" />"
Next
Return New HtmlString(html)
End If
End Function
So again, instead of using:
#Styles.Render("~/Content/Style")
I replaced the calls with:
#Html.RenderStyles("~/Content/Style")
I hope this is useful to someone!
I'm trying to pre fill in fields to log in to a forum. However, I don't own the forum. So how do I link my own .js file so that I can fire a function that will pre fill the log in fields?
(Remember I don't own the servers that host the html files, so I cannot hook it up via HTML.)
You can inject your own javascript into a page being displayed by a UIWebView by
1) Put your javascript into a file in your app bundle, for example something like this will inject myFunction().
var script = document.createElement('script');
script.type = 'text/javascript';
script.text = function myFunction()
{
alert("my function");
};
document.getElementsByTagName('head')[0].appendChild(script);
2) Load the .js file and run it using stringByEvaluationJavaScriptFromString:
3) If its important your myFunction() doesn't get added until the dom has loaded, then within the same .js file add some other JavaScript that will ensure that the code in part 1) doesn't get run until you get a dom loaded event.
cross domain javascript fails everytime.
use ajax to retrieve the page you wish.
$(document).ready(function(){
jQuery.ajax(’forumlogin’).done(function(data)
{
$(’body’).html(data)
})
})
then fill in the forms using the forms element.
I'm using Handlebars.js, and currently all my templates live inside script tags which live inside .html files housing dozens of other templates, also inside script tags.
<script type="text/template" id="template-1">
<div>{{variable}}</div>
</script>
<script type="text/template" id="template-2">
<div>{{variable}}</div>
</script>
<script type="text/template" id="template-3">
<div>{{variable}}</div>
</script>
...
Then I include this file on the server-side as a partial.
This has the following disadvantages:
A bunch of templates are crammed into HTML files.
Finding a given template is tedious.
I'm looking for a better way to organize my templates. I'd like each each template to live in its own file. For example:
/public/views/my_controller/my_action/some_template.html
/public/views/my_controller/my_action/some_other_template.html
/public/views/my_controller/my_other_action/another_template.html
/public/views/my_controller/my_other_action/yet_another_template.html
/public/views/shared/my_shared_template.html
Then at the top of my view, in the backend code, I can include these templates when the page loads, like this:
SomeTemplateLibrary.require(
"/public/views/my_controller/my_action/*",
"/public/views/shared/my_shared_template.html"
)
This would include all templates in /public/views/my_controller/my_action/ and also include /public/views/shared/my_shared_template.html.
My question: Are there any libraries out there that provide this or similar functionality? Or, does anyone have any alternative organizational suggestions?
RequireJS is really good library for AMD style dependency management. You can actually use the 'text' plugin of requireJS to load the template file in to your UI component. Once the template is attached to the DOM, you may use any MVVM, MVC library for bindings OR just use jQuery events for your logic.
I'm the author of BoilerplateJS. BoilerplateJS reference architecture uses requireJS for dependency management. It also provides a reference implementations to show how a self contained UI Components should be created. Self contained in the sense to handle its own view template, code behind, css, localization files, etc.
There is some more information available on the boilerplateJS homepage, under "UI components".
http://boilerplatejs.org/
I ended up using RequireJS, which pretty much let me do this. See http://aaronhardy.com/javascript/javascript-architecture-requirejs-dependency-management/.
I use a template loader that loads the template using ajax the first time it is needed, and caches it locally for future requests. I also use a debug variable to make sure the template is not cached when I am in development:
var template_loader = {
templates_cache : {},
load_template : function load_template (params, callback) {
var template;
if (this.templates_cache[params.url]){
callback(this.templates_cache[params.url]);
}
else{
if (debug){
params.url = params.url + '?t=' + new Date().getTime(), //add timestamp for dev (avoid caching)
console.log('avoid caching url in template loader...');
}
$.ajax({
url: params.url,
success: function(data) {
template = Handlebars.compile(data);
if (params.cache){
this.templates_cache[params.url] = template;
}
callback(template);
}
});
}
}
};
The template is loaded like this:
template_loader.load_template({url: '/templates/mytemplate.handlebars'}, function (template){
var template_data = {}; //get your data
$('#holder').html(template(template_data)); //render
})
there's this handy little jquery plugin I wrote for exactly this purpose.
https://github.com/cultofmetatron/handlebar-helper