laravel (blade) and `:href="route(...)"`, `href="{{ route..)}}`, `:action="route(...)"`, and `action="{{route(...)}}` - laravel-8

In Laravel's bootcamp (blade), there are two different syntaxes used for calling route() in a blade view component:
<x-dropdown-link :href="route('chirps.edit', $chirp)">
{{ __('Edit') }}
</x-dropdown-link>
<form method="POST" action="{{ route('chirps.destroy', $chirp) }}">
#csrf
#method('delete')
<x-dropdown-link :href="route('chirps.destroy', $chirp)" onclick="event.preventDefault(); this.closest('form').submit();">
{{ __('Delete') }}
</x-dropdown-link>
My understanding from documentation is that {{ }} is just a shortcut for <?= ?>
and by adding a : to html attribute laravel renders those attributes in the template.
So I started testing variations to see what works and doesn't work. This is what I got:
:href="{{ route(...) }}" doesn't work
href="{{ route(...) }}" works
:action="route(...)" doesn't work
action="{{ route(...)}}" does work
So my understanding makes sense for 1, 2, and 4, but not 3:
What is going on here? Does laravel not have an :action attribute for forms for some reason? or is it due to POSTing rather than GETting? Or is my understanding of laravel's syntax flawed?

It seems putting the : before a laravel/blade component attribute is the way to pass a variable of that name/value into that component class' constructor (passing-data-to-components).
So :href sets __constructor($href) to the string {{route(...)}} in the <x-dropdown-links> class, which laravel/blade constructs when the component is called. Which explains why <form :action...> wouldn't work.
There is an <x-form> component in the extra blade-ui library, but interestingly even there it seems that the action attribute is set with action="" not :action="". So guessing there is an issue related to POSTing after all.

Related

Vue.js How To Manage nuxt keep-alive key?

I am using Nuxt for my vue project. I want to build a multi-tab application. But I could not manage the caching mechanism of nuxt.
The case is that, my full path never contains any parameters even in update paths.
I mean my paths are always like
/myapp/customer/update
instead of
/myapp/custmer/update/:id
So when I try to bind the nuxt key like
<nuxt keep-alive :key="$route.path + ($route.params ? JSON.stringify($route.params) : '')" />
It does not caches anything and keeps loading all lifecyles (beforeCreate, created, beforeMount, mounted...)
If I do not use :key,
then keep-alive works perfect for pages without parameters
but works wrong with parameters. If I route to customer with id:3 once, then when I go to another customer with id:4, it still caches and displays the data of customer with id:3.
Here is my nuxt-link code:
<span
v-for="(tag, index) in tabbedViews"
:key="tag.name + (tag.params ? JSON.stringify(tag.params) : '')"
>
<nuxt-link
:key="tag.name + (tag.params ? JSON.stringify(tag.params) : '')"
:to="{ name: tag.name, params: tag.params }"
#click.native="tabClicked(index)"
>
{{ tag.name }}
<span
v-if="!tag.keepOpen"
class="el-icon-close"
#click.prevent.stop="closeSelectedTag(index)"
/>
</nuxt-link>
</span>
And below is the code that I use for viewing routes
<nuxt keep-alive :key="$route.path + ($route.params ? JSON.stringify($route.params) : '')" />
Any help will be pleasured.
Thank you...
Try to use :nuxt-child-key instead of :key.
https://nuxtjs.org/api/components-nuxt

Whats the simplest and proper way to route in a template

Instead of showing data from a component, I'm currently routing a user to login at /account if they are not authorized, by doing this:
<template>
<q-card v-if="authorized">
<q-card-section>
<DataGrid/>
</q-card-section>
</q-card>
<span v-else>
{{ this.$router.push('/account') }}
</span>
</template>
It's simple and works, but I'm not sure its really correct because although it pushes the user to where I want them to be, the console gets this error:
uncaught exception: undefined
(I'm currently on Quasar v1.9.14)
Basically I want to show the data if the user is authorized or redirect if they are not authorized, or become unauthorized later.
first of all, you do not need to use this in the template.
If you want to route them based on the authorized value, you could probably use a watcher.
Alternatively, I would probably do something in mounted which checks if the user can be there. E.g.
async mounted ()
{
const authorized = await fetch("something")
if (!authorized)
{
this.$router.push('/account')
}
}
Its simpler than I thought. I believe the answer is events. The span can simply change to:
<span v-else #load="$router.push('/account')"/>
<span v-else :class="authorized ? '' : $router.push('/account')"/>
Its even simpler because its a one liner
It will work even after mounted() has already fired and authorized becomes false
Actually, I now can delete similar logic in mounted() (DRY principle)
EDITS:
After proper testing I found it actually does not work with my first #load event example.

"Unable to resolve constructor for: '"dojox.mobile.TextBox"'"

I am facing following issue while working with dojo inline template:
I am creating the template in a html page by defining the template in between the following tags(It has mvc also integrated)
`<script id="createNewItem" type="text/template">
<div>
<div data-dojo-type='dijit/TitlePane' data-dojo-props='open:false'>
<div class='accordionRev'>
<input type="number" name="qty" placeholder="Quantity (required)" data-dojo-type="dojox.mobile.TextBox" class='quantity' data-dojo-props="value: at('rel:','quantity'), class:at('rel:', 'quantityErr')" onchange="cntrl.createQuoteFields(true, this, '{{lineNumber}}');" onkeypress="cntrl.quantityHandler.call(this, '{{lineNumber}}')" onpaste="cntrl.pastehandler.call(this)" tabindex=0/>
</div>
</div>
</div>
</script>'
I will be using this template in another page like this
`<ul id="rfqTitlePane" class="hide"
data-dojo-type="dojox/mvc/WidgetList"
data-dojo-mixins="dojox/mvc/_InlineTemplateMixin"
data-dojo-props="children: at(cntrl.createQuoteList,'items').direction(at.from)"
data-mvc-child-type="dojox/mvc/Templated"
data-mvc-child-props="clickable: true, variableHeight: 'true',
indexAtStartup: this.indexAtStartup,
customTitle: at(this.target, 'title').direction(at.both), titlePaneDOM:at(this.target,'displayDetails').direction(at.from).transform({format:custom.helpers.RequestMarkup})">
<script type="dojox/mvc/InlineTemplate">
<li>
${titlePaneDOM}
</li>
</script>
</ul>`
I am getting "Unable to resolve constructor for: '"dojox.mobile.TextBox"'" error. I actually defined textbox before the template gets loaded. I am not sure where its going wrong
It appears that the question contains wrong markdown, so I'd recommend fixing that so that better answers can be provided. Meanwhile some things I can say are:
You may want to try importing dojox/mobile/TextBox module before running the template.
Recent releases of Dojo prefers AMD format in data-dojo-type e.g. data-dojo-typ="dojox/mobile/TextBox".
dojox/mvc/InlineTemplateMixin looks at <script type="dojox/mvc/InlineTemplate"> instead of <script type="text/template">.
Hope this helps.
Best, Akira

How can I use cshtml files with Durandal?

I got the DurandalJS StarterKit template on VS2012... All works great...
But in some views I need to do something like that:
#if (Roles.IsUserInRole("Administrators"))
{
<p>Test</p>
}
However with durandal all my views are '.html' files... Is that possible to use '.cshtml' files to access some information like that?
Or is there any other way to do that with durandal?
Junior
I am doing it like this:
Create a generic controller for Durandal views:
public class DurandalViewController : Controller
{
//
// GET: /App/views/{viewName}.html
[HttpGet]
public ActionResult Get(string viewName)
{
return View("~/App/views/" + viewName + ".cshtml");
}
}
Register a route:
routes.MapRoute(
name: "Durandal App Views",
url: "App/views/{viewName}.html",
defaults: new { controller = "DurandalView", action = "Get" }
);
Copy Views/web.config to /App/views/web.config (so Razor views work in this location).
This lets me use the normal Durandal conventions (even the html extension for views), and put durandal views as cshtml files in their normal location without adding any more server code.
If you also have static html views, you can also place the cshtml views in a subfolder or use the normal MVC /Views folder.
I wouldn't recommend using ASP.NET MVC with Durandal.
What you are probably looking to do is use the Razor view engine (to get the benefits of a compiler, strong typing etc.) which exists independently from ASP.NET MVC. Just WebAPI for data I/O is more than enough to very efficiently create a Durandal.js application.
If you are interested in using Razor/CSHTML with Durandal and Knockout there is an open source option out there called FluentKnockoutHelpers that may be exactly what you are looking for. It offers much of the 'nice' parts of ASP.NET MVC allowing you to use the awesome abilities of Durandal and Knockout with almost no downfalls.
Source
Live demo using Durandal.js
In a nutshell it provides a bunch of features which makes doing Durandal/Knockout development just as easy as ASP.NET MVC. (You simply provide a C# type that your JavaScript model is based off of for most of the features.) You only have to write JavaScript and un-compiled markup for complicated cases which is unavoidable and no different than MVC! (Except in MVC your code would also likely end up would also be a big jQuery mess which is why you are using Durandal/Knockout in the first place!)
Features:
Painlessly generate Knockout syntax with strongly typed, fluent, lambda expression helpers similar to ASP.NET MVC
Rich intellisense and compiler support for syntax generation
Fluent syntax makes it a breeze to create custom helpers or extend whats built in
OSS alternative to ASP.NET MVC helpers: feel free to add optional features that everyone in the community can use
Painlessly provides validation based on .NET types and DataAnnotations in a few lines of code for all current/future application types and changes
Client side JavaScript object factory (based on C# types) to create new items in for example, a list, with zero headaches or server traffic
Example without FluentKnockoutHelpers
<div class="control-group">
<label for="FirstName" class="control-label">
First Name
</label>
<div class="controls">
<input type="text" data-bind="value: person.FirstName" id="FirstName" />
</div>
</div>
<div class="control-group">
<label for="LastName" class="control-label">
Last Name
</label>
<div class="controls">
<input type="text" data-bind="value: person.LastName" id="LastName" />
</div>
</div>
<h2>
Hello,
<!-- ko text: person.FirstName --><!-- /ko -->
<!-- ko text: person.LastName --><!-- /ko -->
</h2>
Provide FluentKnockoutHelpers with a .NET type and you can do this in style with Intellisense and a compiler in Razor / CSHTML
#{
var person = this.KnockoutHelperForType<Person>("person", true);
}
<div class="control-group">
#person.LabelFor(x => x.FirstName).Class("control-label")
<div class="controls">
#person.BoundTextBoxFor(x => x.FirstName)
</div>
</div>
<div class="control-group">
#person.LabelFor(x => x.LastName).Class("control-label")
<div class="controls">
#person.BoundTextBoxFor(x => x.LastName)
</div>
</div>
<h2>
Hello,
#person.BoundTextFor(x => x.FirstName)
#person.BoundTextFor(x => x.LastName)
</h2>
Take a look at the Source or Live Demo for an exhaustive overview of FluentKnockoutHelper's features in a non-trivial Durandal.js application.
Yes, you can absolutely use cshtml files with Durandal and take advantage of Razor on the server. I assume that also means you want MVC, so you can do that too and use its routing.
If you don;t want the routing then you can set the webpages.Enabled in the web.config, as the other comments suggest.
<add key="webpages:Enabled" value="true" />
I don't recommend that you use .cshtml files as views directly. You're better off placing the .cshtml files behind a controller.
For example, take the HotTowel sample, edit /App/main.js, and replace the function definition with the following:
define(['durandal/app',
'durandal/viewLocator',
'durandal/system',
'durandal/plugins/router',
'durandal/viewEngine',
'services/logger'],
function (app, viewLocator, system, router, viewEngine, logger) {
Note that we added a reference to the Durandal viewEngine. Then we need to replace
viewLocator.useConvention();
with
viewLocator.useConvention('viewmodels', '../../dynamic');
viewEngine.viewExtension = '/';
The first argument to viewLocation.useConvention sets the /Apps/viewmodels/ directory as the location for the view models js files, but for the view location, uses the URL http://example.com/dynamic/, with an extension of '/'. So that if Durandal is looking for the view named 'shell', it will reference http://example.com/dynamic/shell/ (this is because the view directory is mapped relative to the viewmodels directory, hence /App/viewmodels/../../dynamic will give you simply /dynamic).
By convention, this previous URL (http://example.com/dynamic/shell/) will be mapped to the controller DynamicController, and the action "Shell".
After this, you simply add a controller - DynamicController.cs, like this:
// will render dynamic views for Durandal
public class DynamicController : Controller
{
public ActionResult Shell()
{
return View();
}
public ActionResult Home()
{
return View();
}
public ActionResult Nav()
{
return View();
}
public ActionResult Details()
{
return View();
}
public ActionResult Sessions()
{
return View();
}
public ActionResult Footer()
{
return View();
}
}
Create .cshtml files for each of the above actions. This way you get to use controllers, server side IoC et al to generate dynamic views for your SPA.
DurandaljS is a client framework which forms mainly a solid base for single-page apps (SPA).
I assume you are using asp.net web API as your server technology. In that case, you can determine the user's role inside your API controller and based on that return data to the client. On the client you can use Knockout "if" binding in order to show / hide certain areas of your page.
What you perhaps can do is placing this code in the Index.cshtml.
Following link shows how to customize moduleid to viewid mapping
http://durandaljs.com/documentation/View-Location/
by convention durandal tries to find view url in following steps
1) Checke whether object has getView() function which returns either dom or a string ( url for the view)
2) If object does not have getView function then checks whether object has viewUrl property
3) If above two steps fails to produce url or a DOM view drundal falls to default convention
which maps moduleid xyz.js to view xyz.html using view url ( path of Views folder ) defined in main.js
so for moduleid xyz.js path of the view will be views/xyz.html
you can overwrite this default mapping behavior by overwriting convertModuleIdToViewId function.
So there are many ways you can customize your view url for specific model (.js object)
I made an extension to Durandal which gives you the ability to place an applicationContent div in your cshtml file together with the applicationHost div. In applicationContent you can now use both ASP .Net MVC syntax together with knockout bindings.
Only thing I did was put some extra code in the viewLocator.js file which looks for an applicationContent div:
locateViewForObject: function(obj, area, elementsToSearch) {
var view;
if (obj.getView) {
view = obj.getView();
if (view) {
return this.locateView(view, area, elementsToSearch);
}
}
if (obj.viewUrl) {
return this.locateView(obj.viewUrl, area, elementsToSearch);
}
view = document.getElementById('applicationContent');
if (view) {
return this.locateView(view, area, elementsToSearch);
}
var id = system.getModuleId(obj);
if (id) {
return this.locateView(this.convertModuleIdToViewId(id), area, elementsToSearch);
}
return this.locateView(this.determineFallbackViewId(obj), area, elementsToSearch);
},
Your original cshtml file can now do something like this:
<div class="row underheader" id="applicationContent">
<div class="small-5 columns">
<div class="contentbox">
#using (Html.BeginForm("Generate", "Barcode", FormMethod.Post, Attributes.Create()
.With("data-bind", "submit: generateBarcodes")))
{
<div class="row formrow">
<label for="aantalBijlagen">#Translations.Label_AantalBijlagen</label>
</div>
<div class="row">
<select name="aantalBijlagen" class="small-6 columns">
<option>0</option>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
</div>
<div class="row">
<button class="button right" type="submit" id="loginbutton"><span class="glyphicon glyphicon-cog"></span> #Translations.Action_Generate</button>
</div>
}
</div>
</div>
<div class="small-7 columns" data-bind="if: hasPdfUrl">
<div class="contentbox lastcontent">
<iframe data-bind="attr: {src: pdf_url}"></iframe>
</div>
</div>
You can find my fork of the durandal project here and a small blogpost of what and how I did this here.
I'm not very familiar with DurandalJS but because it's a client-side system, it should make no difference what technology is used on the server to generate the HTML markup. So if you use Razor CSHTML files to generate the HTML on the server, DurandalJS should work just fine with it.
If you're getting a particular error then please share that error, but I can't think of any reason why it wouldn't work.

Dijit combobox not rendering in custom widget

I am trying to use the combobox provided by Dijit inside of a custom-made widget. I have been using Dojo's tutorial on comboboxes to guide me.
When I implement a stand-alone webpage similar to their tutorial examples, everything worked fine; but when I ported the code into my custom-made widget, it just renders the combobox as a plain HTML text box.
Here's what my custom widget's template looks like:
<div class='customWidget'>
...
<div dojoAttachPoint="mainDiv" class="mainDiv">
<div dojoType="dojo.data.ItemFileReadStore" jsId="stateStore" url="states.txt"></div>
<input dojoType="dijit.form.ComboBox"
store="stateStore"
value="California"
searchAttr="name"
name="state2" />
<button dojoAttachEvent="onclick:chooseState">OK</button>
</div>
...
</div>
In the widget code, I require the combobox and read store:
dojo.require("dijit.form.ComboBox");
dojo.require("dojo.data.ItemFileReadStore");
I also tried putting these includes in a <script/> within the custom widget (similar to the way they do it in the tutorial), but it didn't work (in fact, it appears as if the script tag wasn't even evaluated, since I couldn't reference a function I declared inside of it!)
Do you have widgetsInTemplate in your widget declaration?
dojo.declare('my.widget.Cool',[ dijit._Widget, dijit._Templated ], {
widgetsInTemplate: true,
// rest of widget JS here
});
Here's an article about including other widgets in your template.
Have you tried adding:
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.addOnLoad(function(){
dojo.parser.parse();
});
</script>
(from Dojocampus) to ensure Dojo is parsing the page? Are there any errors in your Javascript console? Is the page rendering any normal Dojo widgets?